Commit graph

338 commits

Author SHA1 Message Date
deeplow
39621fe51d
Limit n. parallel conversions in GUI
Limit the number of simultaneous document conversions to prevent
consuming too much CPU.
2022-11-21 12:39:20 +00:00
deeplow
45a865aae3
Prompt on exit: abort conversion?
Foot-shooting prevention by prompting the user if they are sure
they want to quit Dangerzone with ongoing conversions in progress.
2022-11-21 12:39:16 +00:00
deeplow
3c1e8a232d
Get OCR settings before conversion starts
In preparation for adding a limit on how many convert threads exist, we
are simplifying its logic. Getting ocr_lang doesn't seem to belong to
the thread.
2022-11-21 12:38:42 +00:00
deeplow
95a0536c61
Change start button text to plural when multiple docs 2022-11-21 12:38:27 +00:00
deeplow
06bd117d52
Align document labels
Aligns document labels following the design specified in issue #117.
It did not specify how it would change with window resize, so it
currently expands the progress bar / error message width and keeps the
document name fixed in size.
2022-11-21 12:38:25 +00:00
deeplow
bbc70df43b
Match styling of document-labels to design reference
- removes bold
- removes font size (default works)
- removes 'suspicious: ' label before the document name
2022-11-21 12:38:24 +00:00
deeplow
6707cbbc4a
Add conversion status icon next to each document
Allows the user to see the staus of each document at a gance.
2022-11-21 12:38:23 +00:00
deeplow
9641a61bb3
Typing: ignore 'unreachable' lint warning
Mypy complains about a line being unreachable. This is probably a false
positive. It must assume the code is not using a framework and thus it
can't when a PySide 'connect()' is being called.
2022-11-21 12:38:21 +00:00
deeplow
ce4efc0c25
Lint mypy: ignore type inconsistency w/ official docs
The official docs state the setProperty() method is (str, Any) but
mypy-pyside says it is (bytes, Any). So we ignore it.

[1]: https://doc.qt.io/qtforpython-5/PySide2/QtCore/QObject.html#PySide2.QtCore.PySide2.QtCore.QObject.setProperty
2022-11-21 12:38:20 +00:00
deeplow
df8e2f1b8b
Remove window management logic
Since everything now happens in a single window, there is no need
to have a way to keep track other windows. They simply won't exist.

But on windows and Linux it will still be possible to start
multiple windows by starting various Dangerzone processes. On MacOS
this doesn't seem to be as easy from the launcher, but it should
not be critical as multiple documents can be converted at the same
time in the one window.
2022-11-21 12:38:19 +00:00
deeplow
6f8eb96b35
Remove systray
Having the application in the systray is no longer needed, since
the new_window() logic no longer applies.
2022-11-21 12:38:17 +00:00
deeplow
814b8b9d0f
Unwrap ApplicationWrapper in GUI
Reverts commit b8e8c74 as the conditions that lead the
ApplicationWrapper to crash if not done with a wrapper no longer
seem to apply.
2022-11-21 12:38:16 +00:00
deeplow
c40502fb46
Don't close MainWindow when first conversion ends
First step in removing the multi-window approach, which got replaced
by multi-document single-window.

Fixes #205.
2022-11-21 12:38:14 +00:00
deeplow
f791dc70ab
Hide widgets: select docs -> settings -> conversion
To help debugging and visualizing what was happening, we set all
widgets to be visible at the same time. Now that is no longer needed,
we can hide them.

This keeps the original program flow:
  1. select the documents
  2. set the settings
  3. see the conversion progress

This diverges from the proposed design in issue #117 for simplification
and consistency (with past program flow) purposes.
2022-11-21 12:38:13 +00:00
deeplow
41017745ec
Add greyed out document name right before '-safe.pdf'
The user is supposed to only be able to select the safe PDF extension.
In a multi-file scenario, the extension will be the same for all files.

We follow here the design document [1]. To achieve this, we needed a
QLabel right next to a QLineEdit, to give the user the illusion that
it is the same graphical object.

[1]: https://github.com/firstlookmedia/dangerzone/files/6657536/DangerZone_NA02a.pdf
2022-11-21 12:38:12 +00:00
deeplow
0e36f8d2eb
Set application stylesheet (.css)
Sets the style for LineEdit boxes similarly to the specified design
in issue #117.
2022-11-21 12:38:10 +00:00
deeplow
e64954acfa
Validate safe-extension (-safe.pdf) before converting
Avoid conversion issues when saving the output file when it is set
wrongly. Inform the user with a red box saying "must end in .pdf"
and prevent the user from clicking "convert" before that is fixed.

