-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create and place auth widget #278
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,10 +20,10 @@ | |
import arrow | ||
import html | ||
from PyQt5.QtCore import Qt, pyqtSlot | ||
from PyQt5.QtGui import QIcon | ||
from PyQt5.QtGui import QIcon, QPalette, QBrush, QColor, QFont | ||
from PyQt5.QtWidgets import QListWidget, QLabel, QWidget, QListWidgetItem, QHBoxLayout, \ | ||
QPushButton, QVBoxLayout, QLineEdit, QScrollArea, QDialog, QAction, QMenu, QMessageBox, \ | ||
QToolButton, QSizePolicy, QTextEdit | ||
QToolButton, QSizePolicy, QTextEdit, QStatusBar | ||
from typing import List | ||
from uuid import uuid4 | ||
|
||
|
@@ -35,48 +35,114 @@ | |
logger = logging.getLogger(__name__) | ||
|
||
|
||
class StatusBar(QStatusBar): | ||
def __init__(self): | ||
super().__init__() | ||
|
||
self.setStyleSheet(''' | ||
QStatusBar { background-color: #fff; } | ||
QStatusBar::item { border: none; } | ||
QPushButton { border: none; } | ||
''') | ||
|
||
self.refresh = QPushButton() | ||
self.refresh.clicked.connect(self.on_refresh_clicked) | ||
self.refresh.setMaximumSize(30, 30) | ||
refresh_pixmap = load_image('refresh.svg') | ||
self.refresh.setIcon(QIcon(refresh_pixmap)) | ||
self.addPermanentWidget(self.refresh) # widget may not be obscured by temporary messages | ||
|
||
def setup(self, controller): | ||
""" | ||
Assign a controller object (containing the application logic). | ||
""" | ||
self.controller = controller | ||
self.controller.sync_events.connect(self._on_sync_event) | ||
|
||
def on_refresh_clicked(self): | ||
""" | ||
Called when the refresh button is clicked. | ||
""" | ||
self.controller.sync_api() | ||
|
||
def _on_sync_event(self, data): | ||
""" | ||
Called when the refresh call completes | ||
""" | ||
self.refresh.setEnabled(data != 'syncing') | ||
|
||
def show_message(self, message, duration=0): | ||
""" | ||
Display a status message to the user. Optionally, supply a duration | ||
(in milliseconds), the default will continuously show the message. | ||
""" | ||
self.showMessage(message, duration) | ||
|
||
def hide_refresh_icon(self): | ||
""" | ||
Hide refresh icon. | ||
""" | ||
self.refresh.hide() | ||
|
||
def show_refresh_icon(self): | ||
""" | ||
Show refresh icon. | ||
""" | ||
self.refresh.show() | ||
|
||
|
||
class ToolBar(QWidget): | ||
""" | ||
Represents the tool bar across the top of the user interface. | ||
""" | ||
|
||
def __init__(self, parent: QWidget): | ||
super().__init__(parent) | ||
layout = QVBoxLayout(self) | ||
layout.setContentsMargins(0, 0, 0, 0) | ||
|
||
self.user_state = QLabel(_('Signed out.')) | ||
|
||
self.login = QPushButton(_('Sign in')) | ||
self.login.setMaximumSize(80, 30) | ||
layout = QVBoxLayout(self) | ||
layout.setContentsMargins(20, 10, 20, 10) | ||
|
||
self.setAutoFillBackground(True) | ||
palette = QPalette() | ||
palette.setBrush(QPalette.Background, QBrush(load_image('hexes.svg'))) | ||
self.setPalette(palette) | ||
self.user_icon = QLabel() | ||
self.user_icon.setFont(QFont("Helvetica [Cronyx]", 16, QFont.Bold)) | ||
self.user_icon.hide() | ||
self.user_state = QLabel() | ||
self.user_state.setFont(QFont("Helvetica [Cronyx]", 12, QFont.Bold)) | ||
self.user_state.hide() | ||
self.user_menu = JournalistMenuButton(self) | ||
self.user_menu.hide() | ||
|
||
self.login = QPushButton(_('SIGN IN')) | ||
self.login.setFont(QFont("Helvetica [Cronyx]", 10)) | ||
self.login.setMinimumSize(200, 40) | ||
button_palette = self.login.palette() | ||
button_palette.setColor(QPalette.Button, QColor('#eee')) | ||
button_palette.setColor(QPalette.ButtonText, QColor('#000')) | ||
self.login.setAutoFillBackground(True) | ||
self.login.setPalette(button_palette) | ||
self.login.update() | ||
self.login.clicked.connect(self.on_login_clicked) | ||
|
||
self.logout = QPushButton(_('Sign out')) | ||
self.logout.clicked.connect(self.on_logout_clicked) | ||
self.logout.setMaximumSize(80, 30) | ||
self.logout.setVisible(False) | ||
|
||
self.refresh = QPushButton() | ||
self.refresh.clicked.connect(self.on_refresh_clicked) | ||
self.refresh.setMaximumSize(30, 30) | ||
refresh_pixmap = load_image('refresh.svg') | ||
spacer = QWidget() | ||
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) | ||
|
||
self.refresh.setIcon(QIcon(refresh_pixmap)) | ||
self.refresh.show() | ||
logo = QLabel() | ||
logo.setMinimumSize(200, 200) | ||
logo.setPixmap(load_image('logo.png')) | ||
|
||
self.logo = QLabel() | ||
self.logo.setPixmap(load_image('icon.png')) | ||
self.logo.setMinimumSize(200, 200) | ||
user_layout = QHBoxLayout() | ||
user_layout.addWidget(self.user_icon, 5, Qt.AlignLeft) | ||
user_layout.addWidget(self.user_state, 5, Qt.AlignLeft) | ||
user_layout.addWidget(self.login, 5, Qt.AlignLeft) | ||
user_layout.addWidget(self.user_menu, 5, Qt.AlignLeft) | ||
user_layout.addStretch() | ||
|
||
journalist_layout = QHBoxLayout() | ||
journalist_layout.addWidget(self.refresh, 1) | ||
journalist_layout.addWidget(self.user_state, 5) | ||
journalist_layout.addWidget(self.login, 5) | ||
journalist_layout.addWidget(self.logout, 5) | ||
journalist_layout.addStretch() | ||
|
||
layout.addLayout(journalist_layout) | ||
layout.addWidget(self.logo) | ||
layout.addLayout(user_layout) | ||
layout.addWidget(spacer, 5) | ||
layout.addWidget(logo, 3, Qt.AlignCenter) | ||
layout.addStretch() | ||
|
||
def setup(self, window, controller): | ||
|
@@ -90,25 +156,31 @@ def setup(self, window, controller): | |
self.window = window | ||
self.controller = controller | ||
|
||
self.controller.sync_events.connect(self._on_sync_event) | ||
|
||
def set_logged_in_as(self, username): | ||
""" | ||
Update the UI to reflect that the user is logged in as "username". | ||
""" | ||
self.user_state.setText(html.escape(username)) | ||
self.login.setVisible(False) | ||
self.logout.setVisible(True) | ||
self.refresh.setVisible(True) | ||
self.login.hide() | ||
|
||
self.user_icon.setText(_('jo')) | ||
self.user_icon.setStyleSheet(''' | ||
QLabel { background-color: #045fb4; color: cyan; padding: 10; border: 1px solid gray; } | ||
''') | ||
self.user_icon.show() | ||
|
||
self.user_state.setText(_('{}').format(html.escape(username))) | ||
self.user_state.show() | ||
|
||
self.user_menu.show() | ||
|
||
def set_logged_out(self): | ||
""" | ||
Update the UI to a logged out state. | ||
""" | ||
self.user_state.setText(_('Signed out.')) | ||
self.login.setVisible(True) | ||
self.logout.setVisible(False) | ||
self.refresh.setVisible(False) | ||
self.login.show() | ||
self.user_icon.hide() | ||
self.user_state.hide() | ||
self.user_menu.hide() | ||
|
||
def on_login_clicked(self): | ||
""" | ||
|
@@ -122,18 +194,6 @@ def on_logout_clicked(self): | |
""" | ||
self.controller.logout() | ||
|
||
def on_refresh_clicked(self): | ||
""" | ||
Called when the refresh button is clicked. | ||
""" | ||
self.controller.sync_api() | ||
|
||
def _on_sync_event(self, data): | ||
""" | ||
Called when the refresh call completes | ||
""" | ||
self.refresh.setEnabled(data != 'syncing') | ||
|
||
|
||
class MainView(QWidget): | ||
""" | ||
|
@@ -158,6 +218,7 @@ def __init__(self, parent): | |
self.error_status = QLabel('') | ||
self.error_status.setObjectName('error_label') | ||
left_layout.addWidget(self.error_status) | ||
self.error_status.hide() | ||
|
||
self.layout.addWidget(left_column, 4) | ||
|
||
|
@@ -893,6 +954,43 @@ def trigger(self): | |
self.messagebox.launch() | ||
|
||
|
||
class JournalistMenu(QMenu): | ||
"""A menu next to the journalist username. | ||
|
||
A menu that provides login options. | ||
""" | ||
|
||
def __init__(self, parent): | ||
super().__init__() | ||
self.logout = QAction(_('SIGN OUT')) | ||
self.logout.setFont(QFont("Helvetica [Cronyx]", 10)) | ||
self.addAction(self.logout) | ||
self.logout.triggered.connect(parent.on_logout_clicked) | ||
|
||
|
||
class JournalistMenuButton(QToolButton): | ||
"""An menu button for the journalist menu | ||
|
||
This button is responsible for launching the journalist menu on click. | ||
""" | ||
|
||
def __init__(self, parent): | ||
super().__init__() | ||
|
||
self.setStyleSheet(''' | ||
QToolButton::menu-indicator { image: none; } | ||
QToolButton { border: none; } | ||
''') | ||
arrow = load_image("dropdown_arrow.svg") | ||
self.setIcon(QIcon(arrow)) | ||
self.setMinimumSize(20, 20) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blarg—is that crappy art on my part, @creviera? If so, happy to re-rip another one... 🤢 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ninavizz - Some higher fidelity images would be great! I'm currently using the svgs provided in the inventory list. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ninavizz and @redshiftzero - In a follow-up PR I was able to improve the look of the refresh icon by changing this (which looked pixely/blocky): refresh_pixmap = load_image('refresh.svg')
self.refresh.setIcon(QIcon(refresh_pixmap)) To the following code: pixmap = load_image('refresh.svg')
scaled = pixmap.scaled(18, 18, Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation)
icon.addPixmap(scaled, QIcon.Normal) But I think I need to do more research into how to work with SVGs and QIcons. I may need to create a |
||
|
||
self.menu = JournalistMenu(parent) | ||
self.setMenu(self.menu) | ||
|
||
self.setPopupMode(QToolButton.InstantPopup) | ||
|
||
|
||
class SourceMenu(QMenu): | ||
"""Renders menu having various operations. | ||
|
||
|
@@ -921,7 +1019,7 @@ def __init__(self, source, controller): | |
class SourceMenuButton(QToolButton): | ||
"""An ellipse based source menu button. | ||
|
||
This button is responsible for launching menu on click. | ||
This button is responsible for launching the source menu on click. | ||
""" | ||
|
||
def __init__(self, source, controller): | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think when we log out we want the refresh button to be hidden no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@creviera asked me about where the Offline icon was, yesterday—did you want to put that in on a separate PR or were you waiting for me to respond to your Slack, Allie? All icons are in table on this Issue: freedomofpress/securedrop-ux#17
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh yeah that's right! The new offline icon will be in the pr for this issue: #279. As I understand it, there will be three icons which are in this inventory list: freedomofpress/securedrop-ux#17. There you'll see an icon labeled "offline", the static refresh icon, and the active refresh icon.
@redshiftzero - Realizing now that when I moved the refresh icon to the StatusBar class, I didn't move this check, so yeah I didn't catch that I changed the behavior. Good catch. However, I'm working on a follow-up PR to change and test the new behavior of refresh, so if you're cool with leaving this as is for now, I'm cool with it. 😎
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this and decided that since the refresh icon appearing when not logged in is a regression it would be good to resolve as part of this PR