Skip to content
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

Prevent UI flicker on conversation switch / update #216

Merged
merged 14 commits into from
Jan 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ SHELL := /bin/bash

.PHONY: clean
clean: ## Clean the workspace of generated resources
@rm -rf build dist *.egg-info .coverage .eggs docs/_build .pytest_cache lib htmlcov && \
@rm -rf build dist *.egg-info .coverage .eggs docs/_build .pytest_cache lib htmlcov .cache && \
find . \( -name '*.py[co]' -o -name dropin.cache \) -delete && \
find . \( -name '*.bak' -o -name dropin.cache \) -delete && \
find . \( -name '*.tgz' -o -name dropin.cache \) -delete && \
find . -name __pycache__ -print0 | xargs rm -rf
find . -name __pycache__ -print0 | xargs -0 rm -rf

TESTS ?= tests
TESTOPTS ?= -v
Expand All @@ -34,7 +34,7 @@ check: clean lint test ## Run the full CI test suite
# 6. Format columns with colon as delimiter.
.PHONY: help
help: ## Print this message and exit.
@printf "Makefile for developing and testing SecureDrop.\n"
@printf "Makefile for developing and testing the SecureDrop client.\n"
@printf "Subcommands:\n\n"
@awk 'BEGIN {FS = ":.*?## "} /^[0-9a-zA-Z_-]+:.*?## / {printf "\033[36m%s\033[0m : %s\n", $$1, $$2}' $(MAKEFILE_LIST) \
| sort \
Expand Down
6 changes: 4 additions & 2 deletions run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ chmod 0700 "$SDC_HOME" "$GPG_HOME"
echo "Running app with home directory: $SDC_HOME"
echo ""

gpg --homedir "$GPG_HOME" --allow-secret-key-import --import tests/files/securedrop.gpg.asc
gpg --homedir "$GPG_HOME" --allow-secret-key-import --import tests/files/securedrop.gpg.asc &

# create the database and config for local testing
./create_dev_data.py "$SDC_HOME"
./create_dev_data.py "$SDC_HOME" &

wait

exec python -m securedrop_client --sdc-home "$SDC_HOME" --no-proxy $@
3 changes: 3 additions & 0 deletions securedrop_client/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,19 @@ def configure_logging(sdc_home: str) -> None:
log_fmt = ('%(asctime)s - %(name)s:%(lineno)d(%(funcName)s) '
'%(levelname)s: %(message)s')
formatter = logging.Formatter(log_fmt)

# define log handlers such as for rotating log files
handler = TimedRotatingFileHandler(log_file, when='midnight',
backupCount=5, delay=0,
encoding=ENCODING)
handler.setFormatter(formatter)
handler.setLevel(logging.DEBUG)

# set up primary log
log = logging.getLogger()
log.setLevel(logging.DEBUG)
log.addHandler(handler)

# override excepthook to capture a log of catastrophic failures.
sys.excepthook = excepthook

Expand Down
8 changes: 8 additions & 0 deletions securedrop_client/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ def make_engine(home: str):


class Source(Base):

__tablename__ = 'sources'

# TODO - add number_of_docs
id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True, nullable=False)
Expand Down Expand Up @@ -66,7 +68,9 @@ def collection(self):


class Submission(Base):

__tablename__ = 'submissions'

id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True, nullable=False)
filename = Column(String(255), nullable=False)
Expand Down Expand Up @@ -101,7 +105,9 @@ def __repr__(self):


class Reply(Base):

__tablename__ = 'replies'

id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True, nullable=False)
source_id = Column(Integer, ForeignKey('sources.id'))
Expand Down Expand Up @@ -133,7 +139,9 @@ def __repr__(self):


class User(Base):

__tablename__ = 'users'

id = Column(Integer, primary_key=True)
uuid = Column(String(36), unique=True, nullable=False)
username = Column(String(255), nullable=False, unique=True)
Expand Down
68 changes: 26 additions & 42 deletions securedrop_client/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
from PyQt5.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QDesktopWidget, QStatusBar
from securedrop_client import __version__
from securedrop_client.gui.widgets import (ToolBar, MainView, LoginDialog,
ConversationView,
SourceProfileShortWidget)
SourceConversationWrapper)
from securedrop_client.resources import load_icon
from securedrop_client.storage import get_data

logger = logging.getLogger(__name__)

Expand All @@ -53,19 +51,29 @@ def __init__(self, sdc_home: str):
self.sdc_home = sdc_home
self.setWindowTitle(_("SecureDrop Client {}").format(__version__))
self.setWindowIcon(load_icon(self.icon))

self.widget = QWidget()
widget_layout = QVBoxLayout()
self.widget.setLayout(widget_layout)
self.setCentralWidget(self.widget)

self.tool_bar = ToolBar(self.widget)

self.main_view = MainView(self.widget)
self.main_view.source_list.itemSelectionChanged.connect(self.on_source_changed)

widget_layout.addWidget(self.tool_bar, 1)
widget_layout.addWidget(self.main_view, 6)
self.setCentralWidget(self.widget)
self.current_source = None # Tracks which source is shown

# Cache a dict of source.uuid -> SourceConversationWrapper
# We do this to not create/destroy widgets constantly (because it causes UI "flicker")
self.conversations = {}
self.show()

# Tracks which source is shown
self.current_source = None

self.autosize_window()
self.show()

def setup(self, controller):
"""
Expand All @@ -74,9 +82,11 @@ def setup(self, controller):
"""
self.controller = controller # Reference the Client logic instance.
self.tool_bar.setup(self, controller)

self.status_bar = QStatusBar(self)
self.setStatusBar(self.status_bar)
self.set_status('Started SecureDrop Client. Please sign in.', 20000)

self.login_dialog = LoginDialog(self)
self.main_view.setup(self.controller)

Expand Down Expand Up @@ -156,47 +166,21 @@ def on_source_changed(self):
self.current_source = source_widget.source
self.show_conversation_for(self.current_source)

def add_item_content_or(self, adder, item, default):
"""
Private helper function to add correct message to conversation widgets
"""
if item.is_downloaded is False:
adder(default)
else:
adder(get_data(self.sdc_home, item.filename))

def show_conversation_for(self, source):
"""
Show conversation of messages and replies between a source and
journalists.
"""
conversation = ConversationView(self)
conversation.setup(self.controller)
conversation.add_message('Source name: {}'.format(
source.journalist_designation))

# Display each conversation item in the source collection.
for conversation_item in source.collection:

if conversation_item.filename.endswith('msg.gpg'):
self.add_item_content_or(conversation.add_message,
conversation_item,
"<Message not yet downloaded>")
elif conversation_item.filename.endswith('reply.gpg'):
self.add_item_content_or(conversation.add_reply,
conversation_item,
"<Reply not yet downloaded>")
else:
conversation.add_file(source, conversation_item)

container = QWidget()
layout = QVBoxLayout()
container.setLayout(layout)
source_profile = SourceProfileShortWidget(source, self.controller)

layout.addWidget(source_profile)
layout.addWidget(conversation)
self.main_view.update_view(container)

conversation_container = self.conversations.get(source.uuid, None)

if conversation_container is None:
conversation_container = SourceConversationWrapper(source,
self.sdc_home,
self.controller)
self.conversations[source.uuid] = conversation_container

self.main_view.set_conversation(conversation_container)
heartsucker marked this conversation as resolved.
Show resolved Hide resolved

def set_status(self, message, duration=5000):
"""
Expand Down
Loading