Combines the validation logic with the already-existing 'update_ui()'
2022-11-21 12:38:09 +00:00
deeplow
1790231db0
Set default output dir and allow users changing it
Set the default directory for saving the file as the one from
the first document. This one will show just the directory name.
If the user changes it by choosing another directory, then show the new
directory name and its full path.
2022-11-21 12:37:57 +00:00
deeplow
4a42627f45
GUI settings: enable & adapt to muti-document
- shows settings again
- removes documents arg from settings widget - this is now stored
  under DangerzoneGui instance.
- removes widget 'dangerous_doc_label' - the doc label is already
  shown next to each document.
- 'Save as' button now serves the purpose of selecting where all
  output files should be saved. Before, it was for selecting where
  the file would be saved.
- 'save_lineedit' widget which was read-only and showed the path
  where the file would be saved, it now called 'safe_suffix' and is
  writable. It is where the user can type the safe file extension
  (e.g. '-safe.pdf'). Validation is not yet implemented.
- when 'start_button' is clicked it now changes the output_filename
  of all the documents to set their output directory to the one the
  user has selected (if 'save_checkbox' enabled) and to set their
  new 'safe_suffix'
- change to plural text for selection of multiple documents
2022-11-21 12:37:49 +00:00
deeplow
5a6c72f09e
Add output_dir manipulation methods to DocumentHolder
These will be needed in for the GUI's settings. This also adds test
cases for these documents. The methods are the following:

  - set_output_dir()
    For changing the output directory of the safe file

  - suffix setter and getter - for changing the suffix of the file
2022-11-21 12:37:47 +00:00
deeplow
fc3cfba450
Security: GUI (via CLI) wildcard injection mitigation
Similar to the mitigation implemented in the CLI version of dangerzone
(commit f9b564be)
2022-11-21 12:37:46 +00:00
deeplow
2e477b8a12
Initial refactor: GUI one-window multi-document support
Allows the user to:
  a) specify filenames via the terminal (for the GUI)
  b) select multiple documents via the GUI

The conversion process can't yet be started since the settings are
broken and disabled (expect mypy complaints).
2022-11-21 12:37:45 +00:00
deeplow
a8001d4f3e
Comment out settings_widget temporarily
The settings widget will be broken when we add multiple document
support in the GUI, at first, at least.
2022-11-21 12:37:43 +00:00
deeplow
bf8ca96a44
Rename 'convert_widget' to 'documents_list' 2022-11-21 12:37:42 +00:00
deeplow
0444fc56ec
Temporarily show all dangerzone widgets (for debugging) 2022-11-21 12:37:41 +00:00
deeplow
89f5e99b0c
Initial GUI multi-window opening via terminal
Allow opening multiple documents at the same time from the terminal
by calling

  $ dangerzone document1.pdf document2.pdf

It will open each document in its own window, making use of the
already existing 'multi-document multi-window' parallel conversion
implementation.
2022-11-21 12:37:39 +00:00
deeplow
1e16eca392
remove unneeded imports: plistlib, grp, getpass
plistlib:
  - originaly added in commit 3be1d63330
  - no longer needed

grp, getpass:
  - originally added in commit ae7c919d8e
  - used for finding the 'docker' executable. No longer needed.
2022-11-18 13:09:01 +00:00
deeplow
0b738ba490
Do not create outfile files when checking if writeable
Checking if files were writeable created files in the process. In the
case where someone adds a list of N files to dangerzone but exits before
converting, they would be left with N 0-byte files for the -safe
version. Now they don't.

Fixes #214
2022-11-14 09:04:54 +00:00
deeplow
93f17b3166
Specialize DocumentFilenameException() for disambiguation
All filename-related exceptions were of class DocumentFilenameException.
This made it difficult to disambiguate them. Specializing them makes it
it easier for tests to detect which exception in particular we want to
verify.
2022-11-14 09:04:23 +00:00
deeplow
d71e230173
Update document state exclusively in convert()
The document's state update is better update in the convert() function.
This is because this function is always called for the conversion
progress regardless of the frontend.
2022-11-14 09:03:50 +00:00
deeplow
65ac0d19c3
Add an identifier to each document
With multiple input documents it is possible only one of them has
issues. Mentioning the document id can help debug.
2022-11-14 09:03:36 +00:00
deeplow
6d2fdf0afe
Deduplicate container output parsing (stdout_callback)
The container output logging logic was in both the CLI and the GUI.
This change moves the core parsing logic to container.py.

