diff --git a/assets/translations/en-Shaw.json b/assets/translations/en-Shaw.json new file mode 100644 index 000000000..29f173b60 --- /dev/null +++ b/assets/translations/en-Shaw.json @@ -0,0 +1,65 @@ +{ + "title-intro": "๐‘ข๐‘ง๐‘ค๐‘’๐‘ฉ๐‘ฅ ๐‘‘ {}", + "header-intro": "๐‘๐‘ฎ๐‘ฆ๐‘๐‘ฉ๐‘•๐‘ฆ ๐‘ฏ ๐‘“๐‘ฎ๐‘ฐ๐‘›๐‘ฉ๐‘ฅ\n๐‘ข๐‘ฆ๐‘ž๐‘ฌ๐‘‘ ๐‘ž ๐‘ฃ๐‘จ๐‘•๐‘ฉ๐‘ค.", + "title-proxy-use-basic-authentication": "๐‘ฟ๐‘Ÿ ๐‘๐‘ฎ๐‘ช๐‘’๐‘•๐‘ฆ ๐‘ท๐‘”๐‘ง๐‘ฏ๐‘‘๐‘ฆ๐‘’๐‘ฑ๐‘–๐‘ฉ๐‘ฏ", + "title-dialog-captcha": "๐‘’๐‘ฉ๐‘ฅ๐‘๐‘ค๐‘ฐ๐‘‘ ยท๐‘’๐‘จ๐‘๐‘—๐‘ฉ", + "header-login": "๐‘‘๐‘ฑ๐‘’ ๐‘š๐‘จ๐‘’ ๐‘ž ๐‘—๐‘จ๐‘‘", + "header-signup-username": "๐‘’๐‘ฎ๐‘ฆ๐‘ฑ๐‘‘ ๐‘ฉ ๐‘ฟ๐‘Ÿ๐‘ผ๐‘ฏ๐‘ฑ๐‘ฅ", + "title-view-delete-devices": "๐‘’๐‘ฉ๐‘ฏ๐‘“๐‘ป๐‘ฅ ๐‘›๐‘ฆ๐‘๐‘ฒ๐‘• ๐‘ฎ๐‘ฆ๐‘ฅ๐‘ต๐‘๐‘ฉ๐‘ค", + "title-view-search-groups": "๐‘ฆ๐‘’๐‘•๐‘๐‘ค๐‘น ๐‘œ๐‘ฎ๐‘ต๐‘๐‘•", + "title-view-search-users": "๐‘•๐‘ป๐‘— ๐‘ฟ๐‘Ÿ๐‘ผ๐‘Ÿ", + "title-view-invite-users": "๐‘ฆ๐‘ฏ๐‘๐‘ฒ๐‘‘ ๐‘ฟ๐‘Ÿ๐‘ผ๐‘Ÿ", + "title-view-chat-users": "๐‘ท๐‘ค ๐‘—๐‘จ๐‘‘ ๐‘ฟ๐‘Ÿ๐‘ผ๐‘Ÿ", + "title-view-create-group-public": "๐‘’๐‘ฎ๐‘ฆ๐‘ฑ๐‘‘ ๐‘ฉ ๐‘๐‘ณ๐‘š๐‘ค๐‘ฆ๐‘’ ๐‘œ๐‘ฎ๐‘ต๐‘", + "title-view-devices": "๐‘›๐‘ฆ๐‘๐‘ฒ๐‘•๐‘ฉ๐‘Ÿ", + "title-view-settings": "๐‘•๐‘ง๐‘‘๐‘ฆ๐‘™๐‘Ÿ", + "title-view-theming": "๐‘ฉ๐‘๐‘ฝ๐‘ฉ๐‘ฏ๐‘•", + "title-view-invite": "๐‘ฆ๐‘ฏ๐‘๐‘ฒ๐‘‘", + "title-view-settings-chat": "๐‘—๐‘จ๐‘‘๐‘•", + "title-view-homeserver-search": "๐‘“๐‘ฒ๐‘ฏ๐‘› ๐‘˜๐‘น ๐‘ฃ๐‘ด๐‘ฅ๐‘•๐‘ป๐‘๐‘ผ", + "title-proxy-host": "๐‘ฅ๐‘ช๐‘›๐‘ฆ๐‘“๐‘ฒ ๐‘๐‘ฎ๐‘ช๐‘’๐‘•๐‘ฆ ๐‘ฃ๐‘ด๐‘•๐‘‘", + "title-proxy-port": "๐‘ฅ๐‘ช๐‘›๐‘ฆ๐‘“๐‘ฒ ๐‘๐‘ฎ๐‘ช๐‘’๐‘•๐‘ฆ ๐‘๐‘น๐‘‘", + "title-proxy-username": "๐‘ฅ๐‘ช๐‘›๐‘ฆ๐‘“๐‘ฒ ๐‘๐‘ฎ๐‘ช๐‘’๐‘•๐‘ฆ ๐‘ฟ๐‘Ÿ๐‘ผ๐‘ฏ๐‘ฑ๐‘ฅ", + "title-proxy-password": "๐‘ฅ๐‘ช๐‘›๐‘ฆ๐‘“๐‘ฒ ๐‘๐‘ฎ๐‘ช๐‘’๐‘•๐‘ฆ ๐‘๐‘ญ๐‘•๐‘ข๐‘ป๐‘›", + "title-search-unencrypted": "๐‘•๐‘ป๐‘— ๐‘ณ๐‘ฏ๐‘ฆ๐‘ฏ๐‘’๐‘ฎ๐‘ฆ๐‘๐‘‘๐‘ฉ๐‘›", + "title-message-details": "๐‘ฅ๐‘ง๐‘•๐‘ฆ๐‘ก ๐‘›๐‘ฐ๐‘‘๐‘ฑ๐‘ค๐‘Ÿ", + "title-send-media-message": "๐‘•๐‘ง๐‘ฏ๐‘› ๐‘ฅ๐‘ฐ๐‘›๐‘พ ๐‘ฅ๐‘ง๐‘•๐‘ฆ๐‘ก", + "title-send-media-message-unencrypted": "๐‘•๐‘ง๐‘ฏ๐‘› ๐‘ฅ๐‘ฐ๐‘›๐‘พ ๐‘ฅ๐‘ง๐‘•๐‘ฆ๐‘ก (๐‘ณ๐‘ฏ๐‘ฆ๐‘ฏ๐‘’๐‘ฎ๐‘ฆ๐‘๐‘‘๐‘ฉ๐‘›)", + "title-toggle-autocorrect": "๐‘‘๐‘ช๐‘œ๐‘ฉ๐‘ค ๐‘’๐‘ฐ๐‘š๐‘น๐‘› ๐‘ท๐‘‘๐‘ด๐‘’๐‘ผ๐‘ง๐‘’๐‘‘", + "title-dialog-confirm-linkout": "๐‘’๐‘ฉ๐‘ฏ๐‘“๐‘ป๐‘ฅ ๐‘ด๐‘๐‘ฉ๐‘ฏ๐‘ฆ๐‘™ ๐‘ค๐‘ฆ๐‘™๐‘’", + "title-dialog-encryption": "๐‘ฆ๐‘ฏ๐‘’๐‘ฎ๐‘ฆ๐‘๐‘‘ ๐‘—๐‘จ๐‘‘?", + "title-dialog-email-requirement": "๐‘ฐ๐‘ฅ๐‘ฑ๐‘ค ๐‘ฎ๐‘ฆ๐‘’๐‘ข๐‘ฒ๐‘ผ๐‘ฅ๐‘ฉ๐‘ฏ๐‘‘", + "title-dialog-email-requirement-verified": "๐‘ฐ๐‘ฅ๐‘ฑ๐‘ค ๐‘๐‘ง๐‘ฎ๐‘ฆ๐‘“๐‘ฆ๐‘’๐‘ฑ๐‘–๐‘ฉ๐‘ฏ", + "title-device-rename": "๐‘ฎ๐‘ฐ๐‘ฏ๐‘ฑ๐‘ฅ ๐‘ฉ ๐‘›๐‘ฆ๐‘๐‘ฒ๐‘•", + "label-on": "๐‘ช๐‘ฏ", + "label-off": "๐‘ช๐‘“", + "label-email": "๐‘ฐ๐‘ฅ๐‘ฑ๐‘ค", + "label-users": "๐‘ฟ๐‘Ÿ๐‘ผ๐‘Ÿ", + "label-back": "๐‘œ๐‘ด ๐‘š๐‘จ๐‘’", + "label-close": "๐‘’๐‘ค๐‘ด๐‘•", + "label-send": "๐‘•๐‘ง๐‘ฏ๐‘›", + "label-download-image": "๐‘ค๐‘ด๐‘› ๐‘ฆ๐‘ฅ๐‘ฆ๐‘ก", + "label-users-results": "๐‘ฅ๐‘จ๐‘—๐‘‘ ๐‘ฟ๐‘Ÿ๐‘ผ๐‘Ÿ", + "title-verification": "๐‘๐‘ง๐‘ฎ๐‘ฆ๐‘“๐‘ฆ๐‘’๐‘ฑ๐‘–๐‘ฉ๐‘ฏ", + "title-view-create-group": "๐‘’๐‘ฎ๐‘ฆ๐‘ฑ๐‘‘ ๐‘ฉ ๐‘œ๐‘ฎ๐‘ต๐‘ ๐‘—๐‘จ๐‘‘", + "title-view-advanced": "๐‘ฉ๐‘›๐‘๐‘ญ๐‘ฏ๐‘•๐‘‘", + "title-view-profile": "๐‘•๐‘ง๐‘‘ ๐‘ณ๐‘ ๐‘˜๐‘น ๐‘๐‘ฎ๐‘ด๐‘“๐‘ฒ๐‘ค", + "title-view-privacy": "๐‘๐‘ฎ๐‘ฆ๐‘๐‘ฉ๐‘•๐‘ฆ ๐‘ฏ ๐‘•๐‘ฆ๐‘’๐‘˜๐‘ซ๐‘ผ๐‘ฆ๐‘‘๐‘ฆ", + "title-proxy-use-proxy": "๐‘ฟ๐‘Ÿ ๐‘ฉ ๐‘๐‘ฎ๐‘ช๐‘’๐‘•๐‘ฆ", + "title-toggle-suggestions": "๐‘‘๐‘ช๐‘œ๐‘ฉ๐‘ค ๐‘’๐‘ฐ๐‘š๐‘น๐‘› ๐‘ข๐‘ป๐‘› ๐‘•๐‘ฉ๐‘ก๐‘ง๐‘•๐‘—๐‘ฉ๐‘ฏ๐‘Ÿ", + "title-dialog-draft-preview": "๐‘›๐‘ฎ๐‘ญ๐‘“๐‘‘ ๐‘๐‘ฎ๐‘ฐ๐‘๐‘ฟ", + "subtitle-proxy-use-proxy": "๐‘ž๐‘ฆ๐‘• ๐‘ข๐‘ฆ๐‘ค ๐‘“๐‘น๐‘• ๐‘ท๐‘ค ๐‘‘๐‘ฎ๐‘จ๐‘“๐‘ฆ๐‘’ ๐‘ค๐‘ฐ๐‘๐‘ฆ๐‘™ ยท๐‘•๐‘ฒ๐‘“๐‘ฉ๐‘ฏ ๐‘‘ ๐‘›๐‘ต ๐‘•๐‘ด ๐‘๐‘ฒ๐‘ฉ ๐‘ฉ ๐‘๐‘ฎ๐‘ช๐‘’๐‘•๐‘ฆ", + "title-confirm-lock-overlay": "๐‘๐‘ค๐‘ฐ๐‘Ÿ ๐‘’๐‘ฉ๐‘ฏ๐‘“๐‘ป๐‘ฅ ๐‘๐‘ญ๐‘•๐‘’๐‘ด๐‘›.", + "title-lock-overlay": "๐‘๐‘ค๐‘ฐ๐‘Ÿ ๐‘ง๐‘ฏ๐‘‘๐‘ผ ๐‘๐‘ญ๐‘•๐‘’๐‘ด๐‘›.", + "title-dialog-delete-keys": "๐‘’๐‘ฉ๐‘ฏ๐‘“๐‘ป๐‘ฅ ๐‘›๐‘ฆ๐‘ค๐‘ฐ๐‘‘๐‘ฆ๐‘™ ๐‘’๐‘ฐ๐‘Ÿ", + "title-dialog-terms": "๐‘’๐‘ฉ๐‘ฏ๐‘“๐‘ป๐‘ฅ ๐‘‘๐‘ป๐‘ฅ๐‘Ÿ ๐‘ ๐‘•๐‘ป๐‘๐‘ฆ๐‘•", + "title-dialog-terms-alpha": "๐‘’๐‘ฉ๐‘ฏ๐‘“๐‘ป๐‘ฅ ๐‘ด๐‘๐‘ฉ๐‘ฏ ๐‘จ๐‘ค๐‘“๐‘ฉ ๐‘‘๐‘ป๐‘ฅ๐‘Ÿ ๐‘ ๐‘•๐‘ป๐‘๐‘ฆ๐‘•", + "title-dialog-accept-invite": "๐‘ฉ๐‘’๐‘•๐‘ง๐‘๐‘‘ ๐‘ฆ๐‘ฏ๐‘๐‘ฒ๐‘‘?", + "title-user-verification": "๐‘ฟ๐‘Ÿ๐‘ผ ๐‘๐‘ง๐‘ฎ๐‘ฆ๐‘“๐‘ฆ๐‘’๐‘ฑ๐‘–๐‘ฉ๐‘ฏ", + "title-confirm-password": "๐‘’๐‘ฉ๐‘ฏ๐‘“๐‘ป๐‘ฅ ๐‘๐‘ญ๐‘•๐‘ข๐‘ป๐‘›", + "title-confirm-email": "๐‘’๐‘ฉ๐‘ฏ๐‘“๐‘ป๐‘ฅ ๐‘ฐ๐‘ฅ๐‘ฑ๐‘ค", + "title-dialog-chat-color": "๐‘•๐‘ฆ๐‘ค๐‘ง๐‘’๐‘‘ ๐‘—๐‘จ๐‘‘ ๐‘’๐‘ณ๐‘ค๐‘ผ", + "subtitle-proxy-use-basic-authentication": "๐‘๐‘ฎ๐‘ฉ๐‘๐‘ฒ๐‘› ๐‘ฟ๐‘Ÿ๐‘ผ๐‘ฏ๐‘ฑ๐‘ฅ ๐‘ฏ ๐‘๐‘ญ๐‘•๐‘ข๐‘ป๐‘› ๐‘‘ ๐‘ท๐‘”๐‘ง๐‘ฏ๐‘‘๐‘ฆ๐‘’๐‘ฑ๐‘‘ ๐‘ข๐‘ฆ๐‘ž ๐‘ฉ ๐‘๐‘ฎ๐‘ช๐‘’๐‘•๐‘ฆ", + "label-users-recent": "๐‘ฎ๐‘ฐ๐‘•๐‘ฉ๐‘ฏ๐‘‘ ๐‘ฟ๐‘Ÿ๐‘ผ๐‘Ÿ", + "label-users-known": "๐‘ฏ๐‘ด๐‘ฏ ๐‘ฟ๐‘Ÿ๐‘ผ๐‘Ÿ" +} diff --git a/assets/translations/en.json b/assets/translations/en.json index 1b5e4eacc..28ac96ac8 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -32,8 +32,8 @@ "title-message-details": "Message Details", "title-send-media-message": "Send Media Message", "title-send-media-message-unencrypted": "Send Media Message (Unencrypted)", - "title-toggle-autocorrect": "Toggle keyboard autocorrect", - "title-toggle-suggestions": "Toggle keyboard word suggestions", + "title-toggle-autocorrect": "Keyboard autocorrect", + "title-toggle-suggestions": "Keyboard word suggestions", "title-dialog-delete-keys": "Confirm Deleting Keys", "title-dialog-confirm-linkout": "Confirm Opening Link", "title-dialog-encryption": "Encrypt Chat?", diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 1626833a9..14ef2703a 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -237,10 +237,10 @@ "list-item-advanced-settings-start-background": "Dรฉmarrer le service en arriรจre-plan", "list-item-advanced-settings-force-function": "Forcer la fonction", "list-item-advanced-settings-test-notifications": "Tester les notifications", - "list-item-via": "Via", + "list-item-via": "Plateforme", "list-item-sent": "Envoyรฉ", "content-dialog-confirm-password": "Veuillez confirmer votre mot de passe (Par dรฉfaut)", - "message-edited-append": " (Modifiรฉ)", + "message-edited-append": " (modifiรฉ)", "label-default": "Par dรฉfaut", "semantics-image-intro": "Utilisateur dรฉtendu, allongรฉ", "title-send-media-message-unencrypted": "Envoyer un mรฉdia (non chiffrรฉ)", @@ -267,5 +267,6 @@ "list-item-chat-detail-vibrate": "Vibration", "content-notification-style-type-inbox": "Regrouper les nouveaux messages dans une mรชme notification", "content-notification-style-type-latest": "Afficher une notification avec le message le plus rรฉcent", - "content-notification-style-type-itemized": "Une nouvelle notification apparaรฎtra pour chaque nouveau message" + "content-notification-style-type-itemized": "Une nouvelle notification apparaรฎtra pour chaque nouveau message", + "label-show-attachment-options": "Ouvrir les options de piรจce jointe" } diff --git a/assets/translations/id.json b/assets/translations/id.json index 631a55b0c..786c88c1a 100644 --- a/assets/translations/id.json +++ b/assets/translations/id.json @@ -9,7 +9,7 @@ "title-view-create-group": "Buat Sebuah Grup Obrolan", "title-view-invite": "Undang", "title-view-settings-chat": "Obrolan", - "title-view-privacy": "Privasi", + "title-view-privacy": "Privasi & Keamanan", "title-view-homeserver-search": "Temukan Homeserver Anda", "title-view-profile": "Siapkan Profil Anda", "title-proxy-host": "Modifikasi Host Proksi", @@ -226,12 +226,12 @@ "list-item-advanced-settings-force-function": "Paksakan Fungsi", "list-item-sent": "Terkirim", "list-item-received": "Diterima", - "list-item-via": "Via", + "list-item-via": "Platform", "alert-offline": "Sepertinya Anda mungkin offline. Periksa koneksi Anda dan coba lagi.", "alert-unknown": "Terjadi Kesalahan Yang Tidak Diketahui", "alert-could-not-launch-url": "Tidak dapat membuka {}", "alert-no-images-found": "Tidak Ada Gambar Yang Ditemukan.", - "message-edited-append": " (Diedit)", + "message-edited-append": " (diedit)", "label-default": "Default", "semantics-image-intro": "Pengguna menyantai", "semantics-image-private-message": "Seseorang membawa sebuah gelembung pesan privat", @@ -267,5 +267,6 @@ "content-notification-style-type-inbox": "Kelompokkan pesan baru dalam satu notifikasi", "content-notification-style-type-itemized": "Sebuah notifikasi baru akan muncul untuk setiap pesan baru", "list-item-chat-detail-vibrate": "Getaran", - "content-notification-style-type-latest": "Tampilkan satu notifikasi dengan pesan terbaru" + "content-notification-style-type-latest": "Tampilkan satu notifikasi dengan pesan terbaru", + "label-show-attachment-options": "Buka opsi lampiran" } diff --git a/assets/translations/uk.json b/assets/translations/uk.json index 8f80854a1..470dff533 100644 --- a/assets/translations/uk.json +++ b/assets/translations/uk.json @@ -267,5 +267,6 @@ "semantics-image-empty-chat-list": "ะšั–ะปัŒะบะฐ ะบั€ะธั…ั–ั‚ะฝะธั…, ะผะธะปะธั… ะผะพะฝัั‚ั€ั–ะฒ, ั‰ะพ ะฒะธะณะปัะดะฐัŽั‚ัŒ ะท-ะทะฐ ะปะธัั‚ั.", "semantics-image-terms-of-service": "ะ ัƒะบะฐ ั‚ั€ะธะผะฐั” ั‚ะตะปะตั„ะพะฝ ะท ะฟั€ะฐะฟะพั€ั†ะตะผ ัƒะผะพะฒ ะฟั€ะพ ะฝะฐะดะฐะฝะฝั ะฟะพัะปัƒะณ", "semantics-image-signup-username": "ะ›ัŽะดะธะฝะฐ, ั‰ะพ ัะฟะธั€ะฐั”ั‚ัŒัั ะปั–ะบั‚ะตะผ ะฝะฐ ID-ะบะฐั€ั‚ะบัƒ", - "semantics-image-password-reset": "ะ›ัŽะดะธะฝะฐ, ั‰ะพ ะฟะพะบะปะฐะปะฐ ั€ะพะทัะปะฐะฑะปะตะฝัƒ ั€ัƒะบัƒ ะฝะฐ ะปะธัั‚ ั–ะท ะฟั€ะฐะฟะพั€ั†ะตะผ ะฒัะตั€ะตะดะธะฝั–" + "semantics-image-password-reset": "ะ›ัŽะดะธะฝะฐ, ั‰ะพ ะฟะพะบะปะฐะปะฐ ั€ะพะทัะปะฐะฑะปะตะฝัƒ ั€ัƒะบัƒ ะฝะฐ ะปะธัั‚ ั–ะท ะฟั€ะฐะฟะพั€ั†ะตะผ ะฒัะตั€ะตะดะธะฝั–", + "label-show-attachment-options": "ะ’ั–ะดะบั€ะธั‚ะธ ะฟะฐั€ะฐะผะตั‚ั€ะธ ะฒะบะปะฐะดะตะฝะฝั" } diff --git a/fastlane/metadata/android/en-US/changelogs/2100.txt b/fastlane/metadata/android/en-US/changelogs/2100.txt new file mode 100644 index 000000000..5aa659edd --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/2100.txt @@ -0,0 +1,25 @@ +0.2.10 + +๐Ÿ”ฅ Hot Fixes +- Crypto Storage performance issues +- Fixed Wordle + +๐Ÿ”ฎ New Features +- Session Key Imports / Exports +- Email and other 3pid auth support +- Fullscreen Image View +- Incoming Read Receipt Support +- Toggle autocorrect setting + +๐Ÿ› Bug Fixes +- fixed missing several translations +- fixed various notification issues +- fixed issues with deleting devices +- fixed issues deactivating accounts +- fixed showing TOS more than once + +๐Ÿ’ก Improvements +- Hide Send Button + +๐Ÿ“ Refactoring +- Lots! diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6c9e06b9f..2b4d73849 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -355,7 +355,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 2007; + CURRENT_PROJECT_VERSION = 2090; DEVELOPMENT_TEAM = W98LUTKH5G; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -371,7 +371,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.2.6; + MARKETING_VERSION = 0.2.9; PRODUCT_BUNDLE_IDENTIFIER = org.tether.tether; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -496,7 +496,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 2007; + CURRENT_PROJECT_VERSION = 2090; DEVELOPMENT_TEAM = W98LUTKH5G; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -512,7 +512,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.2.6; + MARKETING_VERSION = 0.2.9; PRODUCT_BUNDLE_IDENTIFIER = org.tether.tether; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -529,7 +529,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 2007; + CURRENT_PROJECT_VERSION = 2090; DEVELOPMENT_TEAM = W98LUTKH5G; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( @@ -545,7 +545,7 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - MARKETING_VERSION = 0.2.6; + MARKETING_VERSION = 0.2.9; PRODUCT_BUNDLE_IDENTIFIER = org.tether.tether; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/cache/middleware.dart b/lib/cache/middleware.dart index cc9923e11..4451b86c2 100644 --- a/lib/cache/middleware.dart +++ b/lib/cache/middleware.dart @@ -22,11 +22,9 @@ bool cacheMiddleware(Store store, dynamic action) { case UpdateRoom: case SetRoom: case RemoveRoom: - case SetOlmAccount: case SetOlmAccountBackup: case SetDeviceKeysOwned: case AddKeySession: - case AddMessageSessionInbound: case AddMessageSessionOutbound: case SetUser: case ResetCrypto: diff --git a/lib/cache/serializer.dart b/lib/cache/serializer.dart index 1d278a294..d3a92f897 100644 --- a/lib/cache/serializer.dart +++ b/lib/cache/serializer.dart @@ -9,6 +9,7 @@ import 'package:syphon/cache/threadables.dart'; import 'package:syphon/global/print.dart'; import 'package:syphon/storage/constants.dart'; import 'package:syphon/store/auth/state.dart'; +import 'package:syphon/store/crypto/sessions/model.dart'; import 'package:syphon/store/crypto/state.dart'; import 'package:syphon/store/events/messages/model.dart'; import 'package:syphon/store/events/reactions/model.dart'; @@ -141,10 +142,17 @@ class CacheSerializer implements StateSerializer { final cryptoState = cryptoStore ?? preloaded[StorageKeys.CRYPTO] as CryptoStore? ?? CryptoStore(); + final messageSessionsLoaded = + preloaded[StorageKeys.MESSAGE_SESSIONS] as Map>>; + return AppState( loading: false, authStore: authStore ?? preloaded[StorageKeys.AUTH] ?? AuthStore(), - cryptoStore: cryptoState.upgradeSessions_temp(), + cryptoStore: messageSessionsLoaded.isEmpty + ? cryptoState.upgradeSessions_temp() + : cryptoState.upgradeSessions_temp().copyWith( + messageSessionsInbound: preloaded[StorageKeys.MESSAGE_SESSIONS], + ), settingsStore: preloaded[StorageKeys.SETTINGS] ?? settingsStore ?? SettingsStore(), syncStore: syncStore ?? SyncStore(), mediaStore: mediaStore ?? MediaStore().copyWith(mediaCache: preloaded[StorageKeys.MEDIA]), diff --git a/lib/global/libs/matrix/devices.dart b/lib/global/libs/matrix/devices.dart index ea0a2ef92..8c3092298 100644 --- a/lib/global/libs/matrix/devices.dart +++ b/lib/global/libs/matrix/devices.dart @@ -55,12 +55,12 @@ abstract class Devices { return await json.decode(response.body); } - /** - * https://matrix.org/docs/spec/client_server/latest#id414 - * - * HTTP:DELETE - * Gets all currently active pushers for the authenticated user. - */ + /// + /// https://matrix.org/docs/spec/client_server/latest#id414 + /// + /// HTTP:DELETE + /// Gets all currently active pushers for the authenticated user. + /// static Future deleteDevices({ String? protocol = 'https://', String? homeserver = Values.homeserverDefault, @@ -100,12 +100,10 @@ abstract class Devices { return await json.decode(response.body); } - /** - * https://spec.matrix.org/unstable/client-server-api/#put_matrixclientv3devicesdeviceid - * - * HTTP:PUT - * Change a given Device's public display name. - */ + /// https://spec.matrix.org/unstable/client-server-api/#put_matrixclientv3devicesdeviceid + /// + /// HTTP:PUT + /// Change a given Device's public display name. static Future renameDevice({ String? protocol = 'https://', String? homeserver = Values.homeserverDefault, @@ -132,5 +130,4 @@ abstract class Devices { return await json.decode(response.body); } - } diff --git a/lib/global/print.dart b/lib/global/print.dart index d4dd06fd7..73d0db8e0 100644 --- a/lib/global/print.dart +++ b/lib/global/print.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:syphon/global/values.dart'; typedef PrintJson = void Function(Map? jsonMap); typedef PrintDebug = void Function(String message, {String title}); @@ -8,28 +9,43 @@ typedef PrintError = void Function(String message, {String? title}); void _printInfo(String content, {String? title}) { final output = title != null ? '[$title] $content' : content; - print(output); + if (DEBUG_MODE) { + print(output); + } } void _printWarning(String content, {String? title}) { final output = title != null ? '[$title] $content' : content; - print(output); + if (DEBUG_MODE) { + print(output); + } } void _printError(String content, {String? title}) { + final output = title != null ? '[$title] $content' : content; + if (DEBUG_MODE) { + print(output); + } +} + +void _printRelease(String content, {String? title}) { final output = title != null ? '[$title] $content' : content; print(output); } void _printDebug(String content, {String? title}) { final output = title != null ? '[$title] $content' : content; - debugPrint(output); + if (DEBUG_MODE) { + debugPrint(output); + } } void _printJson(Map? jsonMap) { final JsonEncoder encoder = JsonEncoder.withIndent(' '); final String prettyEvent = encoder.convert(jsonMap); - debugPrint(prettyEvent, wrapWidth: 2048); + if (DEBUG_MODE) { + debugPrint(prettyEvent, wrapWidth: 2048); + } } PrintJson printJson = _printJson; @@ -45,5 +61,6 @@ class log { static warn(String content, {String? title}) => _printWarning(content, title: title); static error(String content, {String? title}) => _printError(content, title: title); static debug(String content, {String? title}) => _printDebug(content, title: title); + static release(String content, {String? title}) => _printRelease(content, title: title); static json(Map? json) => _printJson(json); } diff --git a/lib/storage/constants.dart b/lib/storage/constants.dart index 9e209cfcc..330e3916b 100644 --- a/lib/storage/constants.dart +++ b/lib/storage/constants.dart @@ -18,6 +18,9 @@ class StorageKeys { static const String RECEIPTS = 'receipts'; static const String REACTIONS = 'reactions'; static const String REDACTIONS = 'redactions'; + + // crypto + static const String MESSAGE_SESSIONS = 'message_sessions'; } enum StorageKey { @@ -33,7 +36,8 @@ enum StorageKey { decrypted, receipts, reactions, - redactions + redactions, + messageSessions, } const int DEFAULT_LOAD_LIMIT = 25; diff --git a/lib/storage/database.dart b/lib/storage/database.dart index 232020e19..4c028a7b9 100644 --- a/lib/storage/database.dart +++ b/lib/storage/database.dart @@ -21,6 +21,7 @@ import 'package:syphon/storage/migrations/5.update.messages.dart'; import 'package:syphon/storage/models.dart'; import 'package:syphon/store/auth/schema.dart'; import 'package:syphon/store/crypto/schema.dart'; +import 'package:syphon/store/crypto/sessions/schema.dart'; import 'package:syphon/store/events/messages/model.dart'; import 'package:syphon/store/events/messages/schema.dart'; import 'package:syphon/store/events/reactions/model.dart'; @@ -210,19 +211,18 @@ LazyDatabase openDatabase(AppContext context, {String pin = Values.empty}) { Auths, Syncs, Cryptos, + MessageSessions, + KeySessions, Settings, ]) class StorageDatabase extends _$StorageDatabase { - // we tell the database where to store the data with this constructor StorageDatabase(AppContext context, {String pin = ''}) : super(openDatabase(context, pin: pin)); - // this is the new constructor StorageDatabase.connect(DatabaseConnection connection) : super.connect(connection); - // you should bump this number whenever you change or add a table definition. Migrations - // are covered later in this readme. + // you should bump this number whenever you change or add a table definition. @override - int get schemaVersion => 7; + int get schemaVersion => 8; @override MigrationStrategy get migration => MigrationStrategy( @@ -231,6 +231,10 @@ class StorageDatabase extends _$StorageDatabase { }, onUpgrade: (Migrator m, int from, int to) async { printInfo('[MIGRATION] VERSION $from to $to'); + if (from == 7) { + await m.createTable(keySessions); + await m.createTable(messageSessions); + } if (from == 6) { await m.createTable(syncs); } diff --git a/lib/storage/index.dart b/lib/storage/index.dart index a26b38f69..99dff4d6d 100644 --- a/lib/storage/index.dart +++ b/lib/storage/index.dart @@ -11,6 +11,7 @@ import 'package:syphon/global/values.dart'; import 'package:syphon/storage/constants.dart'; import 'package:syphon/storage/database.dart'; import 'package:syphon/store/auth/storage.dart'; +import 'package:syphon/store/crypto/sessions/storage.dart'; import 'package:syphon/store/crypto/storage.dart'; import 'package:syphon/store/events/messages/actions.dart'; import 'package:syphon/store/events/messages/model.dart'; @@ -117,6 +118,11 @@ Future> loadStorage(StorageDatabase storage) async { rooms: rooms.values.toList(), ); + final messageSessions = await loadMessageSessionsInbound( + roomIds: rooms.keys.toList(), + storage: storage, + ); + return { StorageKeys.AUTH: auth, StorageKeys.CRYPTO: crypto, @@ -127,6 +133,7 @@ Future> loadStorage(StorageDatabase storage) async { StorageKeys.MESSAGES: messages, StorageKeys.REACTIONS: reactions, StorageKeys.DECRYPTED: decrypted, + StorageKeys.MESSAGE_SESSIONS: messageSessions, }; } catch (error) { printError('[loadStorage] ${error.toString()}'); diff --git a/lib/storage/middleware/save-storage-middleware.dart b/lib/storage/middleware/save-storage-middleware.dart index 4015d5f53..391daffba 100644 --- a/lib/storage/middleware/save-storage-middleware.dart +++ b/lib/storage/middleware/save-storage-middleware.dart @@ -7,6 +7,7 @@ import 'package:syphon/store/auth/storage.dart'; import 'package:syphon/store/crypto/actions.dart'; import 'package:syphon/store/crypto/keys/actions.dart'; import 'package:syphon/store/crypto/sessions/actions.dart'; +import 'package:syphon/store/crypto/sessions/storage.dart'; import 'package:syphon/store/crypto/storage.dart'; import 'package:syphon/store/events/actions.dart'; import 'package:syphon/store/events/messages/storage.dart'; @@ -25,9 +26,7 @@ import 'package:syphon/store/settings/actions.dart'; import 'package:syphon/store/settings/notification-settings/actions.dart'; import 'package:syphon/store/settings/proxy-settings/actions.dart'; import 'package:syphon/store/settings/storage.dart'; -import 'package:syphon/store/sync/actions.dart'; import 'package:syphon/store/sync/background/storage.dart'; -import 'package:syphon/store/sync/storage.dart'; import 'package:syphon/store/user/actions.dart'; import 'package:syphon/store/user/storage.dart'; @@ -166,13 +165,28 @@ saveStorageMiddleware(StorageDatabase? storage) { case SetDeviceKeys: case SetOneTimeKeysCounts: case SetOneTimeKeysClaimed: - case AddMessageSessionInbound: case AddMessageSessionOutbound: case UpdateMessageSessionOutbound: case AddKeySession: case ResetCrypto: saveCrypto(store.state.cryptoStore, storage: storage); break; + case AddMessageSessionInbound: + final _action = action as AddMessageSessionInbound; + saveMessageSessionInbound( + roomId: _action.roomId, + identityKey: _action.senderKey, + session: _action.session, + messageIndex: _action.messageIndex, + storage: storage, + ); + break; + case SaveMessageSessionsInbound: + saveMessageSessionsInbound( + store.state.cryptoStore.messageSessionsInbound, + storage: storage, + ); + break; case SetNotificationSettings: // handles updating the background sync thread with new chat settings saveNotificationSettings( diff --git a/lib/store/auth/actions.dart b/lib/store/auth/actions.dart index 8d29c7597..9de847fa6 100644 --- a/lib/store/auth/actions.dart +++ b/lib/store/auth/actions.dart @@ -1320,6 +1320,9 @@ ThunkAction resolveUsername({String? username}) { } else if (int.tryParse(localpart) != null) { //msisdn 3pid store.dispatch(setMsisdn(msisdn: int.parse(localpart))); + } else { + // raw username + store.dispatch(setUsername(username: localpart)); } // If user enters full username, make sure to set homeserver diff --git a/lib/store/auth/selectors.dart b/lib/store/auth/selectors.dart index a9ae04f77..99f1561b7 100644 --- a/lib/store/auth/selectors.dart +++ b/lib/store/auth/selectors.dart @@ -1,5 +1,6 @@ import 'package:syphon/global/libs/matrix/auth.dart'; import 'package:syphon/global/libs/matrix/errors.dart'; +import 'package:syphon/global/print.dart'; import 'package:syphon/store/index.dart'; // Preauth diff --git a/lib/store/crypto/events/actions.dart b/lib/store/crypto/events/actions.dart index 6894f1319..4142c007f 100644 --- a/lib/store/crypto/events/actions.dart +++ b/lib/store/crypto/events/actions.dart @@ -50,7 +50,7 @@ ThunkAction encryptMessageContent({ final encryptedPayload = outboundMessageSession.encrypt(serializedPayload); // save the outbound session after processing content - await store.dispatch(saveMessageSessionOutbound( + await store.dispatch(AddMessageSessionOutbound( roomId: roomId, session: outboundMessageSession.pickle(roomId), )); @@ -258,7 +258,7 @@ ThunkAction decryptMessage({ ); } - await store.dispatch(saveMessageSessionInbound( + await store.dispatch(addMessageSessionInbound( roomId: roomId, identityKey: identityKey, session: session, diff --git a/lib/store/crypto/sessions/actions.dart b/lib/store/crypto/sessions/actions.dart index 5869b0815..0676dc94c 100644 --- a/lib/store/crypto/sessions/actions.dart +++ b/lib/store/crypto/sessions/actions.dart @@ -20,6 +20,7 @@ import 'package:syphon/store/crypto/sessions/converters.dart'; import 'package:syphon/store/crypto/sessions/model.dart'; import 'package:syphon/store/index.dart'; import 'package:syphon/store/settings/actions.dart'; +import 'package:syphon/store/sync/actions.dart'; /// /// @@ -95,7 +96,13 @@ class SetMessageSessionsInbound { SetMessageSessionsInbound({required this.sessions}); } -ThunkAction saveMessageSessionOutbound({ +class SaveMessageSessionsInbound { + Map>> sessions; + + SaveMessageSessionsInbound({required this.sessions}); +} + +ThunkAction addMessageSessionOutbound({ required String roomId, required String session, }) { @@ -332,7 +339,7 @@ ThunkAction loadMessageSessionInbound({ /// Save Message Session Inbound /// /// Saves the message session and index after encrypting and sending an event -ThunkAction saveMessageSessionInbound({ +ThunkAction addMessageSessionInbound({ required String roomId, required String identityKey, required olm.InboundGroupSession session, @@ -552,9 +559,11 @@ ThunkAction importSessionKeys(FilePickerResult file, {String? password return (Store store) async { try { store.dispatch(SetLoadingSettings(loading: true)); + await store.dispatch(stopSyncObserver()); final keyFile = File(file.paths[0]!); final fileData = await keyFile.readAsString(); + final roomsEncrypted = store.state.roomStore.roomList.where((room) => room.encryptionEnabled); var sessionJson; @@ -567,7 +576,6 @@ ThunkAction importSessionKeys(FilePickerResult file, {String? password ); } - final roomIdsEncrypted = []; final messageSessions = Map>>.from( store.state.cryptoStore.messageSessionsInbound, ); @@ -608,31 +616,50 @@ ThunkAction importSessionKeys(FilePickerResult file, {String? password senderKey: [messageSession], }, ); - - roomIdsEncrypted.add(roomId); } + // save _all_ keys to cold storage + await store.dispatch(SaveMessageSessionsInbound( + sessions: messageSessions, + )); + + // Only set local message sessions where rooms are actively being synced + final roomIdsEncrypted = roomsEncrypted.map((room) => room.id); + final messageSessionsActive = Map>>.from( + messageSessions, + ); + messageSessionsActive.removeWhere( + (key, value) => !roomIdsEncrypted.contains(key), + ); + + // set active keys to state await store.dispatch(SetMessageSessionsInbound( - sessions: combineMessageSesssions( - messageSessions, - store.state.cryptoStore.messageSessionsInbound, - ), + sessions: messageSessionsActive, )); - for (final roomId in roomIdsEncrypted) { - store.dispatch(backfillDecryptMessages(roomId)); - } + // TODO: needs work for on combining them efficiently + // await store.dispatch(SetMessageSessionsInbound( + // sessions: combineMessageSesssions( + // messageSessions, + // store.state.cryptoStore.messageSessionsInbound, + // ), + // )); + + await Future.forEach(roomIdsEncrypted, (String roomId) { + return store.dispatch(backfillDecryptMessages(roomId)); + }); store.dispatch(addConfirmation( origin: 'importSessionKeys', message: 'Successfully imported keys, your previous messages should be decrypting.', )); + + await store.dispatch(startSyncObserver()); } catch (error) { store.dispatch(addAlert( error: error, origin: 'importSessionKeys', - message: - 'Failed to import your session key backup, contact us at https://syphon.org/support', + message: 'Failed to import your session key backup, check your password and try again.', )); } finally { store.dispatch(SetLoadingSettings(loading: false)); diff --git a/lib/store/crypto/sessions/model.dart b/lib/store/crypto/sessions/model.dart index 09c66fd16..64aa66000 100644 --- a/lib/store/crypto/sessions/model.dart +++ b/lib/store/crypto/sessions/model.dart @@ -29,24 +29,3 @@ class MessageSession extends Equatable { Map toJson() => _$MessageSessionToJson(this); factory MessageSession.fromJson(Map json) => _$MessageSessionFromJson(json); } - -/// -/// Message Session model -/// -/// allows for multiple identitykey sessions and sorting -/// based on last used or created -/// -@JsonSerializable() -class MessageSessionMapper extends Equatable { - // Map - final Map sessions; - - const MessageSessionMapper({this.sessions = const {}}); - - @override - List get props => [sessions]; - - Map toJson() => _$MessageSessionMapperToJson(this); - factory MessageSessionMapper.fromJson(Map json) => - _$MessageSessionMapperFromJson(json); -} diff --git a/lib/store/crypto/sessions/schema.dart b/lib/store/crypto/sessions/schema.dart new file mode 100644 index 000000000..2d73ccc7a --- /dev/null +++ b/lib/store/crypto/sessions/schema.dart @@ -0,0 +1,38 @@ +import 'package:drift/drift.dart'; + +/// +/// +/// +/// Message Sessions Model (Table) +/// +/// Meant to store all of crypto state in _cold storage_ +/// +class MessageSessions extends Table { + TextColumn get id => text().customConstraint('UNIQUE')(); + + TextColumn get roomId => text()(); + IntColumn get index => integer()(); + TextColumn get identityKey => text().nullable()(); // outbound keys have no identity + TextColumn get session => text()(); + BoolColumn get inbound => boolean()(); + IntColumn get createdAt => integer()(); + + @override + Set get primaryKey => {id}; +} + +/// +/// Key Sessions Model (Table) +/// +/// Meant to store all of crypto state in _cold storage_ +/// +class KeySessions extends Table { + TextColumn get id => text().customConstraint('UNIQUE')(); + + TextColumn get sessionId => text()(); + TextColumn get identityKey => text()(); + TextColumn get session => text().nullable()(); + + @override + Set get primaryKey => {id}; +} diff --git a/lib/store/crypto/sessions/storage.dart b/lib/store/crypto/sessions/storage.dart new file mode 100644 index 000000000..6bf30a42e --- /dev/null +++ b/lib/store/crypto/sessions/storage.dart @@ -0,0 +1,139 @@ +import 'package:drift/drift.dart'; +import 'package:syphon/global/print.dart'; +import 'package:syphon/storage/database.dart'; +import 'package:syphon/store/crypto/sessions/model.dart' as model; + +/// +/// Auth Quesies - unencrypted (Cold Storage) +/// +/// In storage, messages are indexed by eventId +/// In redux, they're indexed by RoomID and placed in a list +/// +extension SessionQueries on StorageDatabase { + Future insertMessageSessions(List sessions) async { + return batch( + (batch) => batch.insertAllOnConflictUpdate( + messageSessions, + sessions, + ), + ); + } + + Future> selectMessageSessionsInbound(List roomIds) { + return (select(messageSessions) + ..where((tbl) => tbl.roomId.isIn(roomIds) & tbl.inbound.equals(true)) + ..orderBy([(tbl) => OrderingTerm(expression: tbl.createdAt, mode: OrderingMode.desc)])) + .get(); + } + + Future> selectMessageSessionsInboundAll() { + return (select(messageSessions) + ..where((tbl) => tbl.inbound.equals(true)) + ..orderBy([(tbl) => OrderingTerm(expression: tbl.createdAt, mode: OrderingMode.desc)])) + .get(); + } +} + +Future saveMessageSessionInbound({ + required String roomId, + required String identityKey, + required String session, + required int messageIndex, + required StorageDatabase storage, +}) async { + return storage.insertMessageSessions([ + MessageSession( + id: session, + roomId: roomId, + session: session, + index: messageIndex, + identityKey: identityKey, + createdAt: DateTime.now().millisecondsSinceEpoch, + inbound: true, + ) + ]); +} + +Future saveMessageSessionsInbound( + Map>> messageSessions, { + required StorageDatabase storage, +}) async { + final List messageSessionsDb = []; + + // prepend session keys to an array per spec + for (final roomSessions in messageSessions.entries) { + final roomId = roomSessions.key; + final sessions = roomSessions.value; + + for (final messsageSessions in sessions.entries) { + final identityKey = messsageSessions.key; + final sessionsSerialized = messsageSessions.value; + + for (final session in sessionsSerialized) { + messageSessionsDb.add( + MessageSession( + id: session.serialized, + index: session.index, + roomId: roomId, + session: session.serialized, + identityKey: identityKey, + createdAt: session.createdAt, + inbound: true, + ), + ); + } + } + } + + return storage.insertMessageSessions(messageSessionsDb); +} + +/// +/// Load Crypto Store (Cold Storage) +/// +/// +Future>>> loadMessageSessionsInbound({ + List? roomIds, + required StorageDatabase storage, +}) async { + try { + final messageSessions = >>{}; + var messageSessionsDb = []; + + if (roomIds == null || roomIds.isEmpty) { + messageSessionsDb = await storage.selectMessageSessionsInboundAll(); + } else { + messageSessionsDb = await storage.selectMessageSessionsInbound(roomIds); + } + + for (final session in messageSessionsDb) { + final roomId = session.roomId; + final senderKey = session.identityKey!; + + final messageSessionNew = model.MessageSession( + index: session.index, + serialized: session.session, // already pickled + createdAt: session.createdAt, + ); + + // new message session updates + messageSessions.update( + roomId, + (identitySessions) => identitySessions + ..update( + senderKey, + (sessions) => sessions..insert(0, messageSessionNew), + ifAbsent: () => [messageSessionNew], + ), + ifAbsent: () => { + senderKey: [messageSessionNew], + }, + ); + } + + return messageSessions; + } catch (error) { + printError(error.toString(), title: 'loadCrypto'); + return {}; + } +} diff --git a/lib/store/crypto/state.dart b/lib/store/crypto/state.dart index 4cb9b817b..79e4d9dd5 100644 --- a/lib/store/crypto/state.dart +++ b/lib/store/crypto/state.dart @@ -21,9 +21,16 @@ class CryptoStore extends Equatable { final bool deviceKeyVerified; final bool oneTimeKeysStable; - // TODO: Map + // Map final Map> keySessions; // both olm inbound and outbound key sessions + // Map // megolm - messages + final Map outboundMessageSessions; + + // Map // megolm - messages per chat + @JsonKey(ignore: true) + final Map>> messageSessionsInbound; + // Map // megolm - index per chat @Deprecated('switch to using "index" inside MessageSession within inboundMessageSessionsAll') final Map> messageSessionIndex; @@ -32,12 +39,6 @@ class CryptoStore extends Equatable { @Deprecated('switch to inboundMessageSessionsAll to include old session for a device') final Map> inboundMessageSessions; - // Map // megolm - messages - final Map outboundMessageSessions; - - // Map // megolm - messages per chat - final Map>> messageSessionsInbound; - /// Map deviceKeys final Map> deviceKeys; diff --git a/lib/store/crypto/storage.dart b/lib/store/crypto/storage.dart index 40cf99087..4ba0d925c 100644 --- a/lib/store/crypto/storage.dart +++ b/lib/store/crypto/storage.dart @@ -41,13 +41,13 @@ Future saveCrypto( } /// -/// Load Auth Store (Cold Storage) +/// Load Crypto Store (Cold Storage) /// Future loadCrypto({required StorageDatabase storage}) async { try { return storage.selectCryptoStore(); } catch (error) { - printError(error.toString(), title: 'loadAuth'); + printError(error.toString(), title: 'loadCrypto'); return null; } } diff --git a/lib/store/sync/actions.dart b/lib/store/sync/actions.dart index a9d8845d1..a1e890b13 100644 --- a/lib/store/sync/actions.dart +++ b/lib/store/sync/actions.dart @@ -388,7 +388,7 @@ ThunkAction syncRooms(Map roomData) { // fetch previous messages since last /sync (a messages gap) // room will be marked limited to indicate this - if (roomSynced.limited) { + if (roomSynced.limited && synced) { printWarning( '[syncRooms] ${roomSynced.name} LIMITED TRUE - Fetching more messages', ); diff --git a/lib/store/sync/parsers.dart b/lib/store/sync/parsers.dart index 4cfdb9363..b95bb41df 100644 --- a/lib/store/sync/parsers.dart +++ b/lib/store/sync/parsers.dart @@ -1,5 +1,6 @@ import 'package:syphon/global/libs/matrix/constants.dart'; import 'package:syphon/global/print.dart'; +import 'package:syphon/global/values.dart'; import 'package:syphon/store/events/messages/model.dart'; import 'package:syphon/store/events/model.dart'; import 'package:syphon/store/events/reactions/model.dart'; @@ -104,7 +105,7 @@ Sync parseSync(Map params) { prevBatch: details.prevBatch, ); - if (details.limited != null) { + if (details.limited != null && DEBUG_MODE) { printInfo( '[parseSync] ${roomExisting.id} limited ${details.limited} lastBatch ${details.lastBatch != null} prevBatch ${details.prevBatch != null}', ); @@ -133,7 +134,7 @@ Sync parseSync(Map params) { ephemerals.readReceipts.forEach((key, value) { if (value.userReadsMapped!.containsKey(currentUser.userId)) { - int rr = value.userReadsMapped![currentUser.userId]; + final int rr = value.userReadsMapped![currentUser.userId]; if (rr > lastRead) { lastRead = rr; diff --git a/lib/views/home/settings/settings-devices-screen.dart b/lib/views/home/settings/settings-devices-screen.dart index 3e245ef4e..55d03d0bb 100644 --- a/lib/views/home/settings/settings-devices-screen.dart +++ b/lib/views/home/settings/settings-devices-screen.dart @@ -5,9 +5,7 @@ import 'package:flutter_redux/flutter_redux.dart'; import 'package:redux/redux.dart'; import 'package:syphon/global/colours.dart'; import 'package:syphon/global/dimensions.dart'; -import 'package:syphon/global/print.dart'; import 'package:syphon/global/strings.dart'; -import 'package:syphon/store/alerts/actions.dart'; import 'package:syphon/store/auth/actions.dart'; import 'package:syphon/store/index.dart'; import 'package:syphon/store/settings/actions.dart'; diff --git a/lib/views/widgets/messages/message.dart b/lib/views/widgets/messages/message.dart index e4f3ace4c..711d18c4f 100644 --- a/lib/views/widgets/messages/message.dart +++ b/lib/views/widgets/messages/message.dart @@ -564,6 +564,7 @@ class MessageWidget extends StatelessWidget { : CrossFadeState.showFirst, firstChild: MarkdownBody( data: body.trim(), + softLineBreak: true, onTapLink: (text, href, title) => onConfirmLink(context, href), styleSheet: MarkdownStyleSheet( diff --git a/pubspec.yaml b/pubspec.yaml index 46322939f..b10ab211b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: syphon description: a privacy focused matrix client -version: 0.2.9+2090 +version: 0.2.10+2100 environment: sdk: ">=2.12.0 <3.0.0"