Translation updates; dynamic sizing TopNav titles; more complex screenshot generator screens

* Improved `SeedReviewPassphraseScreen` display
* Blue "Sign" icon replaces green paper airplane.
* New `SeedPassphraseWarningView` before adding passphrase, plus `Settings` option to disable.
* TextArea spacing bugfix
* Additional note on SeedBackupScreen
This commit is contained in:
kdmukai
2022-05-21 13:56:23 -05:00
parent 3df2fb14ab
commit 8bedaa29f3
16 changed files with 2693 additions and 252 deletions
+157 -140
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-05-10 17:32-0500\n"
"POT-Creation-Date: 2022-05-17 19:36-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -42,7 +42,7 @@ msgid "Please wait about 30 seconds before disconnecting power."
msgstr ""
#: src/seedsigner/gui/screens/psbt_screens.py:29
#: src/seedsigner/views/seed_views.py:352
#: src/seedsigner/views/seed_views.py:385
msgid "Review PSBT"
msgstr ""
@@ -172,29 +172,29 @@ msgstr ""
msgid "Settings imported successfully!"
msgstr ""
#: src/seedsigner/gui/screens/screen.py:731
#: src/seedsigner/views/seed_views.py:954
#: src/seedsigner/views/seed_views.py:1201
#: src/seedsigner/gui/screens/screen.py:734
#: src/seedsigner/views/seed_views.py:986
#: src/seedsigner/views/seed_views.py:1235
msgid "Success!"
msgstr ""
#: src/seedsigner/gui/screens/screen.py:735
#: src/seedsigner/views/seed_views.py:956
#: src/seedsigner/views/seed_views.py:1204
#: src/seedsigner/views/seed_views.py:1647 src/seedsigner/views/view.py:145
#: src/seedsigner/gui/screens/screen.py:738
#: src/seedsigner/views/seed_views.py:988
#: src/seedsigner/views/seed_views.py:1238
#: src/seedsigner/views/seed_views.py:1681 src/seedsigner/views/view.py:145
msgid "OK"
msgstr ""
#: src/seedsigner/gui/screens/screen.py:852
#: src/seedsigner/gui/screens/screen.py:853
#: src/seedsigner/views/psbt_views.py:430
msgid "Caution"
msgstr ""
#: src/seedsigner/gui/screens/screen.py:855
#: src/seedsigner/gui/screens/screen.py:856
msgid "I Understand"
msgstr ""
#: src/seedsigner/gui/screens/screen.py:867
#: src/seedsigner/gui/screens/screen.py:868
msgid "Classified Info!"
msgstr ""
@@ -207,134 +207,145 @@ msgstr ""
msgid "fingerprint"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:455
#: src/seedsigner/views/seed_views.py:388
msgid "Backup Seed"
msgstr ""
#. Additional explainer for the two seed backup options (mnemonic phrase and
#. SeedQR).
#: src/seedsigner/gui/screens/seed_screens.py:462
msgid "Backups do not include your passphrase."
msgstr ""
#. Displays the page number and total: (e.g. page 1 of 6)
#: src/seedsigner/gui/screens/seed_screens.py:461
#: src/seedsigner/gui/screens/seed_screens.py:479
msgid "Seed Words: {}/{}"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:541
#: src/seedsigner/gui/screens/seed_screens.py:559
msgid "Verify Backup?"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:547
#: src/seedsigner/gui/screens/seed_screens.py:565
msgid "Optionally verify that your mnemonic backup is correct."
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:556
#: src/seedsigner/gui/screens/seed_screens.py:574
msgid "Derivation Path"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:703
#: src/seedsigner/gui/screens/seed_screens.py:721
msgid "Xpub Details"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:709
#: src/seedsigner/gui/screens/seed_screens.py:713
#: src/seedsigner/views/seed_views.py:354
#: src/seedsigner/views/seed_views.py:471
#: src/seedsigner/views/seed_views.py:504
#: src/seedsigner/views/seed_views.py:578
#: src/seedsigner/gui/screens/seed_screens.py:727
#: src/seedsigner/gui/screens/seed_screens.py:731
#: src/seedsigner/views/seed_views.py:387
#: src/seedsigner/views/seed_views.py:503
#: src/seedsigner/views/seed_views.py:536
#: src/seedsigner/views/seed_views.py:610
msgid "Export Xpub"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:722
#: src/seedsigner/gui/screens/seed_screens.py:740
msgid "Fingerprint"
msgstr ""
#. Short for "Derivation Path"
#: src/seedsigner/gui/screens/seed_screens.py:732
#: src/seedsigner/gui/screens/seed_screens.py:750
msgid "Derivation"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:741
#: src/seedsigner/gui/screens/seed_screens.py:759
msgid "Xpub"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:754
#: src/seedsigner/views/seed_views.py:217
#: src/seedsigner/gui/screens/seed_screens.py:772
#: src/seedsigner/views/seed_views.py:230
msgid "Add Passphrase"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1106
#: src/seedsigner/gui/screens/seed_screens.py:1124
msgid "Verify Passphrase"
msgstr ""
#. Describes the effect of applying a BIP-39 passphrase; it changes the seed's
#. fingerprint
#: src/seedsigner/gui/screens/seed_screens.py:1115
msgid "changes fingerprint"
#: src/seedsigner/gui/screens/seed_screens.py:1133
msgid "changes seed:"
msgstr ""
#. Refers to the SeedQR type: Standard or Compact
#: src/seedsigner/gui/screens/seed_screens.py:1170
#: src/seedsigner/gui/screens/seed_screens.py:1190
msgid "Standard"
msgstr ""
#. Briefly explains the Standard SeedQR data format
#: src/seedsigner/gui/screens/seed_screens.py:1172
#: src/seedsigner/gui/screens/seed_screens.py:1192
msgid "BIP-39 wordlist indices"
msgstr ""
#. Refers to the SeedQR type: Standard or Compact
#: src/seedsigner/gui/screens/seed_screens.py:1180
#: src/seedsigner/gui/screens/seed_screens.py:1200
msgid "Compact"
msgstr ""
#. Briefly explains the Compact SeedQR data format
#: src/seedsigner/gui/screens/seed_screens.py:1182
#: src/seedsigner/gui/screens/seed_screens.py:1202
msgid "Raw entropy bits"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1196
#: src/seedsigner/gui/screens/seed_screens.py:1216
msgid "Transcribe SeedQR"
msgstr ""
#. Refers to the QR code size: 21x21, 25x25, or 29x29
#: src/seedsigner/gui/screens/seed_screens.py:1198
#: src/seedsigner/gui/screens/seed_screens.py:1218
msgid "Begin {}x{}"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1275
#: src/seedsigner/gui/screens/seed_screens.py:1295
msgid "click to exit"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1411
#: src/seedsigner/gui/screens/seed_screens.py:1431
msgid ""
"Optionally scan your transcribed SeedQR to confirm that it reads back "
"correctly."
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1468
#: src/seedsigner/views/seed_views.py:1284
#: src/seedsigner/views/seed_views.py:1342
#: src/seedsigner/gui/screens/seed_screens.py:1488
#: src/seedsigner/views/seed_views.py:1318
#: src/seedsigner/views/seed_views.py:1376
msgid "Verify Address"
msgstr ""
#. Inserts the nth address number (e.g. "Checking address 7")
#: src/seedsigner/gui/screens/seed_screens.py:1527
#: src/seedsigner/gui/screens/seed_screens.py:1547
msgid "Checking address {}"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1544
#: src/seedsigner/gui/screens/seed_screens.py:1564
msgid "Multisig Verification"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1549
#: src/seedsigner/gui/screens/seed_screens.py:1569
msgid ""
"Load your multisig wallet descriptor to verify your receive/self-transfer"
" or change address."
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1562
#: src/seedsigner/gui/screens/seed_screens.py:1582
msgid "Descriptor Loaded"
msgstr ""
#. Label for the multisig wallet's signing policy (e.g. 2-of-3)
#: src/seedsigner/gui/screens/seed_screens.py:1568
#: src/seedsigner/gui/screens/seed_screens.py:1588
msgid "Policy"
msgstr ""
#: src/seedsigner/gui/screens/seed_screens.py:1576
#: src/seedsigner/gui/screens/seed_screens.py:1596
msgid "Signing Keys"
msgstr ""
@@ -485,84 +496,88 @@ msgid "Regtest"
msgstr ""
#: src/seedsigner/models/settings_definition.py:122
#: src/seedsigner/views/seed_views.py:466
#: src/seedsigner/views/seed_views.py:498
msgid "Single Sig"
msgstr ""
#: src/seedsigner/models/settings_definition.py:123
#: src/seedsigner/views/seed_views.py:467
#: src/seedsigner/views/seed_views.py:499
msgid "Multisig"
msgstr ""
#: src/seedsigner/models/settings_definition.py:354
#: src/seedsigner/models/settings_definition.py:355
msgid "Language"
msgstr ""
#: src/seedsigner/models/settings_definition.py:362
#: src/seedsigner/models/settings_definition.py:363
msgid "Mnemonic language"
msgstr ""
#: src/seedsigner/models/settings_definition.py:370
#: src/seedsigner/models/settings_definition.py:371
msgid "Persistent settings"
msgstr ""
#: src/seedsigner/models/settings_definition.py:376
#: src/seedsigner/models/settings_definition.py:377
msgid "Coordinator software"
msgstr ""
#: src/seedsigner/models/settings_definition.py:383
#: src/seedsigner/models/settings_definition.py:384
msgid "Denomination display"
msgstr ""
#: src/seedsigner/models/settings_definition.py:392
#: src/seedsigner/models/settings_definition.py:393
msgid "Bitcoin network"
msgstr ""
#: src/seedsigner/models/settings_definition.py:400
#: src/seedsigner/models/settings_definition.py:401
msgid "QR code density"
msgstr ""
#: src/seedsigner/models/settings_definition.py:408
#: src/seedsigner/models/settings_definition.py:409
msgid "Xpub export"
msgstr ""
#: src/seedsigner/models/settings_definition.py:414
#: src/seedsigner/models/settings_definition.py:415
msgid "Sig types"
msgstr ""
#: src/seedsigner/models/settings_definition.py:422
#: src/seedsigner/models/settings_definition.py:423
msgid "Script types"
msgstr ""
#: src/seedsigner/models/settings_definition.py:430
#: src/seedsigner/models/settings_definition.py:431
msgid "Show xpub details"
msgstr ""
#: src/seedsigner/models/settings_definition.py:436
#: src/seedsigner/models/settings_definition.py:437
msgid "BIP-39 passphrase"
msgstr ""
#: src/seedsigner/models/settings_definition.py:444
#: src/seedsigner/models/settings_definition.py:445
msgid "Camera rotation"
msgstr ""
#: src/seedsigner/models/settings_definition.py:452
#: src/seedsigner/models/settings_definition.py:453
msgid "Compact SeedQR"
msgstr ""
#: src/seedsigner/models/settings_definition.py:458
#: src/seedsigner/models/settings_definition.py:459
msgid "Show privacy warnings"
msgstr ""
#: src/seedsigner/models/settings_definition.py:464
#: src/seedsigner/models/settings_definition.py:465
msgid "Show dire warnings"
msgstr ""
#: src/seedsigner/models/settings_definition.py:470
#: src/seedsigner/models/settings_definition.py:471
msgid "Show passphrase warning"
msgstr ""
#: src/seedsigner/models/settings_definition.py:477
msgid "Show partner logos"
msgstr ""
#: src/seedsigner/models/settings_definition.py:485
#: src/seedsigner/models/settings_definition.py:492
msgid "QR background color"
msgstr ""
@@ -592,12 +607,12 @@ msgid "Reset / Power"
msgstr ""
#: src/seedsigner/views/psbt_views.py:36
#: src/seedsigner/views/seed_views.py:1322
#: src/seedsigner/views/seed_views.py:1356
msgid "Scan a seed"
msgstr ""
#: src/seedsigner/views/psbt_views.py:37
#: src/seedsigner/views/seed_views.py:1323
#: src/seedsigner/views/seed_views.py:1357
msgid "Enter 12/24 words"
msgstr ""
@@ -650,7 +665,7 @@ msgstr ""
#. Short for "Next step"
#: src/seedsigner/views/psbt_views.py:251
#: src/seedsigner/views/psbt_views.py:320
#: src/seedsigner/views/seed_views.py:796
#: src/seedsigner/views/seed_views.py:828
msgid "Next"
msgstr ""
@@ -768,7 +783,7 @@ msgid "Review & Edit"
msgstr ""
#: src/seedsigner/views/seed_views.py:188
#: src/seedsigner/views/seed_views.py:310
#: src/seedsigner/views/seed_views.py:343
#: src/seedsigner/views/tools_views.py:259
msgid "Discard"
msgstr ""
@@ -781,228 +796,230 @@ msgstr ""
msgid "Checksum failure; not a valid seed phrase."
msgstr ""
#: src/seedsigner/views/seed_views.py:216
#: src/seedsigner/views/seed_views.py:268
#: src/seedsigner/views/seed_views.py:797
#: src/seedsigner/views/seed_views.py:1146
#: src/seedsigner/views/seed_views.py:229
#: src/seedsigner/views/seed_views.py:301
#: src/seedsigner/views/seed_views.py:829
#: src/seedsigner/views/seed_views.py:1180
msgid "Done"
msgstr ""
#: src/seedsigner/views/seed_views.py:267
#: src/seedsigner/views/seed_views.py:260
msgid ""
"BIP-39 passphrase changes your seed, resulting in new xpubs, wallets, "
"etc.\n"
"Be sure you know what you're doing!"
msgstr ""
#: src/seedsigner/views/seed_views.py:300
msgid "Edit passphrase"
msgstr ""
#: src/seedsigner/views/seed_views.py:309
#: src/seedsigner/views/seed_views.py:342
msgid "Keep Seed"
msgstr ""
#: src/seedsigner/views/seed_views.py:315
#: src/seedsigner/views/seed_views.py:348
msgid "Discard Seed?"
msgstr ""
#: src/seedsigner/views/seed_views.py:317
#: src/seedsigner/views/seed_views.py:350
msgid "Wipe seed {} from the device?"
msgstr ""
#: src/seedsigner/views/seed_views.py:351
#: src/seedsigner/views/seed_views.py:384
msgid "Scan PSBT"
msgstr ""
#: src/seedsigner/views/seed_views.py:353
#: src/seedsigner/views/seed_views.py:1646
#: src/seedsigner/views/seed_views.py:386
#: src/seedsigner/views/seed_views.py:1680
msgid "Verify Addr"
msgstr ""
#: src/seedsigner/views/seed_views.py:355
#: src/seedsigner/views/seed_views.py:436
msgid "Backup Seed"
msgstr ""
#: src/seedsigner/views/seed_views.py:356
#: src/seedsigner/views/seed_views.py:389
msgid "Discard Seed"
msgstr ""
#: src/seedsigner/views/seed_views.py:431
#: src/seedsigner/views/seed_views.py:464
msgid "View Seed Words"
msgstr ""
#: src/seedsigner/views/seed_views.py:432
#: src/seedsigner/views/seed_views.py:465
msgid "Export as SeedQR"
msgstr ""
#: src/seedsigner/views/seed_views.py:620
#: src/seedsigner/views/seed_views.py:652
msgid "Privacy Leak!"
msgstr ""
#: src/seedsigner/views/seed_views.py:621
#: src/seedsigner/views/seed_views.py:653
msgid "Xpub can be used to view all future transactions."
msgstr ""
#: src/seedsigner/views/seed_views.py:651
#: src/seedsigner/views/seed_views.py:683
msgid "Generating xpub..."
msgstr ""
#: src/seedsigner/views/seed_views.py:771
#: src/seedsigner/views/seed_views.py:803
msgid "You must keep your seed words private & away from all online devices."
msgstr ""
#: src/seedsigner/views/seed_views.py:836
#: src/seedsigner/views/seed_views.py:868
msgid "Verify"
msgstr ""
#: src/seedsigner/views/seed_views.py:837
#: src/seedsigner/views/seed_views.py:869
msgid "Skip"
msgstr ""
#: src/seedsigner/views/seed_views.py:886
#: src/seedsigner/views/seed_views.py:918
msgid "Verify Word #{}"
msgstr ""
#: src/seedsigner/views/seed_views.py:926
#: src/seedsigner/views/seed_views.py:958
msgid "Review Seed Words"
msgstr ""
#: src/seedsigner/views/seed_views.py:927
#: src/seedsigner/views/seed_views.py:959
msgid "Try Again"
msgstr ""
#: src/seedsigner/views/seed_views.py:931
#: src/seedsigner/views/seed_views.py:963
msgid "Verification Error"
msgstr ""
#: src/seedsigner/views/seed_views.py:933
#: src/seedsigner/views/seed_views.py:965
msgid "Wrong Word!"
msgstr ""
#: src/seedsigner/views/seed_views.py:934
#: src/seedsigner/views/seed_views.py:966
msgid "Word #{} is not \"{}\"!"
msgstr ""
#: src/seedsigner/views/seed_views.py:952
#: src/seedsigner/views/seed_views.py:984
msgid "Backup Verified"
msgstr ""
#: src/seedsigner/views/seed_views.py:955
#: src/seedsigner/views/seed_views.py:987
msgid "All mnemonic backup words were successfully verified!"
msgstr ""
#: src/seedsigner/views/seed_views.py:978
#: src/seedsigner/views/seed_views.py:1010
msgid "Standard: 25x25"
msgstr ""
#: src/seedsigner/views/seed_views.py:979
#: src/seedsigner/views/seed_views.py:1011
msgid "Compact: 21x21"
msgstr ""
#: src/seedsigner/views/seed_views.py:983
#: src/seedsigner/views/seed_views.py:1015
msgid "Standard: 29x29"
msgstr ""
#: src/seedsigner/views/seed_views.py:984
#: src/seedsigner/views/seed_views.py:1016
msgid "Compact: 25x25"
msgstr ""
#: src/seedsigner/views/seed_views.py:1003
#: src/seedsigner/views/seed_views.py:1037
msgid "SeedQR Format"
msgstr ""
#: src/seedsigner/views/seed_views.py:1052
#: src/seedsigner/views/seed_views.py:1086
msgid "SeedQR is your private key!"
msgstr ""
#: src/seedsigner/views/seed_views.py:1053
#: src/seedsigner/views/seed_views.py:1087
msgid "Never photograph it or scan it into an online device."
msgstr ""
#: src/seedsigner/views/seed_views.py:1145
#: src/seedsigner/views/seed_views.py:1189
#: src/seedsigner/views/seed_views.py:1200
#: src/seedsigner/views/seed_views.py:1212
#: src/seedsigner/views/seed_views.py:1179
#: src/seedsigner/views/seed_views.py:1223
#: src/seedsigner/views/seed_views.py:1234
#: src/seedsigner/views/seed_views.py:1246
msgid "Confirm SeedQR"
msgstr ""
#: src/seedsigner/views/seed_views.py:1150
#: src/seedsigner/views/seed_views.py:1184
msgid "Confirm SeedQR?"
msgstr ""
#: src/seedsigner/views/seed_views.py:1180
#: src/seedsigner/views/seed_views.py:1214
msgid "Scan your SeedQR"
msgstr ""
#: src/seedsigner/views/seed_views.py:1190
#: src/seedsigner/views/seed_views.py:1213
#: src/seedsigner/views/seed_views.py:1224
#: src/seedsigner/views/seed_views.py:1247
msgid "Error!"
msgstr ""
#: src/seedsigner/views/seed_views.py:1191
#: src/seedsigner/views/seed_views.py:1225
msgid "Your transcribed SeedQR does not match your original seed!"
msgstr ""
#: src/seedsigner/views/seed_views.py:1193
#: src/seedsigner/views/seed_views.py:1216
#: src/seedsigner/views/seed_views.py:1227
#: src/seedsigner/views/seed_views.py:1250
msgid "Review SeedQR"
msgstr ""
#: src/seedsigner/views/seed_views.py:1202
#: src/seedsigner/views/seed_views.py:1236
msgid "Your transcribed SeedQR successfully scanned and yielded the same seed."
msgstr ""
#: src/seedsigner/views/seed_views.py:1214
#: src/seedsigner/views/seed_views.py:1248
msgid "Your transcribed SeedQR could not be read!"
msgstr ""
#: src/seedsigner/views/seed_views.py:1285
#: src/seedsigner/views/seed_views.py:1319
msgid "Sig type can't be auto-detected from this address. Please specify:"
msgstr ""
#: src/seedsigner/views/seed_views.py:1326
#: src/seedsigner/views/seed_views.py:1360
msgid "Load the seed to verify"
msgstr ""
#: src/seedsigner/views/seed_views.py:1336
#: src/seedsigner/views/seed_views.py:1370
msgid "Select seed to verify"
msgstr ""
#: src/seedsigner/views/seed_views.py:1388
#: src/seedsigner/views/seed_views.py:1422
msgid "Can't validate a single sig addr without specifying a seed"
msgstr ""
#. Option when scanning for a matching address; skips ten addresses ahead
#: src/seedsigner/views/seed_views.py:1434
#: src/seedsigner/views/seed_views.py:1468
msgid "Skip 10"
msgstr ""
#: src/seedsigner/views/seed_views.py:1435
#: src/seedsigner/views/seed_views.py:1616
#: src/seedsigner/views/seed_views.py:1469
#: src/seedsigner/views/seed_views.py:1650
msgid "Cancel"
msgstr ""
#: src/seedsigner/views/seed_views.py:1563
#: src/seedsigner/views/seed_views.py:1579
#: src/seedsigner/views/seed_views.py:1597
#: src/seedsigner/views/seed_views.py:1613
msgid "Taproot verification not yet implemented!"
msgstr ""
#: src/seedsigner/views/seed_views.py:1575
#: src/seedsigner/views/seed_views.py:1609
msgid "Legacy P2PKH verification not implemented!"
msgstr ""
#: src/seedsigner/views/seed_views.py:1600
#: src/seedsigner/views/seed_views.py:1634
msgid "multisig"
msgstr ""
#: src/seedsigner/views/seed_views.py:1602
#: src/seedsigner/views/seed_views.py:1636
msgid "seed {}"
msgstr ""
#: src/seedsigner/views/seed_views.py:1605
#: src/seedsigner/views/seed_views.py:1639
msgid "Address Verified"
msgstr ""
#: src/seedsigner/views/seed_views.py:1615
#: src/seedsigner/views/seed_views.py:1649
msgid "Scan Descriptor"
msgstr ""
#: src/seedsigner/views/seed_views.py:1645
#: src/seedsigner/views/seed_views.py:1679
msgid "Return to PSBT"
msgstr ""
+40 -9
View File
@@ -27,6 +27,7 @@ class GUIConstants:
ACCENT_COLOR = "orange"
TESTNET_COLOR = "#00f100"
REGTEST_COLOR = "#00caf1"
LIGHT_BLUE = "#0084ff"
ICON_FONT_NAME__FONT_AWESOME = "Font_Awesome_6_Free-Solid-900.otf"
ICON_FONT_NAME__SEEDSIGNER = "seedsigner-glyphs.otf"
@@ -163,6 +164,7 @@ class FontAwesomeIconConstants:
DICE_FOUR = "\uf524"
DICE_FIVE = "\uf523"
DICE_SIX = "\uf526"
FILE_SIGNATURE = "\uf573"
GEAR = "\uf013"
KEY = "\uf084"
KEYBOARD = "\uf11c"
@@ -348,7 +350,7 @@ class TextArea(BaseComponent):
is_text_centered: bool = True
supersampling_factor: int = 1
auto_line_break: bool = True
allow_text_overflow: bool = False
allow_text_overflow: bool = True
def __post_init__(self):
@@ -390,8 +392,9 @@ class TextArea(BaseComponent):
text_x = int((self.supersampled_width - width) / 2)
else:
text_x = self.supersampling_factor * self.edge_padding
if self.min_text_x is not None and text_x < self.min_text_x:
text_x = self.min_text_x
if self.min_text_x is not None and text_x < self.supersampling_factor * self.min_text_x:
text_x = self.supersampling_factor * self.min_text_x
self.text_lines.append({"text": text, "text_x": text_x})
if width > self.text_width:
@@ -563,12 +566,12 @@ class IconTextLine(BaseComponent):
icon_size: int = GUIConstants.ICON_FONT_SIZE
icon_color: str = GUIConstants.BODY_FONT_COLOR
label_text: str = None
value_text: str = "73c5da0a"
value_text: str = ""
font_name: str = None
font_size: int = None
is_text_centered: bool = False
auto_line_break: bool = False
allow_text_overflow: bool = False
allow_text_overflow: bool = True
screen_x: int = 0
screen_y: int = 0
@@ -610,8 +613,7 @@ class IconTextLine(BaseComponent):
is_text_centered=self.is_text_centered if not self.icon_name else False,
auto_line_break=False,
screen_x=text_screen_x,
screen_y=self.screen_y,
allow_text_overflow=False
screen_y=self.screen_y
)
else:
self.label_textarea = None
@@ -1351,6 +1353,35 @@ class TopNav(BaseComponent):
if self.show_back_button:
# Don't let the title intrude on the BACK button
min_x = self.left_button.screen_x + self.left_button.width + GUIConstants.COMPONENT_PADDING
# TODO: Account for the icon's width if there is one
max_width = self.width - min_x
if self.show_power_button:
max_width -= (self.width - self.right_button.screen_x) + GUIConstants.COMPONENT_PADDING
else:
max_width -= GUIConstants.EDGE_PADDING
# print(f"width: {self.width}")
# print(f"min_x: {min_x}")
# print(f"max_width: {max_width}")
# print(f"self.font_size: {self.font_size}")
# Dynamically size the title font size as needed
title_font_size = self.font_size
min_title_font_size = GUIConstants.get_body_font_size()
while title_font_size >= min_title_font_size:
font = Fonts.get_font(self.font_name, title_font_size)
(left, top, text_width, bottom) = font.getbbox(self.text, anchor="ls")
# print(f"text_width: {text_width}")
if text_width > max_width:
title_font_size -= 1
# print(f"title_font_size: {title_font_size}")
else:
# It fits!
break
if title_font_size < min_title_font_size:
title_font_size = min_title_font_size
if self.icon_name:
self.title = IconTextLine(
@@ -1363,7 +1394,7 @@ class TopNav(BaseComponent):
value_text=self.text,
is_text_centered=True,
font_name=self.font_name,
font_size=self.font_size,
font_size=title_font_size,
)
else:
self.title = TextArea(
@@ -1374,7 +1405,7 @@ class TopNav(BaseComponent):
text=self.text,
is_text_centered=True,
font_name=self.font_name,
font_size=self.font_size,
font_size=title_font_size,
)
+6 -6
View File
@@ -344,7 +344,7 @@ class PSBTOverviewScreen(ButtonListScreen):
destination_y += destination_y_spacing
# Resize to target and sharpen final image
image = image.resize((self.canvas_width, chart_height), Image.LANCZOS)
image = image.resize((self.canvas_width, chart_height), Image.Resampling.LANCZOS)
self.paste_images.append((image.filter(ImageFilter.SHARPEN), (self.chart_x, self.chart_y)))
# Pass input and output curves to the animation thread
@@ -568,7 +568,7 @@ class PSBTMathScreen(ButtonListScreen):
)
# Resize to target and sharpen final image
image = image.resize((body_width, body_height), Image.LANCZOS)
image = image.resize((body_width, body_height), Image.Resampling.LANCZOS)
self.paste_images.append((image.filter(ImageFilter.SHARPEN), (GUIConstants.EDGE_PADDING, self.top_nav.height + GUIConstants.COMPONENT_PADDING)))
@@ -685,8 +685,8 @@ class PSBTFinalizeScreen(ButtonListScreen):
super().__post_init__()
icon = Icon(
icon_name=FontAwesomeIconConstants.PAPER_PLANE,
icon_color=GUIConstants.SUCCESS_COLOR,
icon_name=FontAwesomeIconConstants.FILE_SIGNATURE,
icon_color=GUIConstants.LIGHT_BLUE,
icon_size=GUIConstants.ICON_LARGE_BUTTON_SIZE,
screen_y=self.top_nav.height + GUIConstants.COMPONENT_PADDING
)
@@ -694,8 +694,8 @@ class PSBTFinalizeScreen(ButtonListScreen):
self.components.append(icon)
self.components.append(TextArea(
text=_("Click to authorize this transaction"),
screen_y=icon.screen_y + icon.height + GUIConstants.COMPONENT_PADDING
text=_("Click to approve this transaction"),
screen_y=icon.screen_y + icon.height + 2*GUIConstants.COMPONENT_PADDING
))
+3 -3
View File
@@ -368,6 +368,9 @@ class ButtonListScreen(BaseTopNavScreen):
super()._render()
self._render_visible_buttons()
# Write the screen updates
self.renderer.show_image()
def _render_visible_buttons(self):
if self.has_scroll_arrows:
@@ -723,7 +726,6 @@ class LargeIconStatusScreen(ButtonListScreen):
status_color: str = GUIConstants.SUCCESS_COLOR
text: str = "" # The body text of the screen
button_data: list = None
allow_text_overflow: bool = False
def __post_init__(self):
@@ -751,7 +753,6 @@ class LargeIconStatusScreen(ButtonListScreen):
width=self.canvas_width,
screen_y=next_y,
font_color=self.status_color,
allow_text_overflow=self.allow_text_overflow,
)
self.components.append(self.warning_headline_textarea)
next_y = next_y + self.warning_headline_textarea.height
@@ -761,7 +762,6 @@ class LargeIconStatusScreen(ButtonListScreen):
text=self.text,
width=self.canvas_width,
screen_y=next_y,
allow_text_overflow=self.allow_text_overflow,
))
+71 -11
View File
@@ -13,7 +13,7 @@ from seedsigner.models.threads import BaseThread, ThreadsafeCounter
from seedsigner.models.seed import Seed
from .screen import RET_CODE__BACK_BUTTON, BaseScreen, BaseTopNavScreen, ButtonListScreen, WarningEdgesMixin
from ..components import (Button, FontAwesomeIconConstants, Fonts, FormattedAddress, IconButton,
from ..components import (Button, FontAwesomeIconConstants, Fonts, FormattedAddress, Icon, IconButton,
IconTextLine, SeedSignerCustomIconConstants, TextArea, GUIConstants)
from seedsigner.gui.keyboard import Keyboard, TextEntryDisplay
@@ -447,6 +447,24 @@ class SeedOptionsScreen(ButtonListScreen):
@dataclass
class SeedBackupScreen(ButtonListScreen):
has_passphrase: bool = False
def __post_init__(self):
self.title = _("Backup Seed")
self.is_bottom_list = True
super().__post_init__()
if self.has_passphrase:
self.components.append(TextArea(
# TRANSLATOR_NOTE: Additional explainer for the two seed backup options (mnemonic phrase and SeedQR).
text=_("Backups do not include your passphrase."),
screen_y=self.top_nav.height + GUIConstants.COMPONENT_PADDING,
))
@dataclass
class SeedWordsScreen(WarningEdgesMixin, ButtonListScreen):
seed: Seed = None
@@ -529,7 +547,7 @@ class SeedWordsScreen(WarningEdgesMixin, ButtonListScreen):
number_box_y += number_box_height + (int(1.5*GUIConstants.COMPONENT_PADDING) * supersampling_factor)
# Resize to target and sharpen final image
self.body_img = self.body_img.resize((self.canvas_width, self.body_height), Image.LANCZOS)
self.body_img = self.body_img.resize((self.canvas_width, self.body_height), Image.Resampling.LANCZOS)
self.body_img = self.body_img.filter(ImageFilter.SHARPEN)
self.paste_images.append((self.body_img, (self.body_x, self.body_y)))
@@ -1108,17 +1126,57 @@ class SeedReviewPassphraseScreen(ButtonListScreen):
super().__post_init__()
self.components.append(IconTextLine(
icon_name=SeedSignerCustomIconConstants.FINGERPRINT,
icon_color="blue",
# self.components.append(IconTextLine(
# icon_name=SeedSignerCustomIconConstants.FINGERPRINT,
# icon_color="blue",
# # TRANSLATOR_NOTE: Describes the effect of applying a BIP-39 passphrase; it changes the seed's fingerprint
# label_text=_("changes seed:"),
# value_text=f"{self.fingerprint_without} > {self.fingerprint_with}",
# font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
# font_size=GUIConstants.get_body_font_size() + 4,
# is_text_centered=False,
# screen_x=GUIConstants.EDGE_PADDING,
# screen_y=self.buttons[0].screen_y - GUIConstants.COMPONENT_PADDING - int(GUIConstants.get_body_font_size()*2.5)
# ))
fingerprint_change_label = TextArea(
# TRANSLATOR_NOTE: Describes the effect of applying a BIP-39 passphrase; it changes the seed's fingerprint
label_text=_("changes fingerprint"),
value_text=f"{self.fingerprint_without} >> {self.fingerprint_with}",
is_text_centered=True,
screen_y = self.buttons[0].screen_y - GUIConstants.COMPONENT_PADDING - int(GUIConstants.get_body_font_size()*2.5)
text=_("changes seed:"),
font_size=GUIConstants.LABEL_FONT_SIZE,
font_color=GUIConstants.LABEL_FONT_COLOR,
is_text_centered=False,
screen_x=int(1.25*GUIConstants.EDGE_PADDING),
screen_y=self.buttons[0].screen_y - GUIConstants.COMPONENT_PADDING - int(GUIConstants.get_body_font_size()*2.25)
)
self.components.append(fingerprint_change_label)
screen_y = fingerprint_change_label.screen_y + fingerprint_change_label.height + int(GUIConstants.COMPONENT_PADDING/2)
self.components.append(TextArea(
text=self.fingerprint_without,
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
font_size=GUIConstants.get_body_font_size() + 4,
font_color=GUIConstants.LABEL_FONT_COLOR,
is_text_centered=False,
screen_x=int(1.25*GUIConstants.EDGE_PADDING),
screen_y=screen_y,
))
self.components.append(Icon(
icon_name=FontAwesomeIconConstants.CARET_RIGHT,
icon_size=GUIConstants.get_body_font_size() + 4,
icon_color=GUIConstants.LIGHT_BLUE,
screen_x=self.components[-1].screen_x + self.components[-1].width + 2*GUIConstants.COMPONENT_PADDING,
screen_y=screen_y,
))
self.components.append(TextArea(
text=self.fingerprint_with,
font_name=GUIConstants.FIXED_WIDTH_EMPHASIS_FONT_NAME,
font_size=GUIConstants.get_body_font_size() + 4,
is_text_centered=False,
screen_x=self.components[-1].screen_x + self.components[-1].width,
screen_y=screen_y,
))
available_height = self.components[-1].screen_y - self.top_nav.height + GUIConstants.COMPONENT_PADDING
available_height = fingerprint_change_label.screen_y - self.top_nav.height
max_font_size = 28
min_font_size = 16
font_size = max_font_size
@@ -1152,6 +1210,7 @@ class SeedReviewPassphraseScreen(ButtonListScreen):
text=line,
font_name=GUIConstants.FIXED_WIDTH_FONT_NAME,
font_size=font_size,
font_color="orange",
is_text_centered=True,
screen_y=screen_y,
))
@@ -1165,6 +1224,8 @@ class SeedTranscribeSeedQRFormatScreen(ButtonListScreen):
self.is_bottom_list = True
super().__post_init__()
print(self)
self.components.append(IconTextLine(
# TRANSLATOR_NOTE: Refers to the SeedQR type: Standard or Compact
label_text=_("Standard"),
@@ -1580,5 +1641,4 @@ class MultisigWalletDescriptorScreen(ButtonListScreen):
screen_y=self.components[-1].screen_y + self.components[-1].height + 2*GUIConstants.COMPONENT_PADDING,
is_text_centered=True,
auto_line_break=True,
allow_text_overflow=True,
))
@@ -36,7 +36,7 @@ class SettingsEntryUpdateSelectionScreen(ButtonListScreen):
text=_(self.display_name),
font_size=GUIConstants.BODY_FONT_MAX_SIZE,
is_text_centered=True,
auto_line_break=False,
auto_line_break=True,
screen_y=self.top_nav.height + GUIConstants.COMPONENT_PADDING
))
@@ -47,6 +47,7 @@ class SettingsEntryUpdateSelectionScreen(ButtonListScreen):
font_color=GUIConstants.LABEL_FONT_COLOR,
is_text_centered=True,
screen_y=prev_component_bottom + GUIConstants.COMPONENT_PADDING,
auto_line_break=True,
))
+5
View File
@@ -69,6 +69,11 @@ class Seed:
return unicodedata.normalize("NFC", " ".join(self._mnemonic)).split()
@property
def has_passphrase(self):
return self._passphrase != ""
@property
def passphrase(self):
return self._passphrase
+8 -1
View File
@@ -59,7 +59,7 @@ class SettingsConstants:
(LOCALE__GERMAN, "German"),
(LOCALE__HEBREW, "Hebrew"),
(LOCALE__JAPANESE, "Japanese"),
# (LOCALE__PORTUGUESE, "Portuguese"),
(LOCALE__PORTUGUESE, "Portuguese"),
(LOCALE__RUSSIAN, "Russian"),
(LOCALE__SPANISH, "Spanish"),
]
@@ -173,6 +173,7 @@ class SettingsConstants:
SETTING__COMPACT_SEEDQR = "compact_seedqr"
SETTING__PRIVACY_WARNINGS = "privacy_warnings"
SETTING__DIRE_WARNINGS = "dire_warnings"
SETTING__PASSPHRASE_WARNING = "passphrase_warning"
SETTING__PARTNER_LOGOS = "partner_logos"
SETTING__DEBUG = "debug"
@@ -465,6 +466,12 @@ class SettingsDefinition:
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__PASSPHRASE_WARNING,
display_name=_("Show passphrase warning"),
visibility=SettingsConstants.VISIBILITY__ADVANCED,
default_value=SettingsConstants.OPTION__ENABLED),
SettingsEntry(category=SettingsConstants.CATEGORY__FEATURES,
attr_name=SettingsConstants.SETTING__PARTNER_LOGOS,
display_name=_("Show partner logos"),
File diff suppressed because it is too large Load Diff
@@ -5,8 +5,8 @@
#
# Translators:
# Cabildo BTC, 2022
# Bitcoin Hornet, 2022
# Keith Mukai, 2022
# Bitcoin Hornet, 2022
#
#, fuzzy
msgid ""
@@ -15,7 +15,7 @@ msgstr ""
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-05-10 17:32-0500\n"
"PO-Revision-Date: 2022-04-29 13:50+0000\n"
"Last-Translator: Keith Mukai, 2022\n"
"Last-Translator: Bitcoin Hornet, 2022\n"
"Language-Team: Spanish (https://www.transifex.com/seedsigner/teams/135832/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -354,7 +354,7 @@ msgstr "Normas"
#: src/seedsigner/gui/screens/seed_screens.py:1576
msgid "Signing Keys"
msgstr "Claves para firmar"
msgstr "Llaves de firmado"
#: src/seedsigner/gui/screens/settings_screens.py:26
#: src/seedsigner/views/main_menu_views.py:21
@@ -366,7 +366,7 @@ msgstr "Ajustes"
#. working properly
#: src/seedsigner/gui/screens/settings_screens.py:58
msgid "I/O Test"
msgstr "Test de hardware"
msgstr "Examen de Hardware"
#. Blank the screen
#: src/seedsigner/gui/screens/settings_screens.py:129
@@ -455,7 +455,7 @@ msgstr "Btc-Solamente"
#: src/seedsigner/models/settings_definition.py:73
msgid "Sats-only"
msgstr "Sats-solamente"
msgstr "Sats-Solamente"
#: src/seedsigner/models/settings_definition.py:74
msgid "Threshold at 0.01"
@@ -463,7 +463,7 @@ msgstr "Límite a 0.01"
#: src/seedsigner/models/settings_definition.py:75
msgid "Btc | Sats hybrid"
msgstr "Btc | Sats híbrido"
msgstr "Btc | Sats Híbrido"
#: src/seedsigner/models/settings_definition.py:83
msgid "0°"
@@ -517,11 +517,11 @@ msgstr "Multifirma"
#: src/seedsigner/models/settings_definition.py:354
msgid "Language"
msgstr "Lengua"
msgstr "Idioma"
#: src/seedsigner/models/settings_definition.py:362
msgid "Mnemonic language"
msgstr "Lenguaje frase mnemónica"
msgstr "Idioma de frase mnemónica"
#: src/seedsigner/models/settings_definition.py:370
msgid "Persistent settings"
@@ -533,7 +533,7 @@ msgstr "Software coordinador"
#: src/seedsigner/models/settings_definition.py:383
msgid "Denomination display"
msgstr "Pantalla denominaciones"
msgstr "Pantalla de Denominaciones"
#: src/seedsigner/models/settings_definition.py:392
msgid "Bitcoin network"
@@ -771,19 +771,19 @@ msgstr "Escanear SeedQR"
#: src/seedsigner/views/seed_views.py:78
msgid "Enter 24-word seed"
msgstr ""
msgstr "Entrar llave de 24 palabras"
#: src/seedsigner/views/seed_views.py:79
msgid "Enter 12-word seed"
msgstr ""
msgstr "Entrar llave de 12 palabras"
#: src/seedsigner/views/seed_views.py:80
msgid "Create a seed"
msgstr ""
msgstr "Crear una llave"
#: src/seedsigner/views/seed_views.py:89
msgid "Load A Seed"
msgstr ""
msgstr "Carga una Llave"
#. Inserts the word number (e.g. "Seed Word #6")
#: src/seedsigner/views/seed_views.py:126
@@ -817,7 +817,7 @@ msgstr "Finalizado"
#: src/seedsigner/views/seed_views.py:267
msgid "Edit passphrase"
msgstr "Editar contraseña"
msgstr "Editar frase de contraseña"
#: src/seedsigner/views/seed_views.py:309
msgid "Keep Seed"
@@ -939,7 +939,7 @@ msgstr "Formato SeedQR"
#: src/seedsigner/views/seed_views.py:1052
msgid "SeedQR is your private key!"
msgstr "¡SeedQR es tu clave privada!"
msgstr "¡SeedQR es tu clave privada/semilla!"
#: src/seedsigner/views/seed_views.py:1053
msgid "Never photograph it or scan it into an online device."
@@ -1044,7 +1044,7 @@ msgstr "Regresar al PSBT"
#: src/seedsigner/views/settings_views.py:20
msgid "I/O test"
msgstr "Test de hardware"
msgstr "Examen de hardware"
#: src/seedsigner/views/settings_views.py:39
#: src/seedsigner/views/settings_views.py:46
@@ -4,8 +4,8 @@
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
# Translators:
# Jp Prudhomme <jpprudhomme@gmail.com>, 2022
# Keith Mukai, 2022
# Jp Prudhomme <jpprudhomme@gmail.com>, 2022
#
#, fuzzy
msgid ""
@@ -14,7 +14,7 @@ msgstr ""
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-05-10 17:32-0500\n"
"PO-Revision-Date: 2022-04-29 13:50+0000\n"
"Last-Translator: Keith Mukai, 2022\n"
"Last-Translator: Jp Prudhomme <jpprudhomme@gmail.com>, 2022\n"
"Language-Team: French (https://www.transifex.com/seedsigner/teams/135832/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -176,7 +176,7 @@ msgstr "Scan d'un QRcode"
#: src/seedsigner/gui/screens/scan_screens.py:138
msgid "Settings QR"
msgstr "Paramètres QRcode"
msgstr "QRcode Paramètres"
#: src/seedsigner/gui/screens/scan_screens.py:156
msgid "Settings imported successfully!"
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -226,7 +226,7 @@ class PSBTAddressDetailsView(View):
"""
Shows the recipient's address and amount they will receive
"""
def __init__(self, address_num, is_change=False):
def __init__(self, address_num):
super().__init__()
self.address_num = address_num
+38 -4
View File
@@ -209,7 +209,20 @@ class SeedFinalizeView(View):
def __init__(self):
super().__init__()
self.seed = self.controller.storage.get_pending_seed()
self.fingerprint = self.seed.get_fingerprint(network=self.settings.get_value(SettingsConstants.SETTING__NETWORK))
if self.seed.get_fingerprint == "":
# Expected normal user flow
self.fingerprint = self.seed.get_fingerprint(network=self.settings.get_value(SettingsConstants.SETTING__NETWORK))
else:
# This view should display the "naked" seed's fingerprint. Normally the
# just-loaded seed would be naked, but this is special handling for the
# screenshot generator which creates a pending seed w/a passphrase already
# set.
passphrase = self.seed.passphrase
self.seed.set_passphrase("")
self.fingerprint = self.seed.get_fingerprint(network=self.settings.get_value(SettingsConstants.SETTING__NETWORK))
self.seed.set_passphrase(passphrase)
def run(self):
@@ -232,8 +245,28 @@ class SeedFinalizeView(View):
return Destination(SeedOptionsView, view_args={"seed_num": seed_num}, clear_history=True)
elif button_data[selected_menu_num] == PASSPHRASE:
if self.settings.get_value(attr_name=SettingsConstants.SETTING__PASSPHRASE_WARNING) == SettingsConstants.OPTION__ENABLED:
return Destination(SeedPassphraseWarningView)
else:
return Destination(SeedAddPassphraseView)
class SeedPassphraseWarningView(View):
def run(self):
selected_menu_num = WarningScreen(
status_headline=None,
text=_("""BIP-39 passphrase changes your seed, resulting in new xpubs, wallets, etc.\nBe sure you know what you're doing!""")
).display()
if selected_menu_num == 0:
# User clicked "I Understand"
return Destination(SeedAddPassphraseView)
elif selected_menu_num == RET_CODE__BACK_BUTTON:
return Destination(BackStackView)
class SeedAddPassphraseView(View):
@@ -432,10 +465,9 @@ class SeedBackupView(View):
EXPORT_SEEDQR = _("Export as SeedQR")
button_data = [VIEW_WORDS, EXPORT_SEEDQR]
selected_menu_num = ButtonListScreen(
title=_("Backup Seed"),
selected_menu_num = seed_screens.SeedBackupScreen(
button_data=button_data,
is_bottom_list=True,
has_passphrase=self.seed.has_passphrase,
).display()
if selected_menu_num == RET_CODE__BACK_BUTTON:
@@ -999,6 +1031,8 @@ class SeedTranscribeSeedQRFormatView(View):
button_data = [STANDARD, COMPACT]
print(f"button_data: {button_data}")
selected_menu_num = seed_screens.SeedTranscribeSeedQRFormatScreen(
title=_("SeedQR Format"),
button_data=button_data,
-1
View File
@@ -144,7 +144,6 @@ class UnhandledExceptionView(View):
text=self.error[1] + "\n" + self.error[2],
button_data=[_("OK")],
show_back_button=False,
allow_text_overflow=True, # Fit what we can, let the rest go off the edges
).display()
return Destination(MainMenuView, clear_history=True)
+141 -34
View File
@@ -1,3 +1,4 @@
import embit
import os
import pathlib
import pytest
@@ -7,9 +8,13 @@ from seedsigner.controller import Controller
from seedsigner.gui.renderer import Renderer
from seedsigner.hardware.buttons import HardwareButtons
from seedsigner.hardware.camera import Camera
from seedsigner.models.decode_qr import DecodeQR
from seedsigner.models.qr_type import QRType
from seedsigner.models.seed import Seed
from seedsigner.models.settings import Settings
from seedsigner.models.settings_definition import SettingsConstants
from seedsigner.views import main_menu_views, scan_views, seed_views, settings_views, tools_views
from seedsigner.models.settings_definition import SettingsConstants, SettingsDefinition
from seedsigner.views import (main_menu_views, psbt_views, scan_views, seed_views,
settings_views, tools_views)
from seedsigner.views.view import View
from .utils import ScreenshotComplete, ScreenshotRenderer
@@ -28,7 +33,7 @@ def test_generate_screenshots():
Camera.get_instance = Mock()
# Prep the ScreenshotRenderer that will be patched over the normal Renderer
screenshot_root = "/Users/kdmukai/dev/seedsigner_screenshots"
screenshot_root = "/Users/kdmukai/dev/seedsigner-screenshots"
ScreenshotRenderer.configure_instance()
screenshot_renderer: ScreenshotRenderer = ScreenshotRenderer.get_instance()
@@ -36,37 +41,121 @@ def test_generate_screenshots():
Renderer.configure_instance = Mock()
Renderer.get_instance = Mock(return_value=screenshot_renderer)
controller = Controller.get_instance()
# Set up some test data that we'll need in the `Controller` for certain Views
mnemonic_12 = "forum undo fragile fade shy sign arrest garment culture tube off merit".split()
mnemonic_24 = "attack pizza motion avocado network gather crop fresh patrol unusual wild holiday candy pony ranch winter theme error hybrid van cereal salon goddess expire".split()
mnemonic_12b = ["abandon"] * 11 + ["about"]
seed_12 = Seed(mnemonic=mnemonic_12, passphrase="cap*BRACKET3stove", wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
seed_12b = Seed(mnemonic=mnemonic_12b, wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
seed_24 = Seed(mnemonic=mnemonic_24, passphrase="some-PASS*phrase9", wordlist_language_code=SettingsConstants.WORDLIST_LANGUAGE__ENGLISH)
controller.storage.seeds.append(seed_12)
controller.storage.seeds.append(seed_12b)
controller.storage.set_pending_seed(seed_24)
# Load a PSBT into memory
BASE64_PSBT_1 = """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"""
decoder = DecodeQR()
decoder.add_data(BASE64_PSBT_1)
controller.psbt = decoder.get_psbt()
controller.psbt_seed = seed_12b
# Multisig wallet descriptor for the multisig in the above PSBT
MULTISIG_WALLET_DESCRIPTOR = """wsh(sortedmulti(1,[22bde1a9/48h/1h/0h/2h]tpubDFfsBrmpj226ZYiRszYi2qK6iGvh2vkkghfGB2YiRUVY4rqqedHCFEgw12FwDkm7rUoVtq9wLTKc6BN2sxswvQeQgp7m8st4FP8WtP8go76/{0,1}/*,[73c5da0a/48h/1h/0h/2h]tpubDFH9dgzveyD8zTbPUFuLrGmCydNvxehyNdUXKJAQN8x4aZ4j6UZqGfnqFrD4NqyaTVGKbvEW54tsvPTK2UoSbCC1PJY8iCNiwTL3RWZEheQ/{0,1}/*))#3jhtf6yx"""
controller.multisig_wallet_descriptor = embit.descriptor.Descriptor.from_string(MULTISIG_WALLET_DESCRIPTOR)
# Parse the main `babel/messages.pot` for overall stats
messages_source_path = os.path.join(pathlib.Path(__file__).parent.resolve().parent.resolve().parent.resolve(), "babel", "messages.pot")
with open(messages_source_path, 'r') as messages_source_file:
num_source_messages = messages_source_file.read().count("msgid \"") - 1
def screencap_view(view_cls: View, view_args: dict={}):
screenshot_renderer.set_screenshot_filename(view_cls.__name__ + ".png")
with pytest.raises(ScreenshotComplete):
def screencap_view(view_cls: View, view_name: str, view_args: dict={}):
screenshot_renderer.set_screenshot_filename(f"{view_name}.png")
try:
print(f"Running {view_name}")
view_cls(**view_args).run()
except ScreenshotComplete:
# Slightly hacky way to exit ScreenshotRenderer as expected
pass
print(f"Completed {view_name}")
except Exception as e:
# Something else went wrong
print(repr(e))
raise e
# Automatically populate all Settings options Views
settings_views_list = []
settings_views_list.append(settings_views.SettingsMenuView)
for settings_entry in SettingsDefinition.settings_entries:
if settings_entry.visibility == SettingsConstants.VISIBILITY__HIDDEN:
continue
settings_views_list.append((settings_views.SettingsEntryUpdateSelectionView, dict(attr_name=settings_entry.attr_name), f"SettingsEntryUpdateSelectionView_{settings_entry.attr_name}"))
settings_views_list.append(settings_views.IOTestView)
settings_views_list.append(settings_views.DonateView)
screenshot_list = [
main_menu_views.MainMenuView,
main_menu_views.PowerOffView,
screenshot_sections = {
"Main Menu Views": [
main_menu_views.MainMenuView,
main_menu_views.PowerOffView,
(scan_views.SettingsUpdatedView, dict(config_name="Keith's Settings")),
],
"Seed Views": [
seed_views.SeedsMenuView,
seed_views.LoadSeedView,
seed_views.SeedMnemonicEntryView,
seed_views.SeedMnemonicInvalidView,
seed_views.SeedFinalizeView,
seed_views.SeedPassphraseWarningView,
seed_views.SeedAddPassphraseView,
seed_views.SeedReviewPassphraseView,
(seed_views.SeedOptionsView, dict(seed_num=0)),
(seed_views.SeedBackupView, dict(seed_num=0)),
(seed_views.SeedWordsWarningView, dict(seed_num=0)),
(seed_views.SeedWordsView, dict(seed_num=0)),
(seed_views.SeedWordsView, dict(seed_num=0, page_index=2), "SeedWordsView_2"),
(seed_views.SeedTranscribeSeedQRFormatView, dict(seed_num=0)),
(seed_views.SeedTranscribeSeedQRWarningView, dict(seed_num=0)),
(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=0, seedqr_format=QRType.SEED__SEEDQR, num_modules=25), "SeedTranscribeSeedQRWholeQRView_12_Standard"),
(seed_views.SeedTranscribeSeedQRWholeQRView, dict(seed_num=0, seedqr_format=QRType.SEED__COMPACTSEEDQR, num_modules=21), "SeedTranscribeSeedQRWholeQRView_12_Compact"),
(scan_views.SettingsUpdatedView, dict(config_name="Keith's Settings")),
# Screenshot doesn't render properly due to how the transparency mask is pre-rendered
# (seed_views.SeedTranscribeSeedQRZoomedInView, dict(seed_num=0, seedqr_format=QRType.SEED__SEEDQR)),
seed_views.LoadSeedView,
seed_views.SeedMnemonicEntryView,
seed_views.SeedMnemonicInvalidView,
(seed_views.SeedWordsWarningView, dict(seed_num=0)),
(seed_views.SeedTranscribeSeedQRConfirmQRPromptView, dict(seed_num=0)),
settings_views.SettingsMenuView,
(settings_views.SettingsEntryUpdateSelectionView, dict(attr_name=SettingsConstants.SETTING__BTC_DENOMINATION)),
settings_views.IOTestView,
settings_views.DonateView,
# Screenshot can't render live preview screens
# (seed_views.SeedTranscribeSeedQRConfirmScanView, dict(seed_num=0)),
tools_views.ToolsMenuView,
tools_views.ToolsDiceEntropyMnemonicLengthView,
(tools_views.ToolsDiceEntropyEntryView, dict(total_rolls=50)),
]
seed_views.LoadMultisigWalletDescriptorView,
seed_views.MultisigWalletDescriptorView,
],
"PSBT Views": [
psbt_views.PSBTSelectSeedView,
psbt_views.PSBTOverviewView,
psbt_views.PSBTUnsupportedScriptTypeWarningView,
psbt_views.PSBTNoChangeWarningView,
psbt_views.PSBTMathView,
(psbt_views.PSBTAddressDetailsView, dict(address_num=0)),
# TODO: Render Multisig change w/ and w/out the multisig wallet descriptor onboard
(psbt_views.PSBTChangeDetailsView, dict(change_address_num=0)),
(psbt_views.PSBTAddressVerificationFailedView, dict(is_change=True, is_multisig=False), "PSBTAddressVerificationFailedView_singlesig_change"),
(psbt_views.PSBTAddressVerificationFailedView, dict(is_change=False, is_multisig=False), "PSBTAddressVerificationFailedView_singlesig_selftransfer"),
(psbt_views.PSBTAddressVerificationFailedView, dict(is_change=True, is_multisig=True), "PSBTAddressVerificationFailedView_multisig_change"),
(psbt_views.PSBTAddressVerificationFailedView, dict(is_change=False, is_multisig=True), "PSBTAddressVerificationFailedView_multisig_selftransfer"),
psbt_views.PSBTFinalizeView,
psbt_views.PSBTSelectCoordinatorView,
psbt_views.PSBTSigningErrorView,
],
"Tools Views": [
tools_views.ToolsMenuView,
tools_views.ToolsDiceEntropyMnemonicLengthView,
(tools_views.ToolsDiceEntropyEntryView, dict(total_rolls=50)),
],
"Settings Views": settings_views_list,
}
main_readme = """# SeedSigner Screenshots \n\n"""
@@ -79,24 +168,42 @@ def test_generate_screenshots():
# Report the translation progress
if locale != SettingsConstants.LOCALE__ENGLISH:
# DEBUGGING
continue
# /DEBUGGING
translated_messages_path = os.path.join(pathlib.Path(__file__).parent.resolve().parent.resolve().parent.resolve(), "src", "seedsigner", "resources", "babel", locale, "LC_MESSAGES", "messages.po")
with open(translated_messages_path, 'r') as translation_file:
locale_translations = translation_file.read()
num_locale_translations = locale_translations.count("msgid \"") - locale_translations.count("""msgstr ""\n\n""") - 1
locale_readme += f"Translation progress: {num_locale_translations / num_source_messages:.1%}\n\n"
locale_readme += f"## Translation progress: {num_locale_translations / num_source_messages:.1%}\n\n"
locale_readme += "---\n\n"
for screenshot in screenshot_list:
if type(screenshot) == tuple:
view_cls, view_args = screenshot
else:
view_cls = screenshot
view_args = {}
screencap_view(view_cls, view_args)
locale_readme += f"{view_cls.__name__}:\n\n"
locale_readme += f"""<img src="{view_cls.__name__}.png">\n\n"""
locale_readme += "---\n\n"
for section_name, screenshot_list in screenshot_sections.items():
locale_readme += "\n\n---\n\n"
locale_readme += f"## {section_name}\n\n"
locale_readme += """<table style="border: 0;">"""
locale_readme += f"""<tr><td align="center">"""
for screenshot in screenshot_list:
if type(screenshot) == tuple:
if len(screenshot) == 2:
view_cls, view_args = screenshot
view_name = view_cls.__name__
elif len(screenshot) == 3:
view_cls, view_args, view_name = screenshot
else:
view_cls = screenshot
view_args = {}
view_name = view_cls.__name__
screencap_view(view_cls, view_name, view_args)
locale_readme += """<table align="left" style="border: 1px solid gray;">"""
locale_readme += f"""<tr><td align="center">{view_name}<br/><br/><img src="{view_name}.png"></td></tr>"""
locale_readme += """</table>"""
locale_readme += "</td></tr></table>"
with open(os.path.join(screenshot_renderer.screenshot_path, "README.md"), 'w') as readme_file:
readme_file.write(locale_readme)