Since the code was largely the same, now cli does need to specify
a stdout_callback since all the necessary logging already happens.

The GUI now only adds an stdout_callback to detect if there was an
error during the conversion process.
2022-11-14 08:54:02 +00:00
deeplow
2d587f4082
Parallel cli bulk conversions via threading
Initial parallel document conversion: creates a pool of N threads
defined by the setting 'parallel_conversions'. Each thread calls
convert() on a document.
2022-11-14 08:54:00 +00:00
deeplow
f9b564be03
Security: cli wildcard injection mitigation
Wildcard arguments like `*` can lead to security vulnerabilities
if files are maliciously named as would-be parameters. In the following
scenario if a file in the current directory was named '--help', running
the following command would show the help.

  $ dangerzone-cli *

By checking if parameters also happen to be files, we mitigate this
risk and have a chance to warn the user.
2022-11-14 08:53:38 +00:00
deeplow
981716ccff
Sequential bulk document support in cli
Basic implementation of bulk document support in dangerzone-cli.

Usage: dangerzone-cli [OPTIONS] doc1.pdf doc2.pdf
2022-11-14 08:51:00 +00:00
Alex Pyrgiotis
5a3a46cd46
Support Click 7.x callback handling
Support Click version 7.x and below, which inspect the number of
arguments a callback handler supports.

Refs #206
2022-11-10 16:35:48 +02:00
deeplow
649e427486
Make DangerzoneGui a subclass of DangerzoneCore
Simplify state sharing by having all dangerzone core logic in one
single class instead of two.
2022-10-27 13:44:16 +01:00
deeplow
dca290fb6b
Rename gui.common.GuiCommon class to gui.logic.DangerzoneGui
Rename the `gui.common` module and `gui.common.GuiCommon` class
to `gui.logic` and `gui.logic.DangerzoneGui` respectively. We keep as is
the original names of the variables that hold instances of this class,
since they will change in subsequent commits.

This change is part of the initial refactor to make the DangerzoneGui
class handle the GUI logic of the Dangerzone project.
2022-10-27 13:44:15 +01:00
deeplow
cb8130042e
Rename global_common.GlobalCommon class to logic.Dangerzone
Rename the `global_common` module and `global_common.GlobalCommon` class
to `logic` and `logic.Dangerzone` respectively. Also rename variables
that hold instances of this class.

This change is part of the initial refactor to make the Dangerzone class
handle the core logic of the Dangerzone project.
2022-10-27 13:44:13 +01:00
deeplow
2bed3c10e4
Move safe PDF naming logic to document.py
Let the Document class suggest the default filename for the safe PDF,
based on the provided input filename, appended with the extension
`-safe.pdf`.

Previously, this logic was copy-pasted throughout the code, which made
it difficult to maintain.
2022-10-27 13:44:12 +01:00
deeplow
7aa08457bd
Always resolve relative paths in Document class
Make the Document class always resolve relative input/output file paths,
which are usually passed as arguments by users.

Previously, resolving relative filepaths was a job left to the
instantiators of the Document class. This was error-prone since this
conversion must happen in all the places where we instantiated the
Document class.
2022-10-27 13:44:11 +01:00
Alex Pyrgiotis
a068770ab4
Validate filename arguments through Click
Implement Click's callback interface and create validators for the
input/output filenames, using the logic from the Document class. This
way, we can catch user errors as early as possible.
2022-10-27 13:44:08 +01:00
deeplow
db17bd0915
Validate I/O filenames in Document class
Factor out the filename validation logic and move it into the Document
class. Previously, the filename validation logic was scattered across
the CLI and GUI code.

Also, introduce a new errors.py module whose purpose is to handle
document-related errors, by providing:

* A special exception for them (DocumentFilenameExcpetion)
* A decorator that handles DocumentFilenameException, logs it and the
  underlying cause, and exits the program gracefully.
