def update_conversation()

in client/securedrop_client/gui/widgets.py [0:0]


    def update_conversation(self, collection: list) -> None:
        """
        Given a list of conversation items that reflect the new state of the
        conversation, this method does two things:

        * Checks if the conversation item already exists in the conversation.
          If so, it checks that it's still in the same position. If it isn't,
          the item is removed from its current position and re-added at the
          new position. Then the index meta-data on the widget is updated to
          reflect this change.
        * If the item is a new item, this is created (as before) and inserted
          into the conversation at the correct index.

        Things to note, speech bubbles and files have an index attribute which
        defines where they currently are. This is the attribute that's checked
        when the new conversation state (i.e. the collection argument) is
        passed into this method in case of a mismatch between where the widget
        has been and now is in terms of its index in the conversation.
        """
        self.controller.session.refresh(self.source)

        # Keep a temporary copy of the current conversation so we can delete any
        # items corresponding to deleted items in the source collection.
        current_conversation = self.current_messages.copy()

        for index, conversation_item in enumerate(collection):
            item_widget = current_conversation.get(conversation_item.uuid)
            if item_widget:
                # FIXME: Item types cannot be defines as (FileWidget, MessageWidget, ReplyWidget)
                # because one test mocks MessageWidget.
                assert isinstance(item_widget, FileWidget | SpeechBubble)
                current_conversation.pop(conversation_item.uuid)
                if item_widget.index != index:
                    # The existing widget is out of order.
                    # Remove / re-add it and update index details.
                    self._scroll.remove_widget_from_conversation(item_widget)
                    item_widget.index = index
                    if isinstance(item_widget, ReplyWidget):
                        self._scroll.add_widget_to_conversation(index, item_widget, Qt.AlignRight)
                    else:
                        self._scroll.add_widget_to_conversation(index, item_widget, Qt.AlignLeft)
                # Check if text in item has changed, then update the
                # widget to reflect this change.
                if not isinstance(item_widget, FileWidget):
                    if (
                        item_widget.message.text() != conversation_item.content
                    ) and conversation_item.content:
                        item_widget.message.setText(conversation_item.content)

                    # If the item widget is not a FileWidget, retrieve the latest list of
                    # usernames of the users who have seen it.
                    item_widget.update_seen_by_list(conversation_item.seen_by_list)

                # TODO: Once the SDK supports the new /users endpoint, this code can be replaced so
                # that we can also update user accounts in the local db who have not sent replies.
                if isinstance(item_widget, ReplyWidget):
                    self.controller.session.refresh(conversation_item)
                    self.controller.session.refresh(conversation_item.journalist)
                    item_widget.sender = conversation_item.journalist
            elif isinstance(conversation_item, Message):
                self.add_message(conversation_item, index)
            elif isinstance(conversation_item, DraftReply | Reply):
                self.add_reply(conversation_item, conversation_item.journalist, index)
            else:
                self.add_file(conversation_item, index)

        # If any items remain in current_conversation, they are no longer in the
        # source collection and should be removed from both the layout and the conversation
        # dict. Note that an item may be removed from the source collection if it is deleted
        # by another user (a journalist using the Web UI is able to delete individual
        # submissions).
        for item_widget in current_conversation.values():
            logger.debug(f"Deleting item: {item_widget.uuid}")
            self.current_messages.pop(item_widget.uuid)
            item_widget.deleteLater()
            self._scroll.remove_widget_from_conversation(item_widget)

        self.update_deletion_markers()
        self.conversation_updated.emit()