diff --git a/client/securedrop_client/gui/conversation/export/export_wizard.py b/client/securedrop_client/gui/conversation/export/export_wizard.py
index 941ab349e..de33aeadc 100644
--- a/client/securedrop_client/gui/conversation/export/export_wizard.py
+++ b/client/securedrop_client/gui/conversation/export/export_wizard.py
@@ -111,14 +111,14 @@ def _set_layout(self) -> None:
)
def _set_pages(self) -> None:
- for page in [
- self._create_preflight(),
- self._create_errorpage(),
- self._create_insert_usb(),
- self._create_passphrase_prompt(),
- self._create_done(),
+ for id, page in [
+ (Pages.PREFLIGHT, self._create_preflight()),
+ (Pages.ERROR, self._create_errorpage()),
+ (Pages.INSERT_USB, self._create_insert_usb()),
+ (Pages.UNLOCK_USB, self._create_passphrase_prompt()),
+ (Pages.EXPORT_DONE, self._create_done()),
]:
- self.addPage(page)
+ self.setPage(id, page)
# Nice to have, but steals the focus from the password field after 1 character is typed.
# Probably another way to have it be based on validating the status
@@ -152,7 +152,7 @@ def on_status_received(self, status: ExportStatus) -> None:
To update the text on an individual page, the page listens
for this signal and can call `update_content` in the listener.
"""
- logger.debug(f"Wizard received {status.value}")
+ logger.debug(f"Wizard received {status.value}. Current page is {type(self.currentPage())}")
# Unrecoverable - end the wizard
if status in [
@@ -178,18 +178,9 @@ def on_status_received(self, status: ExportStatus) -> None:
target = Pages.UNLOCK_USB
# Someone may have yanked out or unmounted a USB
- if target:
+ if target and self.currentId() > target:
self.rewind(target)
- # If the user is stuck, show them a hint.
- should_show_hint = target == self.currentId()
- logger.debug(f"{status.value} - should show hint is {should_show_hint}")
-
- page = self.currentPage()
- # Sometimes self.currentPage() is None
- if page and should_show_hint:
- page.update_content(status, should_show_hint)
-
# Update status
self.current_status = status
@@ -207,8 +198,7 @@ def end_wizard_with_error(self, error: ExportStatus) -> None:
"""
if isinstance(self.currentPage(), PreflightPage):
# Update its status so it shows error next self.currentPage()
- # self.next()
- logger.debug("On preflight page")
+ logger.debug("On preflight page, no reordering needed")
else:
while self.currentId() > Pages.ERROR:
self.back()
@@ -227,9 +217,5 @@ def _create_insert_usb(self) -> QWizardPage:
def _create_passphrase_prompt(self) -> QWizardPage:
return PassphraseWizardPage(self.export)
- # def _create_export(self) -> QWizardPage:
- # return ExportPage(self.export)
-
- # readywhen: all done
def _create_done(self) -> QWizardPage:
return FinalPage(self.export)
diff --git a/client/securedrop_client/gui/conversation/export/export_wizard_page.py b/client/securedrop_client/gui/conversation/export/export_wizard_page.py
index 1c5dc9b61..b6d9a3e05 100644
--- a/client/securedrop_client/gui/conversation/export/export_wizard_page.py
+++ b/client/securedrop_client/gui/conversation/export/export_wizard_page.py
@@ -173,8 +173,9 @@ def stop_animate_header(self) -> None:
@pyqtSlot(object)
def on_status_received(self, status: ExportStatus) -> None:
logger.debug(f"Child page received status {status.value}")
- self.status = status
- self.completeChanged.emit()
+ # Child pages should overwrite this method
+ # self.status = status
+ # self.completeChanged.emit()
# Some children (not the Prefight Page) may wish to call update_content here
def update_content(self, status: ExportStatus, should_show_hint: bool = False) -> None:
@@ -195,6 +196,7 @@ def update_content(self, status: ExportStatus, should_show_hint: bool = False) -
class PreflightPage(ExportWizardPage):
def __init__(self, export, summary):
+ self.summary = summary
header = _(
"Preparing to export:
" '{}'
).format(summary)
@@ -241,10 +243,16 @@ def nextId(self):
@pyqtSlot(object)
def on_status_received(self, status: ExportStatus):
self.stop_animate_header()
- if status in (ExportStatus.DEVICE_LOCKED, ExportStatus.DEVICE_WRITABLE):
+ if status in (
+ ExportStatus.DEVICE_LOCKED,
+ ExportStatus.DEVICE_WRITABLE,
+ ExportStatus.NO_DEVICE_DETECTED,
+ ExportStatus.MULTI_DEVICE_DETECTED,
+ ExportStatus.INVALID_DEVICE_DETECTED,
+ ):
header = _(
"Ready to export:
" '{}'
- ).format(self.header_text)
+ ).format(self.summary)
self.header.setText(header)
self.status = status
@@ -256,17 +264,13 @@ def __init__(self, export, summary):
super().__init__(export, header=header, body=summary)
- def on_status_received(self, status: ExportStatus):
- body = STATUS_MESSAGES.get(status)
- self.body.setText(body)
- self.status = status
-
def isComplete(self) -> bool:
return False
class InsertUSBPage(ExportWizardPage):
def __init__(self, export, summary):
+ self.summary = summary
header = _("Ready to export:
" '{}').format(
summary
)
@@ -278,38 +282,39 @@ def __init__(self, export, summary):
@pyqtSlot(object)
def on_status_received(self, status: ExportStatus) -> None:
- super().on_status_received(status)
+ logger.debug(f"InsertUSB received {status.value}")
should_show_hint = status in (
ExportStatus.NO_DEVICE_DETECTED,
ExportStatus.MULTI_DEVICE_DETECTED,
ExportStatus.INVALID_DEVICE_DETECTED,
)
self.update_content(status, should_show_hint)
+ self.status = status
- def validatePage(self) -> bool:
- """
- Override method to implement custom validation logic, which prevents the
- wizard from advancing past this stage unless preconditions are met, and
- shows an error-specific hint to the user.
- """
- if self.status in (ExportStatus.DEVICE_WRITABLE, ExportStatus.DEVICE_LOCKED):
- self.error_details.hide()
- return True
- else:
- logger.debug(f"Status is {self.status}, rechecking")
-
- # Show the user a hint
- if self.status in (
- ExportStatus.MULTI_DEVICE_DETECTED,
- ExportStatus.NO_DEVICE_DETECTED,
- ExportStatus.INVALID_DEVICE_DETECTED,
- ):
- self.update_content(self.status, should_show_hint=True)
- else:
- # Shouldn't reach
- # Status may be None here
- logger.warning("InsertUSBPage encountered unexpected status")
- return False
+ # def validatePage(self) -> bool:
+ # """
+ # Override method to implement custom validation logic, which prevents the
+ # wizard from advancing past this stage unless preconditions are met, and
+ # shows an error-specific hint to the user.
+ # """
+ # if self.status in (ExportStatus.DEVICE_WRITABLE, ExportStatus.DEVICE_LOCKED):
+ # self.error_details.hide()
+ # return True
+ # else:
+ # logger.debug(f"Status is {self.status}")
+
+ # # Show the user a hint
+ # if self.status in (
+ # ExportStatus.MULTI_DEVICE_DETECTED,
+ # ExportStatus.NO_DEVICE_DETECTED,
+ # ExportStatus.INVALID_DEVICE_DETECTED,
+ # ):
+ # self.update_content(self.status, should_show_hint=True)
+ # else:
+ # # Shouldn't reach
+ # # Status may be None here
+ # logger.warning("InsertUSBPage encountered unexpected status")
+ # return False
def nextId(self):
"""
@@ -318,8 +323,14 @@ def nextId(self):
if self.status == ExportStatus.DEVICE_WRITABLE:
logger.debug("Skip password prompt")
return Pages.EXPORT_DONE
+ elif self.status == ExportStatus.DEVICE_LOCKED:
+ return Pages.UNLOCK_USB
+ elif self.status in (ExportStatus.UNEXPECTED_RETURN_STATUS, ExportStatus.DEVICE_ERROR):
+ return Pages.ERROR
else:
- return super().nextId()
+ next = super().nextId()
+ logger.error("Unexpected status on InsertUSBPage {status.value}, nextID is {next}")
+ return next
class FinalPage(ExportWizardPage):
@@ -332,10 +343,13 @@ def __init__(self, export: Export) -> None:
@pyqtSlot(object)
def on_status_received(self, status: ExportStatus) -> None:
- super().on_status_received(status)
+ logger.debug(f"Final page received status {status}")
self.update_content(status)
+ self.status = status
def update_content(self, status: ExportStatus, should_show_hint: bool = False):
+ header = None
+ body = None
if status == ExportStatus.SUCCESS_EXPORT:
header = _("Export successful")
body = _(
@@ -347,13 +361,11 @@ def update_content(self, status: ExportStatus, should_show_hint: bool = False):
body = STATUS_MESSAGES.get(ExportStatus.ERROR_EXPORT_CLEANUP)
else:
- header = _("Export failed")
- if not self.status:
- self.status = ExportStatus.UNEXPECTED_RETURN_STATUS
- body = STATUS_MESSAGES.get(self.status)
+ header = _("Working...")
self.header.setText(header)
- self.body.setText(body)
+ if body:
+ self.body.setText(body)
class PassphraseWizardPage(ExportWizardPage):
@@ -398,15 +410,39 @@ def _build_layout(self) -> QVBoxLayout:
passphrase_form_layout.addWidget(self.passphrase_field)
passphrase_form_layout.addWidget(check, alignment=Qt.AlignRight)
- # Add it
layout.addWidget(self.passphrase_form)
return layout
@pyqtSlot(object)
def on_status_received(self, status: ExportStatus) -> None:
- super().on_status_received(status)
+ logger.debug(f"Passphrase page rececived {status.value}")
should_show_hint = status in (
ExportStatus.ERROR_UNLOCK_LUKS,
ExportStatus.ERROR_UNLOCK_GENERIC,
)
self.update_content(status, should_show_hint)
+ self.status = status
+
+ def validate(self):
+ return self.status == ExportStatus.DEVICE_WRITABLE
+
+ def nextId(self):
+ if self.status == ExportStatus.SUCCESS_EXPORT:
+ return Pages.EXPORT_DONE
+ elif self.status in (ExportStatus.ERROR_UNLOCK_LUKS, ExportStatus.ERROR_UNLOCK_GENERIC):
+ return Pages.UNLOCK_USB
+ elif self.status in (
+ ExportStatus.NO_DEVICE_DETECTED,
+ ExportStatus.MULTI_DEVICE_DETECTED,
+ ExportStatus.INVALID_DEVICE_DETECTED,
+ ):
+ return Pages.INSERT_USB
+ elif self.status in (
+ ExportStatus.ERROR_MOUNT,
+ ExportStatus.ERROR_EXPORT,
+ ExportStatus.ERROR_EXPORT_CLEANUP,
+ ExportStatus.UNEXPECTED_RETURN_STATUS,
+ ):
+ return Pages.ERROR
+ else:
+ return super().nextId()