2022-10-27 13:44:06 +01:00
deeplow
e8b56627c9
Rename select_document() function to new_window()
Rename select_document() to new_window() to better encapsulate the fact
that this function is opening a new Dangerzone window.
2022-10-27 13:44:04 +01:00
deeplow
e487b7f0a9
Instantiate documents with a filename
Avoid setting document's filename via document.filename and instead
do it via object instantiation where possible.

Incidentally this has to change some window logic. When
select_document() is called it no longer checks if there is already an
open window with no document selected yet. The user can open as many
windows with unselected documents as they want.
2022-10-27 13:44:03 +01:00
deeplow
0493aca036
Rename common.Common class to document.Document
Rename the `common` module and `common.Common` class to `document` and
`document.Document` respectively. Also, rename the variables that hold
instances of this class.

This change reflects the fact that the class is responsible for tracking
the state of the document. When we add bulk document conversion,
allowing us to keep track of a document's state will be key. This name
change is a step towards that.
2022-10-27 13:44:01 +01:00
deeplow
aecacee315
fix return type for container.install()
Note: the container installation failure is not addressed here. See
https://github.com/freedomofpress/dangerzone/issues/193
2022-09-15 13:26:05 +01:00
deeplow
82ac22e837
remove hardcoded 'docker' logging reference
Closes #122 as this was the last remaining hardcoded docker
reference where the code also applied to podman.
2022-09-15 12:17:22 +01:00
deeplow
57e455bbf1
remove "container" from container.py method names
Container-related methods recently moved to container.py no longer
need to have 'container' in their name as they are within the
container scope already.

Additonally it made it awkward to call from another module:

    from .. import container
    container.get_container_runtime()
2022-09-15 12:09:38 +01:00
deeplow
6202c0dba9
deduplicate container-tech-checking logic
The logic for detecting if we were are running on docker or podman
and identifying its respective binary were scattered across the
codebase. This centralizes it all in container.py
2022-09-15 12:09:37 +01:00
deeplow
a822870853
move global_common container logic to container.py
Container-specific methods in global_common class were basically
static methods. So it made sense to move these to container.py
2022-09-15 12:09:34 +01:00
deeplow
272281a29e
move to util: get_subprocess_startupinfo 2022-09-15 10:40:36 +01:00
deeplow
2d6826afa9
move ocr_languages from global_common to share/
ocr_languages can be treated as just a json file instead of being
in global_common. This way it is easier to maintain and makes
global_common cleaner.
2022-09-15 10:40:34 +01:00
deeplow
c0f0e7bf6a
move banner() code to cli & version() to util
- display_banner() was only displayed in CLI mode so it makes sense
for it to be in the CLI.
- get_version(), was mvoed to util since it is a static function
that is needed in multiple parts of the application.
2022-09-15 10:40:31 +01:00
deeplow
ce57fc0449
move get_resource_path to util.py
static methods that are used application-wide should belong to
the utilities python file.

inspired by @gmarmstrong's PR #166 on refactoring global_common
methods to be static and have a dzutil.py
2022-09-15 09:24:11 +01:00
deeplow
01a5e3b7ca
fix type hints for gui-common (CI would fail)
CI fails: https://app.circleci.com/pipelines/github/freedomofpress/dangerzone/397/workflows/cba836ed-98df-41f8-8f34-abcde5a8c015/jobs/1538
2022-09-13 13:17:20 +01:00
deeplow
f10446c309
make dz-cli exit(1) when it fails
Otherwise the failure cannot be detected easily by the calling
tests.
2022-09-13 13:07:13 +01:00
deeplow
377665c459
move tests to project root 2022-09-13 13:07:10 +01:00
Guthrie McAfee Armstrong
36d96ccb5c
Add unit tests 2022-09-13 13:06:59 +01:00
deeplow
1fa1b90c30
remove container after use
The containers and their respective volumes where not being deleted.
By adding `--rm` to the `podman run` it now removes the containers
after use along with anonymous (unnamed) volumes [1]. The same
happens in docker [2].

Fixes #196

[1]: https://docs.podman.io/en/latest/markdown/podman-run.1.html#volume-v-source-volume-host-dir-container-dir-options
[2]: https://docs.docker.com/storage/volumes/#remove-volumes
2022-08-26 10:14:43 +01:00
deeplow
6b385abfef
fix regression: --output-filename fails
--output-filename failed with the message:

   Safe PDF filename is not writable

