From 2816a6e632187f5727c83af00779b7ed854b1ba1 Mon Sep 17 00:00:00 2001 From: Allie Crevier Date: Fri, 12 Apr 2019 17:25:33 -0700 Subject: [PATCH] no longer try to show conversation for deleted src --- securedrop_client/gui/main.py | 8 +++++++- securedrop_client/gui/widgets.py | 23 ++++++++++++----------- securedrop_client/logic.py | 7 +++++++ tests/gui/test_widgets.py | 16 ++++++++++------ 4 files changed, 36 insertions(+), 18 deletions(-) diff --git a/securedrop_client/gui/main.py b/securedrop_client/gui/main.py index 4c1ecf6b9..ff85ea761 100644 --- a/securedrop_client/gui/main.py +++ b/securedrop_client/gui/main.py @@ -172,9 +172,15 @@ def on_source_changed(self): """ source_item = self.main_view.source_list.currentItem() source_widget = self.main_view.source_list.itemWidget(source_item) - if source_widget: + + # Show conversation for the currently-selected source if it hasn't been deleted. If the + # current source no longer exists, clear the conversation for that source + self.current_source = None + if source_widget and self.controller.source_exists(source_widget.source.uuid): self.current_source = source_widget.source self.show_conversation_for(self.current_source, self.controller.is_authenticated) + else: + self.main_view.clear_conversation() def show_conversation_for(self, source: Source, is_authenticated: bool): """ diff --git a/securedrop_client/gui/widgets.py b/securedrop_client/gui/widgets.py index b8587d294..3ca793088 100644 --- a/securedrop_client/gui/widgets.py +++ b/securedrop_client/gui/widgets.py @@ -27,7 +27,7 @@ from typing import List from uuid import uuid4 -from securedrop_client.db import Source, Message, File +from securedrop_client.db import Source, Message, File, Reply from securedrop_client.gui import SvgLabel, SvgPushButton from securedrop_client.logic import Client from securedrop_client.resources import load_svg, load_icon, load_image @@ -631,6 +631,12 @@ def set_conversation(self, widget): self.view_layout.addWidget(widget) widget.show() + def clear_conversation(self): + while self.view_layout.count(): + child = self.view_layout.takeAt(0) + if child.widget(): + child.widget().deleteLater() + class SourceList(QListWidget): """ @@ -701,12 +707,7 @@ def launch(self): """ message = self._construct_message(self.source) reply = QMessageBox.question( - self.parent, - "", - _(message), - QMessageBox.Cancel | QMessageBox.Yes, - QMessageBox.Cancel - ) + self.parent, "", _(message), QMessageBox.Cancel | QMessageBox.Yes, QMessageBox.Cancel) if reply == QMessageBox.Yes: logger.debug("Deleting source %s" % (self.source.uuid,)) @@ -715,16 +716,19 @@ def launch(self): def _construct_message(self, source: Source) -> str: files = 0 messages = 0 + replies = 0 for submission in source.collection: if isinstance(submission, Message): messages += 1 + if isinstance(submission, Reply): + replies += 1 elif isinstance(submission, File): files += 1 message = ( "Deleting the Source account for", "{} will also".format(source.journalist_designation,), - "delete {} files and {} messages.".format(files, messages), + "delete {} files, {} replies, and {} messages.".format(files, replies, messages), "
", "This Source will no longer be able to correspond", "through the log-in tied to this account.", @@ -1261,7 +1265,6 @@ def update_conversation(self, collection: list) -> None: if conversation_item.filename.endswith('msg.gpg'): self.add_message(conversation_item) elif conversation_item.filename.endswith('reply.gpg'): - self.controller.session.refresh(conversation_item) if conversation_item.content is not None: content = conversation_item.content else: @@ -1293,7 +1296,6 @@ def add_message(self, message: Message) -> None: """ Add a message from the source. """ - self.controller.session.refresh(message) if message.content is not None: content = message.content else: @@ -1423,7 +1425,6 @@ def __init__(self, source, parent, controller): def trigger(self): if self.controller.api is None: self.controller.on_action_requiring_login() - return else: self.messagebox.launch() diff --git a/securedrop_client/logic.py b/securedrop_client/logic.py index d4463313c..24f8ba73c 100644 --- a/securedrop_client/logic.py +++ b/securedrop_client/logic.py @@ -731,3 +731,10 @@ def _on_reply_complete(self, result, current_object: (str, str)) -> None: def _on_reply_timeout(self, current_object: (str, str)) -> None: _, reply_uuid = current_object self.reply_failed.emit(reply_uuid) + + def source_exists(self, source_uuid: str): + try: + self.session.query(db.Source).filter_by(uuid=source_uuid).one() + return True + except Exception: + return False diff --git a/tests/gui/test_widgets.py b/tests/gui/test_widgets.py index 4c29cbd11..318dd5ab2 100644 --- a/tests/gui/test_widgets.py +++ b/tests/gui/test_widgets.py @@ -1307,6 +1307,8 @@ def test_DeleteSourceMssageBox_launch_when_user_chooses_yes(mocker, source, sess session.add(message) message = factory.Message(source=source) session.add(message) + reply = factory.Reply(source=source) + session.add(reply) session.commit() mock_message_box_question = mocker.MagicMock(QMessageBox.question) @@ -1326,11 +1328,11 @@ def test_DeleteSourceMssageBox_launch_when_user_chooses_yes(mocker, source, sess message = ( "Deleting the Source account for " "{designation} will also " - "delete {files} files and {messages} messages. " - "
" + "delete {files} files, {replies} replies, and {messages} messages." + "
" "This Source will no longer be able to correspond " "through the log-in tied to this account." - ).format(designation=source.journalist_designation, files=1, messages=2) + ).format(designation=source.journalist_designation, files=1, replies=1, messages=2) mock_message_box_question.assert_called_once_with( None, "", @@ -1348,6 +1350,8 @@ def test_DeleteSourceMessageBox_construct_message(mocker, source, session): session.add(message) message = factory.Message(source=source) session.add(message) + reply = factory.Reply(source=source) + session.add(reply) session.commit() mock_controller = mocker.MagicMock() @@ -1359,11 +1363,11 @@ def test_DeleteSourceMessageBox_construct_message(mocker, source, session): expected_message = ( "Deleting the Source account for " "{designation} will also " - "delete {files} files and {messages} messages. " - "
" + "delete {files} files, {replies} replies, and {messages} messages." + "
" "This Source will no longer be able to correspond " "through the log-in tied to this account." - ).format(designation=source.journalist_designation, files=1, messages=2) + ).format(designation=source.journalist_designation, files=1, replies=1, messages=2) assert message == expected_message