Skip to content

Commit

Permalink
Merge pull request #2230 from freedomofpress/top-pane-to-the-bottom
Browse files Browse the repository at this point in the history
Move Top Pane to Bottom
  • Loading branch information
legoktm authored Sep 20, 2024
2 parents 36671f2 + 86e07a5 commit 0c1cc43
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 85 deletions.
28 changes: 14 additions & 14 deletions client/securedrop_client/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from securedrop_client import __version__, state
from securedrop_client.db import Source, User
from securedrop_client.gui.auth import LoginDialog
from securedrop_client.gui.widgets import LeftPane, MainView, TopPane
from securedrop_client.gui.widgets import BottomPane, LeftPane, MainView
from securedrop_client.logic import Controller
from securedrop_client.resources import load_all_fonts, load_css, load_icon

Expand All @@ -51,19 +51,19 @@ def __init__(
Create the default start state. The window contains a root widget into
which is placed:
* A status bar widget at the top, containing curent user / status
information.
* A main-view widget, itself containing a list view for sources and a
place for details / message contents / forms.
* A status bar widget at the bottom, containing network and error status
information.
"""
super().__init__()
load_all_fonts()
self.setStyleSheet(load_css("sdclient.css"))
self.setWindowTitle(_("SecureDrop Client {}").format(__version__))
self.setWindowIcon(load_icon(self.icon))

# Top Pane to display activity and error messages
self.top_pane = TopPane()
# Bottom Pane to display activity and error messages
self.bottom_pane = BottomPane()

# Main Pane to display everything else
self.main_pane = QWidget()
Expand All @@ -77,15 +77,15 @@ def __init__(
layout.addWidget(self.left_pane)
layout.addWidget(self.main_view)

# Set the main window's central widget to show Top Pane and Main Pane
# Set the main window's central widget to show Main Pane and Bottom Pane
self.central_widget = QWidget()
central_widget_layout = QVBoxLayout()
central_widget_layout.setContentsMargins(0, 0, 0, 0)
central_widget_layout.setSpacing(0)
self.central_widget.setLayout(central_widget_layout)
self.setCentralWidget(self.central_widget)
central_widget_layout.addWidget(self.top_pane)
central_widget_layout.addWidget(self.main_pane)
central_widget_layout.addWidget(self.bottom_pane)

# Dialogs
self.login_dialog: LoginDialog | None = None
Expand All @@ -103,7 +103,7 @@ def setup(self, controller: Controller) -> None:
views used in the UI.
"""
self.controller = controller
self.top_pane.setup(self.controller)
self.bottom_pane.setup(self.controller)
self.left_pane.setup(self, self.controller)
self.main_view.setup(self.controller)
self.show_login()
Expand Down Expand Up @@ -182,41 +182,41 @@ def set_logged_in_as(self, db_user: User) -> None:
Update the UI to show user logged in with username.
"""
self.left_pane.set_logged_in_as(db_user)
self.top_pane.set_logged_in()
self.bottom_pane.set_logged_in()

def logout(self) -> None:
"""
Update the UI to show the user is logged out.
"""
self.left_pane.set_logged_out()
self.top_pane.set_logged_out()
self.bottom_pane.set_logged_out()

def update_sync_status(self, message: str, duration: int = 0) -> None:
"""
Display an activity status message to the user. Optionally, supply a duration
(in milliseconds), the default will continuously show the message.
"""
self.top_pane.update_sync_status(message, duration)
self.bottom_pane.update_sync_status(message, duration)

def update_activity_status(self, message: str, duration: int = 0) -> None:
"""
Display an activity status message to the user. Optionally, supply a duration
(in milliseconds), the default will continuously show the message.
"""
self.top_pane.update_activity_status(message, duration)
self.bottom_pane.update_activity_status(message, duration)

def update_error_status(self, message: str, duration: int = 10000) -> None:
"""
Display an error status message to the user. Optionally, supply a duration
(in milliseconds), the default will continuously show the message.
"""
self.top_pane.update_error_status(message, duration)
self.bottom_pane.update_error_status(message, duration)

def clear_error_status(self) -> None:
"""
Clear any message currently in the error status bar.
"""
self.top_pane.clear_error_status()
self.bottom_pane.clear_error_status()

def clear_clipboard(self) -> None:
"""
Expand Down
6 changes: 3 additions & 3 deletions client/securedrop_client/gui/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@
NO_DELAY = 1


class TopPane(QWidget):
class BottomPane(QWidget):
"""
Top pane of the app window.
Bottom pane of the app window.
"""

def __init__(self) -> None:
Expand Down Expand Up @@ -155,7 +155,7 @@ def __init__(self) -> None:

activity_status_bar_spacer = QWidget()

# Set height of top pane to 42 pixels
# Set height of bottom pane to 42 pixels
self.setFixedHeight(42)
self.sync_icon.setFixedHeight(42)
self.activity_status_bar.setFixedHeight(42)
Expand Down
2 changes: 1 addition & 1 deletion client/tests/functional/test_offline_delete_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def check_for_conversation():
controller.delete_source(conversation.conversation_title_bar.source)

def check_for_error():
msg = gui.top_pane.error_status_bar.status_bar.currentMessage()
msg = gui.bottom_pane.error_status_bar.status_bar.currentMessage()
assert msg == "You must sign in to perform this action."

qtbot.waitUntil(check_for_error, timeout=TIME_CLICK_ACTION)
2 changes: 1 addition & 1 deletion client/tests/functional/test_offline_star_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def check_for_sources():
qtbot.mouseClick(first_source_widget.star, Qt.LeftButton)

def sign_in_required_error():
msg = gui.top_pane.error_status_bar.status_bar.currentMessage()
msg = gui.bottom_pane.error_status_bar.status_bar.currentMessage()
assert msg == "You must sign in to perform this action."

qtbot.waitUntil(sign_in_required_error, timeout=TIME_RENDER_CONV_VIEW)
Expand Down
44 changes: 22 additions & 22 deletions client/tests/gui/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ def test_setup(mocker, homedir, session_maker):
"""
w = Window()
w.show_login = mocker.MagicMock()
w.top_pane = mocker.MagicMock()
w.bottom_pane = mocker.MagicMock()
w.left_pane = mocker.MagicMock()
w.main_view = mocker.MagicMock()
controller = Controller("http://localhost", mocker.MagicMock(), session_maker, homedir, None)

w.setup(controller)

assert w.controller == controller
w.top_pane.setup.assert_called_once_with(controller)
w.bottom_pane.setup.assert_called_once_with(controller)
w.left_pane.setup.assert_called_once_with(w, controller)
w.main_view.setup.assert_called_once_with(controller)
w.show_login.assert_called_once_with()
Expand Down Expand Up @@ -270,58 +270,58 @@ def test_show_sources(mocker):

def test_update_error_status_default(mocker):
"""
Ensure that the error to be shown in the error status bar will be passed to the top pane with a
default duration of 10 seconds.
Ensure that the error to be shown in the error status bar will be passed
to the bottom pane with a default duration of 10 seconds.
"""
w = Window()
w.top_pane = mocker.MagicMock()
w.bottom_pane = mocker.MagicMock()
w.update_error_status(message="test error message")
w.top_pane.update_error_status.assert_called_once_with("test error message", 10000)
w.bottom_pane.update_error_status.assert_called_once_with("test error message", 10000)


def test_update_error_status(mocker):
"""
Ensure that the error to be shown in the error status bar will be passed to the top pane with
the duration of seconds provided.
Ensure that the error to be shown in the error status bar will be passed to the
bottom pane with the duration of seconds provided.
"""
w = Window()
w.top_pane = mocker.MagicMock()
w.bottom_pane = mocker.MagicMock()
w.update_error_status(message="test error message", duration=123)
w.top_pane.update_error_status.assert_called_once_with("test error message", 123)
w.bottom_pane.update_error_status.assert_called_once_with("test error message", 123)


def test_update_activity_status_default(mocker):
"""
Ensure that the activity to be shown in the activity status bar will be passed to the top pane
with a default duration of 0 seconds, i.e. forever.
Ensure that the activity to be shown in the activity status bar will be passed to the
bottom pane with a default duration of 0 seconds, i.e. forever.
"""
w = Window()
w.top_pane = mocker.MagicMock()
w.bottom_pane = mocker.MagicMock()
w.update_activity_status(message="test message")
w.top_pane.update_activity_status.assert_called_once_with("test message", 0)
w.bottom_pane.update_activity_status.assert_called_once_with("test message", 0)


def test_update_activity_status(mocker):
"""
Ensure that the activity to be shown in the activity status bar will be passed to the top pane
with the duration of seconds provided.
Ensure that the activity to be shown in the activity status bar will be passed to the
bottom pane with the duration of seconds provided.
"""
w = Window()
w.top_pane = mocker.MagicMock()
w.bottom_pane = mocker.MagicMock()
w.update_activity_status(message="test message", duration=123)
w.top_pane.update_activity_status.assert_called_once_with("test message", 123)
w.bottom_pane.update_activity_status.assert_called_once_with("test message", 123)


def test_clear_error_status(mocker):
"""
Ensure clear_error_status is called.
"""
w = Window()
w.top_pane = mocker.MagicMock()
w.bottom_pane = mocker.MagicMock()

w.clear_error_status()

w.top_pane.clear_error_status.assert_called_once_with()
w.bottom_pane.clear_error_status.assert_called_once_with()


def test_show_last_sync(mocker):
Expand Down Expand Up @@ -363,9 +363,9 @@ def test_logout(mocker):
"""
w = Window()
w.left_pane = mocker.MagicMock()
w.top_pane = mocker.MagicMock()
w.bottom_pane = mocker.MagicMock()

w.logout()

w.left_pane.set_logged_out.assert_called_once_with()
w.top_pane.set_logged_out.assert_called_once_with()
w.bottom_pane.set_logged_out.assert_called_once_with()
72 changes: 36 additions & 36 deletions client/tests/gui/test_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from securedrop_client.gui.source import DeleteSourceDialog
from securedrop_client.gui.widgets import (
ActivityStatusBar,
BottomPane,
ConversationView,
EmptyConversationView,
ErrorStatusBar,
Expand All @@ -46,7 +47,6 @@
SpeechBubble,
StarToggleButton,
SyncIcon,
TopPane,
UserButton,
UserIconLabel,
UserMenu,
Expand All @@ -55,87 +55,87 @@
from tests import factory


def test_TopPane_init(mocker):
def test_BottomPane_init(mocker):
"""
Ensure the TopPane instance is correctly set up.
Ensure the BottomPane instance is correctly set up.
"""
tp = TopPane()
file_path = tp.sync_icon.sync_animation.fileName()
bp = BottomPane()
file_path = bp.sync_icon.sync_animation.fileName()
filename = file_path[file_path.rfind("/") + 1 :]
assert filename == "sync_disabled.gif"


def test_TopPane_setup(mocker):
def test_BottomPane_setup(mocker):
"""
Calling setup calls setup for SyncIcon.
"""
tp = TopPane()
tp.sync_icon = mocker.MagicMock()
bp = BottomPane()
bp.sync_icon = mocker.MagicMock()
mock_controller = mocker.MagicMock()

tp.setup(mock_controller)
bp.setup(mock_controller)

tp.sync_icon.setup.assert_called_once_with(mock_controller)
bp.sync_icon.setup.assert_called_once_with(mock_controller)


def test_TopPane_set_logged_in(mocker):
def test_BottomPane_set_logged_in(mocker):
"""
Calling set_logged_in calls enable on TopPane.
Calling set_logged_in calls enable on BottomPane.
"""
tp = TopPane()
tp.sync_icon = mocker.MagicMock()
bp = BottomPane()
bp.sync_icon = mocker.MagicMock()

tp.set_logged_in()
bp.set_logged_in()

tp.sync_icon.enable.assert_called_once_with()
bp.sync_icon.enable.assert_called_once_with()


def test_TopPane_set_logged_out(mocker):
def test_BottomPane_set_logged_out(mocker):
"""
Calling set_logged_out calls disable on SyncIcon.
"""
tp = TopPane()
tp.sync_icon = mocker.MagicMock()
bp = BottomPane()
bp.sync_icon = mocker.MagicMock()

tp.set_logged_out()
bp.set_logged_out()

tp.sync_icon.disable.assert_called_once_with()
bp.sync_icon.disable.assert_called_once_with()


def test_TopPane_update_activity_status(mocker):
def test_BottomPane_update_activity_status(mocker):
"""
Calling update_activity_status calls update_message on ActivityStatusBar.
"""
tp = TopPane()
tp.activity_status_bar = mocker.MagicMock()
bp = BottomPane()
bp.activity_status_bar = mocker.MagicMock()

tp.update_activity_status(message="test message", duration=5)
bp.update_activity_status(message="test message", duration=5)

tp.activity_status_bar.update_message.assert_called_once_with("test message", 5)
bp.activity_status_bar.update_message.assert_called_once_with("test message", 5)


def test_TopPane_update_error_status(mocker):
def test_BottomPane_update_error_status(mocker):
"""
Calling update_error_status calls update_message on ErrorStatusBar.
"""
tp = TopPane()
tp.error_status_bar = mocker.MagicMock()
bp = BottomPane()
bp.error_status_bar = mocker.MagicMock()

tp.update_error_status(message="test message", duration=5)
bp.update_error_status(message="test message", duration=5)

tp.error_status_bar.update_message.assert_called_once_with("test message", 5)
bp.error_status_bar.update_message.assert_called_once_with("test message", 5)


def test_TopPane_clear_error_status(mocker):
def test_BottomPane_clear_error_status(mocker):
"""
Calling clear_error_status calls clear_message.
"""
tp = TopPane()
tp.error_status_bar = mocker.MagicMock()
bp = BottomPane()
bp.error_status_bar = mocker.MagicMock()

tp.clear_error_status()
bp.clear_error_status()

tp.error_status_bar.clear_message.assert_called_once_with()
bp.error_status_bar.clear_message.assert_called_once_with()


def test_LeftPane_init(mocker):
Expand Down
Loading

0 comments on commit 0c1cc43

Please sign in to comment.