Bug introduced in commit 95ed346.
2022-08-25 09:03:43 +01:00
deeplow
ec3b92a008
install_container return true when already installed 2022-08-22 12:28:50 +01:00
deeplow
f44e6521b6
better handle QFileDialog.getOpenFileName filename 2022-08-22 12:28:39 +01:00
deeplow
e0b3c5b599
resolve naming conflict: QWidget.update()
QWidget.update() is already a built-in Qt method [1]. This method
was unintentionally being overriden. Renamed it to update_progress
to fix it.

[1]: https://doc.qt.io/qtforpython-5/PySide2/QtWidgets/QWidget.html#PySide2.QtWidgets.PySide2.QtWidgets.QWidget.update
2022-08-22 11:13:37 +01:00
deeplow
75ce244195
type hint application wrapper monkeypatch
ignore method assignment. Currently mypy cannot check this.
Related upstream issues:
  - https://github.com/python/mypy/issues/2427
  - https://github.com/python/mypy/issues/708
2022-08-22 11:13:35 +01:00
deeplow
392c4bddb5
add blank line at end of file (black lint)
Satisfy the black lint tool
2022-08-22 11:12:22 +01:00
deeplow
201bf5ec03
simplify ansi disabling on mac (removing type issues) 2022-08-22 11:12:20 +01:00
deeplow
95ed34626d
fix type hint in checking if output files exist 2022-08-22 11:12:18 +01:00
deeplow
46a62c1669
fix type hints with commonn input/output filename
Input_filename and output_filename could be None or Str. This lead
to typing issues where the static analysis type hint tool could not
check that the type colisions would not happen in runtime.

So the logic was replaced by throwing a runtime exception if either
of these valiables is ever used without first having been set.
2022-08-22 11:12:16 +01:00
deeplow
7b46d1e3cf
fix spacing (black lint tool) 2022-08-22 11:12:14 +01:00
deeplow
f67c1c3656
fix TypeErrors "object is not subscriptable"
The type hint shoudld be List[] instead of list[] [1] and TypeError:
'ABCMeta' object is not subscriptable (using instead typing.Callable).

[1]: https://mail.python.org/pipermail/python-dev/2017-April/147818.html
2022-08-22 11:12:10 +01:00
deeplow
dcc0b269cd
fix typing for filename in gui_main (is optional) 2022-08-22 11:10:04 +01:00
deeplow
e76132a2f0
add typed hints to Settings dictionary
Originally tied to implment following PEP 589 [1] – TypedDict: Type
Hints for Dictionaries with a Fixed Set of Keys for the Settings
dict.

But this quickly turned out to very challenging without redoing the
code. So we opted instead for using the Any keyword.

[1]: https://peps.python.org/pep-0589/
2022-08-22 11:09:13 +01:00
deeplow
b1c039c4a4
add type hinting to systray (avoid circular imports) 2022-08-22 11:09:11 +01:00
deeplow
b34f7381b4
fix GlobalCommon ref. that was supposed to be Common
The type hints actually warned about this inconsistency.
2022-08-22 11:09:09 +01:00
deeplow
ccacf50db5
simplify resources_path logic to reolve type hint
The following logic was leading to type hint issues:

>	inspect.getfile(inspect.currentframe())

But this code is overly complex for what it does is the same as
simply __file__. So we kill two birds with one stone, so to speak.
2022-08-22 11:09:03 +01:00
deeplow
c69f228261
handle case for no Popen.stdin
Similar to the previous commit (cb0f828)
2022-08-22 10:52:39 +01:00
deeplow
f99131e30c
type hints for container.py & handle no stdout
We added the following check as well:

+        if stdout_callback and p.stdout is not None:

Because, according to the subprocess docs[1]:

>  If the stdout argument was not PIPE, this attribute is None.

In this case, it should not need to confirm that p.stdout is not
None in the mypy static analysis. However it still complained. So
we made mypy the favor and confirmed this was the case.

[1]: https://docs.python.org/3/library/subprocess.html#subprocess.Popen.stdout
2022-08-22 10:52:17 +01:00
deeplow
78daf75638
add type hint to GuiCommon app argument 2022-08-22 10:49:04 +01:00
deeplow
4aab47af38
ignore type hint to windows-only subprocess command
`subprocess.STARTUPINFO()` only exists in windows systems. Because
of this, in linux-based systems it was raising type hint issues
as it didn't recognize the return function.
2022-08-22 10:49:02 +01:00
deeplow
6ddd411be8
add type get_container_runtime & handle no runtime
There was no code to handle if at this stage the runtime existed.
This caused issues with type hints since `shutil.which()` can
return None, which had not previously been accounted for.

We did not use the opportunity to consolidate all the code for
detecting the runtime, to make this review easier.
2022-08-22 10:48:57 +01:00
deeplow
665e4d54f7
add type hints (1st pass: non problematic cases) 2022-08-22 10:33:28 +01:00
deeplow
d579a47a84
add type hints (1st pass: non problematic cases) 2022-08-22 10:33:23 +01:00
deeplow
7bac3eb6b1
remove get_resource_path() comments (too long)
The black lint tool complained.
2022-08-22 10:15:32 +01:00
deeplow
4d8e4c53e3
sort imports with isort linter 2022-08-22 10:15:26 +01:00
deeplow
bd51947fca
deduplicate container_args
The container arguments was duplicated. This could potentially lead
to refactor errors. For example security arg could be added in one
container call but forgotten to be added in a second one.
2022-08-22 09:24:40 +01:00
deeplow
345ac8a396
podman run with --userns=keep-id to mount volumes
Moving to /dangerzone was failing with insuficient permissions:

    Invalid JSON returned from container: PermissionError: [Errno
    13] Permission denied: '/dangerzone/page-3.rgb'

A previous approach was removed in commit 805222. It started with
root at first in a wrapper script and then dropped these
priviledges which running the script.

`--userns=keep-id` solves the mountpoint issues as it maps the user
starting the container is mapped in the container [1].

[1]: https://www.redhat.com/sysadmin/user-flag-rootless-containers
2022-08-22 08:44:00 +01:00
deeplow
21a9a6c98c
running dangerzone without root in container
There was previously a user created in the container but it was not
used via the dockerfile RUN directive (as pointed out by
gmarmstrong[1]).

Fixes #169

[1]: https://github.com/freedomofpress/dangerzone/issues/169#issue-1268399245
2022-08-22 08:43:58 +01:00
deeplow
2d4bad680e
drop all linux kernel capabilities from containers
These are not needed in order to convert documents in the
dangerzone containers.
2022-08-22 08:43:56 +01:00
deeplow
a02801cc2d
add again the --security-opt flag
Had previously been added but removed in a refactor (see commit
488dca).
2022-08-22 08:43:32 +01:00
deeplow
f2f2e6f143
in cli-mode banner should be printed instead
Was calling color spillover to the adjacent text if the banner was
logged instead of printed. Since this is the CLI version, it could
make sense to have this printed.
2022-08-18 12:20:26 +01:00
deeplow
67d91be81a
replace prints with logging
fixes #144: printing non-ascii characters in a macOS application
opened directly from finder would sometimes lead to an error
message in /var/log/system.log similar to this:

  Failed to execute script 'dangerzone' due to unhandled exception:
  'ascii' codec can't encode character '\u201c' in position 1:
  ordinal not in range(128)
2022-08-18 12:07:23 +01:00
deeplow
c2a140807f
simplify get_resource_path logic
Simplifying the logic for obtaining resource paths by using pathlib
instead inspect.

Co-authored-by: Guthrie McAfee Armstrong <git@gmarmstrong.dev>
Based on commit bbce13d
2022-08-16 17:06:43 +01:00
Micah Lee
d8adb2e9cc
Properly cleanup after conversion 2021-12-14 12:29:55 -08:00
Micah Lee
ccb5d85afa
Fix "open with" Dangerzone in macOS 2021-12-14 10:21:42 -08:00
Micah Lee
c312420aba
Ignore exceptions when there's an error deleting an old container image 2021-12-13 11:37:19 -08:00
Micah Lee
d90097e7af
In GUI only use OCR if the OCR box is checked 2021-12-13 11:32:41 -08:00
Micah Lee
a81b2043cf
Use shutil.move instead of os.rename in case files are on different disks 2021-12-13 10:55:55 -08:00
Micah Lee
369ffe6cea
Allow opening external links in the WaitingWidget label 2021-11-30 14:59:05 -08:00
Micah Lee
89cf07d2b1
Remove obsolete status bar action 2021-11-30 09:59:05 -08:00