diff --git a/CHANGES.md b/CHANGES.md index d2e289625..25f3abef4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,39 @@ +## Changes in 1.11.1 (2023-08-29) + +✨ Features + +- New settings cell to manage your account through MAS if the home server allows it. ([#7653](https://github.com/vector-im/element-ios/issues/7653)) + +🙌 Improvements + +- Upgrade MatrixSDK version ([v0.27.1](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.27.1)). + +🐛 Bugfixes + +- Prevent mention crashes when room members are missing display names (objc interop) ([#7649](https://github.com/vector-im/element-ios/pull/7649)) +- Add email UI is hidden if the 3 pid changes capability is disabled. ([#7645](https://github.com/vector-im/element-ios/issues/7645)) +- You can now log out from other sessions using MAS on supported OIDC home servers. ([#7646](https://github.com/vector-im/element-ios/issues/7646)) +- Deactivate account is hidden for servers with OIDC auth. ([#7648](https://github.com/vector-im/element-ios/issues/7648)) +- Prevent pill crashes when room members are missing display names (objc interop) ([#7651](https://github.com/vector-im/element-ios/issues/7651)) + + +## Changes in 1.11.0 (2023-08-15) + +✨ Features + +- Integrate Device Dehydration v2 through the Crypto SDK ([#7630](https://github.com/vector-im/element-ios/pull/7630)) + +🙌 Improvements + +- Upgrade MatrixSDK version ([v0.27.0](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.27.0)). +- Disable Siri, Share and Reply (from NSE) Extensions. ([#7618](https://github.com/vector-im/element-ios/issues/7618)) + +🐛 Bugfixes + +- Fix bug in SSO URL generation that was non-compliant with the spec. ([#7639](https://github.com/vector-im/element-ios/pull/7639)) +- Fix a crash when opening v11 rooms. ([#7633](https://github.com/vector-im/element-ios/issues/7633)) + + ## Changes in 1.10.14 (2023-06-21) 🙌 Improvements diff --git a/CHANGES_BWI.md b/CHANGES_BWI.md index 80278a54c..6dafe3ae9 100644 --- a/CHANGES_BWI.md +++ b/CHANGES_BWI.md @@ -1,3 +1,37 @@ +Changes in BWI project 2.10.0 (2023-09-26) +=================================================== + +Upstream merge ✨: +- v1.11.1 + +Features ✨: +- New WYSIWYG composer (#4941) + +Improvements 🙌: +- UI optimization room overview (#4704, #4806) +- Change date format for poll details (#4798) +- Add additional information to the developer menu for Beta app (#4565) +- Update huddle permissions for new rooms (#4928) +- Update element call permissions when resetting room permissions (#5035) +- Disable all WYSIWYG commands (#4955) +- Change date format for maintenance to ISO-8601 (#5071) +- Update notes room layout in room overview (#4802) +- Enable/disable notes room in settings (#4730) + +Bugfix 🐛: +- Crashfix for dm creation when WYSIWYG composer is enabled (#5133) +- Change permalink URL scheme (#4910) +- No popup for successful password change (#4951) + +Translations 🗣 : +- German translations voice message (#5017) +- German translations wrong password (#4857) +- Add special characters for password creation/resetting (#4952) + +SDK API changes ⚠️: + +Build 🧱: + Changes in BWI project 2.9.1 (2023-09-08) =================================================== diff --git a/Config/AppConfiguration.swift b/Config/AppConfiguration.swift index 2686a6048..4222295bc 100644 --- a/Config/AppConfiguration.swift +++ b/Config/AppConfiguration.swift @@ -55,6 +55,10 @@ class AppConfiguration: CommonConfiguration { // bwi: explicitly set option for key sharing MXSDKOptions.sharedInstance().enableRoomSharedHistoryOnInvite = BWIBuildSettings.shared.allowKeySharingOnRoomInvite + + // bwi: #4941 activate WYSIWYG and deactivate voice broadcast + RiotSettings.shared.enableWysiwygComposer = BWIBuildSettings.shared.enableFeatureWYSIWYGByDefault + RiotSettings.shared.enableVoiceBroadcast = BWIBuildSettings.shared.enableFeatureVoiceBroadcastsByDefault } diff --git a/Config/AppVersion.xcconfig b/Config/AppVersion.xcconfig index 92b5127ef..dcbbb154e 100644 --- a/Config/AppVersion.xcconfig +++ b/Config/AppVersion.xcconfig @@ -16,5 +16,5 @@ // // Version -MARKETING_VERSION = 2.9.1 +MARKETING_VERSION = 2.10.0 CURRENT_PROJECT_VERSION = 20220714163152 diff --git a/Config/BWIBuildSettings.swift b/Config/BWIBuildSettings.swift index 55ec0a76a..7a0669446 100644 --- a/Config/BWIBuildSettings.swift +++ b/Config/BWIBuildSettings.swift @@ -136,7 +136,6 @@ class BWIBuildSettings: NSObject { var bwiAutoCreateAliasOnRoomCreation = true var bwiLocationShareButtonVisible = false - var bwiUseCustomPersonalNotesAvatar = true var bwiBetterIgnoredUsers = true var bwiSettingsShowInAppNotifications = false var bwiFilteredContextMenu = true @@ -271,6 +270,9 @@ class BWIBuildSettings: NSObject { var applicationTermsConditionsUrlString = "" var applicationPrivacyPolicyWithMatomoSectionUrlString = "https://messenger.bwi.de/datenschutz#c6637" + // (#4751) use privacy policy link of well known + var bwiUseWellKnownPrivacyPolicyLink: Bool = false + // MARk: - Matrix permalinks // Paths for URLs that will considered as Matrix permalinks. Those permalinks are opened within the app var permalinkSupportedHosts: [String: [String]] = [:] @@ -509,7 +511,7 @@ class BWIBuildSettings: NSObject { var passwordIndicatorOnLogin = true // MARK: Displays the element base version on the settings screen - var elementBaseVersion = "1.10.14" + var elementBaseVersion = "1.11.1" var showElementBaseVersion = true @@ -530,6 +532,9 @@ class BWIBuildSettings: NSObject { var bwiPersonalNotesRoom = false var bwiPersonalNotesRoomLeavable = false var bwiResetPersonalNotesAccountData = false + var bwiUseCustomPersonalNotesAvatar = true + var bwiPersonalNotesVisibilityInSettings = false + var bwiShowTimelineSettings = false // MARK BWI personal state @@ -653,12 +658,30 @@ class BWIBuildSettings: NSObject { var accessibilityDeclarationFileEn = "" // MARK: Voice Broadcast - var enableLabFeatureVoiceBroadcasts = false + var enableFeatureVoiceBroadcastsByDefault = false // MARK: WYSIWYG - var enableLabFeatureWYSIWYG = false + var enableFeatureWYSIWYGByDefault = true + var enableWYSIWYGCommands = false // MARK: itunes var itunesAppLink = "" + + // MARK: itunes + var showAllChatsFilterMenu = false + + // MARK: itunes + var roomFiltersToggle = true + // MARK: OIDC + var isOIDCEnabled = true + + // MARK: Change Password + var showPasswordChangedConfirmation = true + + // MARK: Create Room Menu + var enableAllChatsToolbar = false + + // MARK: App Config + var avoidServerSelectionOnAppConfig = false } diff --git a/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift b/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift index 023974a5f..bb6112246 100644 --- a/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift +++ b/Config/BuM-Beta/BWIBuildSettings+BuM-Beta.swift @@ -34,13 +34,12 @@ extension BWIBuildSettings { bwiLoginFlowLayout = false useRustEncryption = true bwiNotificationTimes = true - enableLabFeatureVoiceBroadcasts = true enableNewSessionManagerByDefault = true - enableLabFeatureWYSIWYG = true showMaintenanceInfoMessageType = true ignoreBlockingMaintenance = true - + bwiUseWellKnownPrivacyPolicyLink = true + itunesAppLink = "itms://itunes.apple.com/app/bundesmessenger-beta/id1617068656?mt=8" + avoidServerSelectionOnAppConfig = true } - } diff --git a/Config/BuM-Open/BWIBuildSettings+BuM-Open.swift b/Config/BuM-Open/BWIBuildSettings+BuM-Open.swift index 7ff2e64bc..ad899f84f 100644 --- a/Config/BuM-Open/BWIBuildSettings+BuM-Open.swift +++ b/Config/BuM-Open/BWIBuildSettings+BuM-Open.swift @@ -31,6 +31,7 @@ extension BWIBuildSettings { bwiEnableLoginProtection = false itunesAppLink = "itms://itunes.apple.com/app/bundesmessenger-open/id6450672686?mt=8" + avoidServerSelectionOnAppConfig = true } } diff --git a/Config/BuM/BWIBuildSettings+BuM.swift b/Config/BuM/BWIBuildSettings+BuM.swift index 1605a0865..2314716cf 100644 --- a/Config/BuM/BWIBuildSettings+BuM.swift +++ b/Config/BuM/BWIBuildSettings+BuM.swift @@ -27,7 +27,9 @@ extension BWIBuildSettings { authScreenShowTestServerOptions = false bwiNotificationTimes = true enableNewSessionManagerByDefault = true + bwiUseWellKnownPrivacyPolicyLink = true itunesAppLink = "itms://itunes.apple.com/app/bundesmessenger/id1616866351?mt=8" + avoidServerSelectionOnAppConfig = true } } diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 44c008e0b..417da0c49 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -192,7 +192,7 @@ final class BuildSettings: NSObject { #else /// The configuration to use for analytics. Set `isEnabled` to false to disable analytics. static let analyticsConfiguration = AnalyticsConfiguration(isEnabled: BuildSettings.baseBundleIdentifier.starts(with: "im.vector.app"), - host: "https://posthog.hss.element.io", + host: "https://posthog.element.io", apiKey: "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO", termsURL: URL(string: "https://element.io/cookie-policy")!) #endif diff --git a/Config/nexus_upload.sh b/Config/nexus_upload.sh index 376ca71a0..cb997e836 100755 --- a/Config/nexus_upload.sh +++ b/Config/nexus_upload.sh @@ -10,11 +10,25 @@ SUBDIR=$7 echo "params: $NEXUS_BASE_URL $VARIANT $EXT $VERSION $DATE_STR $GITHASH $SUBDIR" echo "dir: $PWD" +check_last_exit_code () { + if [ $1 -ne 0 ]; then + echo "Error: exit code != 0" + exit $1 + fi +} + +echo "checking if file is found" +find ../out$SUBDIR -name "*$VARIANT*.$EXT" | grep . +check_last_exit_code $? +echo "find end" + find ../out$SUBDIR -name "*$VARIANT*.$EXT" -print0 | while read -d $'\0' file do echo "Source: $file" zip -r $file.zip $file BASENAME=$(basename $file .zip)-$VERSION-$DATE_STR-$GITHASH.$EXT$SUBDIR.zip echo "Destination $NEXUS_BASE_URL/$VERSION/$BASENAME" - curl --fail -u $S_BWMESSENGER_ID:$S_BWMESSENGER_PASSWORD -v --upload-file $file.zip $NEXUS_BASE_URL/$VERSION/$BASENAME -done \ No newline at end of file + curl --fail -v -u $S_BWMESSENGER_ID:$S_BWMESSENGER_PASSWORD --upload-file $file.zip $NEXUS_BASE_URL/$VERSION/$BASENAME + check_last_exit_code $? +done +check_last_exit_code $? diff --git a/Podfile b/Podfile index 5ad00a6d9..6ae6b8b40 100644 --- a/Podfile +++ b/Podfile @@ -16,7 +16,7 @@ use_frameworks! # - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI # # Warning: our internal tooling depends on the name of this variable name, so be sure not to change it -$matrixSDKVersion = '= 0.26.12' +$matrixSDKVersion = '= 0.27.1' # $matrixSDKVersion = :local # $matrixSDKVersion = { :branch => 'develop'} # $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } } @@ -43,7 +43,7 @@ when String # specific MatrixSDK released version $matrixSDKVersionSpec = $matrixSDKVersion end -$matrixSDKVersionSpec = { :git => 'https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk', :tag => 'v2.9.0' } +$matrixSDKVersionSpec = { :git => 'https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk', :tag => 'v2.10.0' } # Method to import the MatrixSDK def import_MatrixSDK @@ -178,11 +178,11 @@ abstract_target 'RiotPods' do target "RiotSwiftUI" do import_SwiftUI_pods - end + end target "RiotSwiftUITests" do import_SwiftUI_pods - end + end target "RiotNSE" do import_MatrixSDK @@ -193,6 +193,18 @@ abstract_target 'RiotPods' do import_MatrixSDK end + # Disabled due to crypto corruption issues. + # https://github.com/vector-im/element-ios/issues/7618 + # target "RiotShareExtension" do + # import_MatrixSDK + # import_MatrixKit_pods + # end + # + # target "SiriIntents" do + # import_MatrixSDK + # import_MatrixKit_pods + # end + end post_install do |installer| diff --git a/Podfile.lock b/Podfile.lock index bdbbb2d00..2254e7cdd 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -20,13 +20,31 @@ PODS: - Down (0.11.0) - DSBottomSheet (0.3.0) - DSWaveformImage (6.1.1) + - DTCoreText (1.6.26): + - DTCoreText/Core (= 1.6.26) + - DTFoundation/Core (~> 1.7.5) + - DTFoundation/DTAnimatedGIF (~> 1.7.5) + - DTFoundation/DTHTMLParser (~> 1.7.5) + - DTFoundation/UIKit (~> 1.7.5) + - DTCoreText/Core (1.6.26): + - DTFoundation/Core (~> 1.7.5) + - DTFoundation/DTAnimatedGIF (~> 1.7.5) + - DTFoundation/DTHTMLParser (~> 1.7.5) + - DTFoundation/UIKit (~> 1.7.5) + - DTFoundation/Core (1.7.18) + - DTFoundation/DTAnimatedGIF (1.7.18) + - DTFoundation/DTHTMLParser (1.7.18): + - DTFoundation/Core + - DTFoundation/UIKit (1.7.18): + - DTFoundation/Core + - DTTJailbreakDetection (0.4.0) - FLEX (4.5.0) - FlowCommoniOS (1.12.2) - GBDeviceInfo (7.1.0): - GBDeviceInfo/Core (= 7.1.0) - GBDeviceInfo/Core (7.1.0) - GZIP (1.3.0) - - Introspect (0.1.4) + - Introspect (0.12.0) - JitsiMeetSDKLite (8.1.2-lite): - JitsiWebRTC (~> 111.0) - JitsiWebRTC (111.0.2) @@ -39,26 +57,29 @@ PODS: - LoggerAPI (1.9.200): - Logging (~> 1.1) - Logging (1.4.0) - - MatrixSDK (0.26.12): - - MatrixSDK/Core (= 0.26.12) - - MatrixSDK/Core (0.26.12): + - MatomoTracker (7.5.2): + - MatomoTracker/Core (= 7.5.2) + - MatomoTracker/Core (7.5.2) + - MatrixSDK (0.27.1): + - MatrixSDK/Core (= 0.27.1) + - MatrixSDK/Core (0.27.1): - AFNetworking (~> 4.0.0) - GZIP (~> 1.3.0) - libbase58 (~> 0.1.4) - - MatrixSDKCrypto (= 0.3.4) + - MatrixSDKCrypto (= 0.3.12) - OLMKit (~> 3.2.5) - Realm (= 10.27.0) - SwiftyBeaver (= 1.9.5) - - MatrixSDK/JingleCallStack (0.26.12): + - MatrixSDK/JingleCallStack (0.27.1): - JitsiMeetSDKLite (= 8.1.2-lite) - MatrixSDK/Core - - MatrixSDKCrypto (0.3.4) + - MatrixSDKCrypto (0.3.12) - OLMKit (3.2.12): - OLMKit/olmc (= 3.2.12) - OLMKit/olmcpp (= 3.2.12) - OLMKit/olmc (3.2.12) - OLMKit/olmcpp (3.2.12) - - PostHog (2.0.0) + - PostHog (2.0.2) - ReadMoreTextView (3.0.1) - Realm (10.27.0): - Realm/Headers (= 10.27.0) @@ -73,7 +94,7 @@ PODS: - Sentry/Core (7.15.0) - SideMenu (6.5.0) - SwiftBase32 (0.9.0) - - SwiftFormat/CLI (0.50.2) + - SwiftFormat/CLI (0.52.3) - SwiftGen (6.6.2) - SwiftJWT (3.6.200): - BlueCryptor (~> 1.0) @@ -81,7 +102,7 @@ PODS: - BlueRSA (~> 1.0) - KituraContracts (~> 1.2) - LoggerAPI (~> 1.7) - - SwiftLint (0.49.1) + - SwiftLint (0.52.4) - SwiftyBeaver (1.9.5) - UICollectionViewLeftAlignedLayout (1.0.2) - UICollectionViewRightAlignedLayout (0.0.3) @@ -95,6 +116,8 @@ DEPENDENCIES: - Down (~> 0.11.0) - DSBottomSheet (~> 0.3) - DSWaveformImage (~> 6.1.1) + - DTCoreText (= 1.6.26) + - DTTJailbreakDetection (~> 0.4.0) - FLEX (~> 4.5.0) - FlowCommoniOS (~> 1.12.0) - GBDeviceInfo (~> 7.1.0) @@ -102,8 +125,9 @@ DEPENDENCIES: - KeychainAccess (~> 4.2.2) - KTCenterFlowLayout (~> 1.3.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.26.12) - - MatrixSDK/JingleCallStack (= 0.26.12) + - MatomoTracker (~> 7.5.2) + - MatrixSDK (from `https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk`, tag `v2.10.0_rc2`) + - MatrixSDK/JingleCallStack (from `https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk`, tag `v2.10.0_rc2`) - OLMKit - PostHog (~> 2.0.0) - ReadMoreTextView (~> 3.0.1) @@ -122,7 +146,7 @@ DEPENDENCIES: - ZXingObjC (~> 3.6.5) SPEC REPOS: - trunk: + https://github.com/CocoaPods/Specs.git: - AFNetworking - BlueCryptor - BlueECC @@ -130,6 +154,9 @@ SPEC REPOS: - Down - DSBottomSheet - DSWaveformImage + - DTCoreText + - DTFoundation + - DTTJailbreakDetection - FLEX - FlowCommoniOS - GBDeviceInfo @@ -144,7 +171,7 @@ SPEC REPOS: - libPhoneNumber-iOS - LoggerAPI - Logging - - MatrixSDK + - MatomoTracker - MatrixSDKCrypto - OLMKit - PostHog @@ -165,6 +192,16 @@ SPEC REPOS: - zxcvbn-ios - ZXingObjC +EXTERNAL SOURCES: + MatrixSDK: + :git: https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk + :tag: v2.10.0_rc2 + +CHECKOUT OPTIONS: + MatrixSDK: + :git: https://dl-gitlab.example.com/bwmessenger/bundesmessenger/bundesmessenger-ios-sdk + :tag: v2.10.0_rc2 + SPEC CHECKSUMS: AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58 BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24 @@ -173,11 +210,14 @@ SPEC CHECKSUMS: Down: b6ba1bc985c9d2f4e15e3b293d2207766fa12612 DSBottomSheet: ca0ac37eb5af2dd54663f86b84382ed90a59be2a DSWaveformImage: 3c718a0cf99291887ee70d1d0c18d80101d3d9ce + DTCoreText: ec749e013f2e1f76de5e7c7634642e600a7467ce + DTFoundation: a53f8cda2489208cbc71c648be177f902ee17536 + DTTJailbreakDetection: 5e356c5badc17995f65a83ed9483f787a0057b71 FLEX: e51461dd6f0bfb00643c262acdfea5d5d12c596b FlowCommoniOS: ca92071ab526dc89905495a37844fd7e78d1a7f2 GBDeviceInfo: 5d62fa85bdcce3ed288d83c28789adf1173e4376 GZIP: 416858efbe66b41b206895ac6dfd5493200d95b3 - Introspect: b62c4dd2063072327c21d618ef2bedc3c87bc366 + Introspect: b66b675de8a85d9ef832f3a710d8e3c7db186884 JitsiMeetSDKLite: 895213158cf62342069a10634a41d2f1c00057f7 JitsiWebRTC: 80f62908fcf2a1160e0d14b584323fb6e6be630b KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 @@ -187,20 +227,21 @@ SPEC CHECKSUMS: libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d Logging: beeb016c9c80cf77042d62e83495816847ef108b - MatrixSDK: 0af737bc461b82d0ec9edd6fdf8f70b02771ebd3 - MatrixSDKCrypto: ac805c22c24f79f349cdbfa065855c73a4c81b51 + MatomoTracker: 1d98ddc58322fd9d65e1a6886b8e41363047bd13 + MatrixSDK: f6c197ca06aab29ff69d1105965a57d277dfcd9d + MatrixSDKCrypto: 25929a40733b4ab54f659aaf6a730552a0a06504 OLMKit: da115f16582e47626616874e20f7bb92222c7a51 - PostHog: 660ec6c9d80cec17b685e148f17f6785a88b597d + PostHog: f9e5c13ceea86bb5314218c85d16125b797eb332 ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2 Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136 Sentry: 63ca44f5e0c8cea0ee5a07686b02e56104f41ef7 SideMenu: f583187d21c5b1dd04c72002be544b555a2627a2 SwiftBase32: 9399c25a80666dc66b51e10076bf591e3bbb8f17 - SwiftFormat: 710117321c55c82675c0dc03055128efbb13c38f + SwiftFormat: 5de81c42f043741a16e17ae2da012bbddc7c0b58 SwiftGen: 1366a7f71aeef49954ca5a63ba4bef6b0f24138c SwiftJWT: 88c412708f58c169d431d344c87bc79a87c830ae - SwiftLint: 32ee33ded0636d0905ef6911b2b67bbaeeedafa5 + SwiftLint: 1cc5cd61ba9bacb2194e340aeb47a2a37fda00b3 SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82 UICollectionViewLeftAlignedLayout: 830bf6fa5bab9f9b464f62e3384f9d2e00b3c0f6 UICollectionViewRightAlignedLayout: 823eef8c567eba4a44c21bc2ffcb0d0d5f361e2d @@ -208,6 +249,6 @@ SPEC CHECKSUMS: zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 0e7e10f516d40d9df60cb874170b91603c632118 +PODFILE CHECKSUM: e7f4847564a30ed02659a311fe204021498f30e8 COCOAPODS: 1.11.3 diff --git a/Riot/Assets/Images.xcassets/BWI/button_new_dark.imageset/Button_BuM_Dark.svg b/Riot/Assets/Images.xcassets/BWI/button_new_dark.imageset/Button_BuM_Dark.svg new file mode 100644 index 000000000..0f0658a77 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/button_new_dark.imageset/Button_BuM_Dark.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Riot/Assets/Images.xcassets/BWI/button_new_dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/button_new_dark.imageset/Contents.json new file mode 100644 index 000000000..0ed753627 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/button_new_dark.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Button_BuM_Dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/button_new_light.imageset/Button_BuM_Light.svg b/Riot/Assets/Images.xcassets/BWI/button_new_light.imageset/Button_BuM_Light.svg new file mode 100644 index 000000000..c9f8c0b29 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/button_new_light.imageset/Button_BuM_Light.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Riot/Assets/Images.xcassets/BWI/button_new_light.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/button_new_light.imageset/Contents.json new file mode 100644 index 000000000..b6b5b9fa7 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/button_new_light.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Button_BuM_Light.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/bwi_settings_filled.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/bwi_settings_filled.imageset/Contents.json new file mode 100644 index 000000000..b76053693 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/bwi_settings_filled.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "settings-filled.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/bwi_settings_filled.imageset/settings-filled.svg b/Riot/Assets/Images.xcassets/BWI/bwi_settings_filled.imageset/settings-filled.svg new file mode 100644 index 000000000..82adbb752 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/bwi_settings_filled.imageset/settings-filled.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_off.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_off.imageset/Contents.json new file mode 100644 index 000000000..67ec47a7c --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_off.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Filter-dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_off.imageset/Filter-dark.svg b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_off.imageset/Filter-dark.svg new file mode 100644 index 000000000..c0e66be50 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_off.imageset/Filter-dark.svg @@ -0,0 +1,3 @@ + + + diff --git a/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_on.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_on.imageset/Contents.json new file mode 100644 index 000000000..068ead335 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_on.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Filter-pressed-dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_on.imageset/Filter-pressed-dark.svg b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_on.imageset/Filter-pressed-dark.svg new file mode 100644 index 000000000..37c1861f3 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_dark_on.imageset/Filter-pressed-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_off.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_off.imageset/Contents.json new file mode 100644 index 000000000..aebe7c313 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_off.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Filter-light.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_off.imageset/Filter-light.svg b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_off.imageset/Filter-light.svg new file mode 100644 index 000000000..bd03bf038 --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_off.imageset/Filter-light.svg @@ -0,0 +1,3 @@ + + + diff --git a/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_on.imageset/Contents.json b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_on.imageset/Contents.json new file mode 100644 index 000000000..2f3d3968c --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_on.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Filter-pressed-light.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_on.imageset/Filter-pressed-light.svg b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_on.imageset/Filter-pressed-light.svg new file mode 100644 index 000000000..7ec32495e --- /dev/null +++ b/Riot/Assets/Images.xcassets/BWI/room_filter_toggle_light_on.imageset/Filter-pressed-light.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/BWI-noticeboard.png b/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/BWI-noticeboard.png deleted file mode 100644 index 7d993a44d..000000000 Binary files a/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/BWI-noticeboard.png and /dev/null differ diff --git a/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/Contents.json b/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/Contents.json index 4c8d4000f..a54b8e9bc 100644 --- a/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/Contents.json @@ -9,7 +9,7 @@ "scale" : "2x" }, { - "filename" : "BWI-noticeboard.png", + "filename" : "Notizen-Avatar.svg", "idiom" : "universal", "scale" : "3x" } diff --git a/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/Notizen-Avatar.svg b/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/Notizen-Avatar.svg new file mode 100644 index 000000000..7cdaae305 --- /dev/null +++ b/Riot/Assets/Images.xcassets/PersonalNotes/personal_notes_avatar.imageset/Notizen-Avatar.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Riot/Assets/de.lproj/Bwi.strings b/Riot/Assets/de.lproj/Bwi.strings index 15b7d1b85..42fa341ff 100644 --- a/Riot/Assets/de.lproj/Bwi.strings +++ b/Riot/Assets/de.lproj/Bwi.strings @@ -52,6 +52,16 @@ "bwi_settings_developer_restrict_user" = "Nutzer einschränken"; "bwi_settings_developer_unrestrict_user" = "Nutzereinschränkung aufheben"; "bwi_settings_developer_unmark_banner" = "Banner als nicht gelesen markieren"; +"bwi_settings_developer_well_known" = "Well-Known"; +"bwi_settings_developer_capabilities" = "Capabilities"; +"bwi_settings_developer_maintenance" = "Maintenance"; +"bwi_settings_developer_user_account_data" = "Account Data"; +"bwi_settings_developer_user_account_data_type" = "Type"; +"bwi_settings_developer_key_backup" = "Key Backup"; +"bwi_settings_developer_key_backup_algorithm" = "Algorithmus"; +"bwi_settings_developer_key_backup_count" = "Gespeicherte Schlüssel"; +"bwi_settings_developer_key_backup_etag" = "ETag"; +"bwi_settings_developer_key_backup_version" = "Version"; // MARK: - MDM @@ -198,7 +208,7 @@ "settings_old_password" = "Aktuelles Passwort"; "settings_new_password" = "Neues Passwort"; "settings_confirm_password" = "Neues Passwort bestätigen"; -"settings_password_updated" = "Dein Passwort wurde aktualisiert"; +"settings_password_changed" = "Dein Passwort wurde erfolgreich geändert."; "settings_password_condition" = "Regeln: Mindestlänge 8 Zeichen, mind. 1 Sonderzeichen, 1 Groß- und 1 Kleinbuchstaben und 1 Ziffer"; "settings_password_too_short_message" = "Das Passwort muss aus mindestens 8 Zeichen bestehen"; "settings_password_has_no_digit" = "Das Passwort muss mindestens eine Ziffer enthalten"; @@ -407,7 +417,7 @@ "voice_message_remaining_recording_time" = "noch %@s"; "voice_message_stop_locked_mode_recording" = "Klick die Nachricht um zu stoppen oder zu hören"; "voice_message_lock_screen_placeholder" = "Sprachnachricht"; -"voice_message_slide_to_cancel" = "Sliden zum Abbrechen"; +"voice_message_slide_to_cancel" = "Wischen zum Abbrechen"; // MARK: - Developer Menu @@ -429,6 +439,7 @@ "bwi_settings_enable_notes_room_title" = "Zeige meine Notizen"; "bwi_settings_enable_notes_room_summary" = "Einen Raum für private Notizen anzeigen"; "bwi_notes_room_title" = "Meine Notizen"; +"home_context_menu_personal_notes" = "Notizen ausblenden"; // MARK: - Personal State @@ -505,6 +516,7 @@ "bwi_error_invite_banned_in_room" = "%@ ist vom Raum gebannt."; "bwi_error_invite_general" = "%@ konnte nicht eingeladen werden."; "bwi_error_logout_offline" = "Abmelden ist ohne Internetverbindung nicht möglich."; +"bwi_error_invalid_password" = "Falsches Passwort"; // MARK: - Matomo @@ -523,7 +535,7 @@ "bwi_settings_new_features_show_features" = "Neue Funktionen anzeigen"; "bwi_feature_banner_header" = "Neue Funktionen"; "bwi_feature_banner_show_more_button" = "Erfahre mehr"; -"bwi_feature_banner_advertisement_text" = "Mit dem neuen Feature Ruhezeiteneinstellung lassen sich Benachrichtigungen nach Wochentag und Uhrzeit ein- bzw stummschalten."; +"bwi_feature_banner_advertisement_text" = "Beim Schreiben von Nachrichten kannst Du jetzt sehr einfach Textformatierungen verwenden."; // MARK: - Onboarding "onboarding_splash_login_button_title" = "Loslegen"; @@ -548,6 +560,8 @@ "poll_timeline_show_participants_button" = "Stimmen ansehen"; "poll_participant_details_show_more" = "Alle ansehen (%lu weitere)"; "poll_participant_details_title" = "Umfragedetails"; +"poll_participant_details_clock_string" = " Uhr"; +"poll_participant_details_date_string_today" = "Heute, "; // MARK: - Welcome Experience "welcome_experience_title1" = "Willkommen beim BundesMessenger"; @@ -584,6 +598,7 @@ // MARK: - New Layout "all_chats_edit_layout_show_personal_notes" = "Notizenraum anzeigen"; +"all_chats_searchbar_prompt" = "Suche"; // MARK: - Permalink Scan "room_recents_scan_qr_code" = "QR Code scannen"; diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index d6a80382a..578b85782 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -406,8 +406,8 @@ "settings_ui_language" = "Sprache"; // Events formatter "event_formatter_member_updates" = "%tu Änderungen der Mitgliedschaft"; -"contacts_user_directory_section" = "NUTZER VERZEICHNIS"; -"contacts_user_directory_offline_section" = "NUTZER VERZEICHNIS (offline)"; +"contacts_user_directory_section" = "NUTZERVERZEICHNIS"; +"contacts_user_directory_offline_section" = "NUTZERVERZEICHNIS (offline)"; "auth_home_server_placeholder" = "URL (z.B. https://matrix.org)"; "auth_identity_server_placeholder" = "URL (z. B. https://vector.im)"; "room_ongoing_conference_call_close" = "Schließen"; @@ -2396,12 +2396,12 @@ "room_suggestion_settings_screen_nav_title" = "Raum vorschlagen"; "room_access_space_chooser_other_spaces_section_info" = "Diese sind vermutlich Dinge, zu denen andere Admins von %@ gehören."; "room_access_space_chooser_other_spaces_section" = "Andere Spaces oder Räume"; -"room_access_settings_screen_setting_room_access" = "Lege Raumzugriff fest"; -"room_access_settings_screen_upgrade_alert_upgrading" = "Raum upgraden"; +"room_access_settings_screen_setting_room_access" = "Raumzutritt festlegen"; +"room_access_settings_screen_upgrade_alert_upgrading" = "Raum aktualisieren"; "room_access_settings_screen_upgrade_alert_upgrade_button" = "Aktualisieren"; "room_access_settings_screen_upgrade_alert_auto_invite_switch" = "Mitglieder automatisch zu neuem Raum einladen"; -"room_access_settings_screen_upgrade_alert_message" = "Jeder in %@ kann diesen Raum finden und ihm beitreten - jeden manuell einzuladen ist nicht nötig. Du kannst diese Einstellung jederzeit ändern."; -"room_access_settings_screen_upgrade_alert_title" = "Raum upgraden"; +"room_access_settings_screen_upgrade_alert_message" = "Jeder in %@ kann diesen Raum finden und ihm beitreten – jeden manuell einzuladen ist nicht nötig. Du kannst diese Einstellung jederzeit ändern."; +"room_access_settings_screen_upgrade_alert_title" = "Raum aktualisieren"; "room_access_settings_screen_edit_spaces" = "Spaces bearbeiten"; "room_access_settings_screen_upgrade_required" = "Upgrade erforderlich"; "room_access_settings_screen_message" = "Lege fest, wer %@ finden und beitreten kann."; @@ -2452,7 +2452,7 @@ // Mark: - All Chats -"all_chats_title" = "Alle Unterhaltungen"; +"all_chats_title" = "Unterhaltungen"; "all_chats_empty_view_title" = "%@\nsieht ein bisschen leer aus."; "all_chats_all_filter" = "Alle"; "all_chats_edit_layout_show_filters" = "Filter anzeigen"; @@ -2461,13 +2461,13 @@ "threads_discourage_information_1" = "Dein Heimserver unterstützt aktuell keine Threads, weshalb diese Funktion unzuverlässig sein könnte. Manche Thread-Nachrichten könnten nicht zuverlässig verfügbar sein. "; "all_chats_nothing_found_placeholder_title" = "Nichts gefunden."; "spaces_create_subspace_title" = "Sub-Space erstellen"; -"room_access_settings_screen_upgrade_alert_note" = "Bitte beachte, dass das Upgrade eine neue Version dieses Raums erstellt. Alle aktuellen Nachrichten bleiben in diesem archivierten Raum."; +"room_access_settings_screen_upgrade_alert_note" = "Bitte beachte, dass die Aktualisierung eine neue Version dieses Raums erstellt. Alle aktuellen Nachrichten bleiben in diesem archivierten Raum."; "invite_to" = "Zu %@ einladen"; "all_chats_empty_unreads_placeholder_message" = "Hier werden deine ungelesenen Nachrichten erscheinen, wenn du welche hast."; "all_chats_edit_layout_show_recents" = "Historie anzeigen"; "all_chats_empty_list_placeholder_title" = "Du bist auf dem neuesten Stand."; "spaces_explore_rooms_format" = "%@ erkunden"; -"room_access_settings_screen_upgrade_alert_message_no_param" = "Jeder in einem übergeordneten Space kann diesen Raum finden und ihm beitreten - jeden manuell einzuladen ist nicht nötig. Du kannst diese Einstellung jederzeit ändern."; +"room_access_settings_screen_upgrade_alert_message_no_param" = "Jeder in einem übergeordneten Space kann diesen Raum finden und ihm beitreten – jeden manuell einzuladen ist nicht nötig. Du kannst diese Einstellung jederzeit ändern."; "room_access_settings_screen_public_message" = "Sichtbar und zugänglich für jeden."; "room_access_settings_screen_restricted_message" = "Sichtbar und betretbar für jeden Nutzer in einem Space.\nDu wählst, für welche Spaces dies gilt."; "room_access_settings_screen_private_message" = "Nur sichtbar und betretbar für eingeladene Personen."; @@ -2771,3 +2771,8 @@ "notice_display_name_changed_to" = "%@ hat den Anzeigenamen zu %@ geändert"; "poll_timeline_loading" = "Lade …"; "room_command_discard_session_description" = "Erzwingt das Verferfen der aktuell ausgehende Gruppensitzung in einem verschlüsseltem Raum"; +"room_creation_user_not_found_prompt_title" = "Bestätigung"; +"room_creation_user_not_found_prompt_message" = "Wir konnten kein Profil für diese Matrix-ID finden. Möchtest du dennoch eine Direktnachricht beginnen?"; +"room_creation_user_not_found_prompt_invite_action" = "Dennoch DM beginnen"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Wir konnten kein Profil für diese Matrix-ID finden. Möchtest du wirklich %@ nach %@ einladen?"; +"room_participants_invite_anyway" = "Dennoch einladen"; diff --git a/Riot/Assets/en.lproj/Bwi.strings b/Riot/Assets/en.lproj/Bwi.strings index 828c1d18e..6267f0522 100644 --- a/Riot/Assets/en.lproj/Bwi.strings +++ b/Riot/Assets/en.lproj/Bwi.strings @@ -53,6 +53,16 @@ "bwi_settings_developer_restrict_user" = "Restrict user"; "bwi_settings_developer_unrestrict_user" = "Remove user restriction"; "bwi_settings_developer_unmark_banner" = "Unmark Feature Banner"; +"bwi_settings_developer_well_known" = "Well-Known"; +"bwi_settings_developer_capabilities" = "Capabilities"; +"bwi_settings_developer_maintenance" = "Maintenance"; +"bwi_settings_developer_user_account_data" = "Account Data"; +"bwi_settings_developer_user_account_data_type" = "Type"; +"bwi_settings_developer_key_backup" = "Key Backup"; +"bwi_settings_developer_key_backup_algorithm" = "Algorithm"; +"bwi_settings_developer_key_backup_count" = "Stored Keys"; +"bwi_settings_developer_key_backup_etag" = "ETag"; +"bwi_settings_developer_key_backup_version" = "Version"; // MARK: - MDM @@ -154,7 +164,7 @@ "settings_new_password" = "new password"; "settings_confirm_password" = "confirm password"; "settings_fail_to_update_password" = "Fail to update password"; -"settings_password_updated" = "Your password has been updated"; +"settings_password_changed" = "Your password has been changed."; "settings_password_condition" = "Rules: Minimum length 8 characters, at least 1 special character, 1 uppercase and 1 lowercase letter and 1 number"; "settings_password_too_short_message" = "The password must be at least 8 characters long"; "settings_password_has_no_digit" = "The password must include at least one digit"; @@ -342,6 +352,7 @@ "bwi_settings_enable_notes_room_title" = "My notes"; "bwi_settings_enable_notes_room_summary" = "how personal notes room."; "bwi_notes_room_title" = "My notes"; +"home_context_menu_personal_notes" = "Hide notes"; // MARK: - Personal State @@ -414,6 +425,7 @@ "bwi_error_invite_banned_in_room" = "%@ is banned from the room."; "bwi_error_invite_general" = "%@ could not be invited."; "bwi_error_logout_offline" = "Logout not possible without internet connection."; +"bwi_error_invalid_password" = "Invalid password"; // MARK: - Matomo @@ -432,7 +444,7 @@ "bwi_settings_new_features_show_features" = "Show new features"; "bwi_feature_banner_header" = "New Features"; "bwi_feature_banner_show_more_button" = "Learn more"; -"bwi_feature_banner_advertisement_text" = "A new feature for notifications times allows you to define time intervals for filtering incoming push notifications."; +"bwi_feature_banner_advertisement_text" = "You can now easily use text formatting when writing messages."; // MARK: - Onboarding "onboarding_splash_login_button_title" = "Let's go"; @@ -453,6 +465,8 @@ "poll_timeline_show_participants_button" = "Show votes"; "poll_participant_details_show_more" = "Show all (%lu more)"; "poll_participant_details_title" = "Poll details"; +"poll_participant_details_clock_string" = ""; +"poll_participant_details_date_string_today" = "Today, "; // MARK: - Welcome Experience "welcome_experience_title1" = "Welcome to BundesMessenger"; @@ -494,6 +508,7 @@ // MARK: - New Layout "all_chats_edit_layout_show_personal_notes" = "Show personal notes"; +"all_chats_searchbar_prompt" = "Suche"; // MARK: - Permalink Scan "room_recents_scan_qr_code" = "Scan QR Code"; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index e22420771..432f8ed23 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -748,6 +748,9 @@ Tap the + to start adding people."; "settings_three_pids_management_information_part1" = "Manage which email addresses or phone numbers you can use to log in or recover your account here. Control who can find you in "; "settings_three_pids_management_information_part2" = "Discovery"; "settings_three_pids_management_information_part3" = "."; +"settings_manage_account_title" = "Account"; +"settings_manage_account_action" = "Manage account"; +"settings_manage_account_description" = "Manage your account at %@"; "settings_confirm_media_size" = "Confirm size when sending"; "settings_confirm_media_size_description" = "When this is on, you’ll be asked to confirm what size images and videos will be sent as."; @@ -964,6 +967,8 @@ Tap the + to start adding people."; "manage_session_trusted" = "Trusted by you"; "manage_session_not_trusted" = "Not trusted"; "manage_session_sign_out" = "Sign out of this session"; +"manage_session_redirect" = "You will be redirected to your server's authentication provider to complete sign out."; +"manage_session_redirect_error" = "Functionality currently unavailable. Please contact your homeserver admin"; "manage_session_rename" = "Rename session"; "manage_session_sign_out_other_sessions" = "Sign out of all other sessions"; // User sessions management @@ -2279,7 +2284,7 @@ Tap the + to start adding people."; // MARK: - All Chats -"all_chats_title" = "All chats"; +"all_chats_title" = "Chats"; "all_chats_section_title" = "Chats"; "all_chats_edit_layout" = "Layout preferences"; "all_chats_edit_layout_recents" = "Recents"; diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 2e91114ed..a09965843 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -1968,7 +1968,7 @@ "notice_encrypted_message" = "Ĉifrita mesaĝo"; "notice_room_related_groups" = "Grupoj rilataj al ĉi tiu ĉambro estas: %@"; "notice_room_aliases_for_dm" = "La kromnomoj estas: %@"; -"notice_room_aliases" = "Kromnomoj de la ĉamrbo estas: %@"; +"notice_room_aliases" = "Kromnomoj de la ĉambro estas: %@"; "notice_room_power_level_event_requirement" = "La minimumaj povniveloj rilataj al okazoj estas:"; "notice_room_power_level_acting_requirement" = "La minimuma povnivelo, kiun uzanto bezonas antaŭ agi, estas:"; "notice_room_power_level_intro_for_dm" = "La povniveloj de ĉambranoj estas:"; diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 21f95be98..0cad547d7 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2709,3 +2709,8 @@ "room_command_change_display_name_description" = "Muudab sinu kuvatavat nime"; "notice_display_name_changed_to" = "%@ muutis oma kuvatavaks nimeks %@"; "poll_timeline_loading" = "Laadin..."; +"room_creation_user_not_found_prompt_title" = "Kinnitus"; +"room_creation_user_not_found_prompt_invite_action" = "Ikkagi alusta vestlust"; +"room_creation_user_not_found_prompt_message" = "Sellele Matrix'i kasutajatunnuse profiili ei leidu. Kas sa ikkagi tahaksid temaga vestlust alustada?"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Sellele Matrix'i kasutajatunnuse profiili ei leidu. Kas sa ikkagi tahaksid saata kutset kasutajale %@ jututuppa %@?"; +"room_participants_invite_anyway" = "Kutsu siiski"; diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 81f489410..d3c3cf5c5 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -274,7 +274,7 @@ "room_title_members" = "%@ membri"; "room_title_one_member" = "1 membro"; // Room Preview -"room_preview_invitation_format" = "Sei stato invitato ad entrare in questa stanza da %@"; +"room_preview_invitation_format" = "Sei stato/a invitato/a ad entrare in questa stanza da %@"; "room_preview_subtitle" = "Questa è l'anteprima della stanza. Le interazioni sono disabilitate."; "room_preview_unlinked_email_warning" = "Questo invito è stato spedito a %@, che non è associato a questo account. Puoi aggiungere questa email al tuo account o provare ad accedere con un account differente."; "room_preview_try_join_an_unknown_room" = "Stai provando ad accedere a %@. Desideri entrare per partecipare alla discussione?"; @@ -457,7 +457,7 @@ "group_home_multi_members_format" = "%tu membri"; "group_home_one_room_format" = "1 stanza"; "group_home_multi_rooms_format" = "%tu stanze"; -"group_invitation_format" = "%@ ti ha invitato ad unirti a questa comunità"; +"group_invitation_format" = "%@ ti ha invitato/a ad unirti a questa comunità"; // Group participants "group_participants_add_participant" = "Aggiungi membri"; "group_participants_leave_prompt_title" = "Lascia la stanza"; @@ -649,7 +649,7 @@ "sign_out_key_backup_in_progress_alert_cancel_action" = "Attendo"; "close" = "Chiudi"; "auth_forgot_password_error_no_configured_identity_server" = "Non è stato configurato alcun server d'identità: aggiungine uno per ripristinare la password dell'account Matrix."; -"auth_softlogout_signed_out" = "Sei uscito"; +"auth_softlogout_signed_out" = "Sei disconnesso/a"; "auth_softlogout_sign_in" = "Accedi"; "auth_softlogout_reason" = "L'amministratore dell'Home Server (%1$@) ti ha disconnesso dal tuo account %2$@ (%3$@)."; "auth_softlogout_recover_encryption_keys" = "Accedi per recuperare le chiavi crittografiche archiviate su questo dispositivo. Le chiavi ti servono per poter leggere i tuoi messaggi cifrati su altri dispositivi."; @@ -1164,7 +1164,7 @@ "biometrics_cant_unlocked_alert_message_login" = "Riaccedi"; "biometrics_cant_unlocked_alert_message_retry" = "Riprova"; "device_verification_self_verify_wait_recover_secrets_checking_availability" = "Controllo di altre possibilità di verifica..."; -"joined" = "Entrato"; +"joined" = "Entrato/a"; "switch" = "Cambia"; "more" = "Altro"; "pin_protection_choose_pin_welcome_after_login" = "Bentornato/a."; @@ -1832,7 +1832,7 @@ "room_error_cannot_load_timeline" = "Caricamento storico dei messaggi fallito"; "room_error_timeline_event_not_found_title" = "Caricamento della posizione nello storico fallito"; "room_error_timeline_event_not_found" = "L'applicazione ha cercato di caricare un punto specifico dello storico dei messaggi in questo canale, ma non è riuscita a trovarlo"; -"room_left" = "Sei uscito dalla stanza"; +"room_left" = "Sei uscito/a dalla stanza"; "room_no_power_to_create_conference_call" = "Hai bisogno del permesso per invitare a iniziare una conferenza in questo canale"; "room_no_conference_call_in_encrypted_rooms" = "Le chiamate in conferenza non sono supportate nei canali criptati"; // Reply to message @@ -1906,7 +1906,7 @@ // Language picker "language_picker_title" = "Scegli una lingua"; "language_picker_default_language" = "Predefinito (%@)"; -"notice_room_invite" = "%@ invitato %@"; +"notice_room_invite" = "%@ ha invitato %@"; "notice_room_third_party_invite" = "%@ ha invitato %@ a unirsi al canale"; "notice_room_third_party_registered_invite" = "%@ ha accettato l'invito per %@"; "notice_room_third_party_revoked_invite" = "%@ ha ritirato l'invito per %@ a unirsi al canale"; @@ -1944,7 +1944,7 @@ "login" = "Entra"; "create_account" = "Crea utente"; "membership_invite" = "Invitati"; -"membership_leave" = "Uscito"; +"membership_leave" = "Uscito/a"; "membership_ban" = "Espulso"; "num_members_one" = "%@ utente"; "num_members_other" = "%@ utenti"; @@ -1979,7 +1979,7 @@ "notification_settings_contain_my_user_name" = "Notifica con un suono i messaggi che contengono il mio nome utente"; "notification_settings_contain_my_display_name" = "Notifica con un suono i messaggi che contengono il mio nome completo"; "notification_settings_just_sent_to_me" = "Notifica con un suono i messaggi inviati solo a me"; -"notification_settings_invite_to_a_new_room" = "Notifica quando sono invitato in un nuovo canale"; +"notification_settings_invite_to_a_new_room" = "Avvisami quando sono invitato/a in una nuova stanza"; "notification_settings_people_join_leave_rooms" = "Notifica quando gli utenti entrano o escono dai canali"; "notification_settings_receive_a_call" = "Notifica quando ricevo una chiamata"; "notification_settings_suppress_from_bots" = "Sopprimi le notifiche dai bot"; @@ -2009,12 +2009,12 @@ "notice_encryption_enabled_unknown_algorithm" = "%1$@ ha attivato la crittografia end-to-end (algoritmo %2$@ non riconosciuto)."; // Notice Events with "You" "notice_room_invite_by_you" = "Hai invitato %@"; -"notice_room_invite_you" = "%@ ti ha invitato"; +"notice_room_invite_you" = "%@ ti ha invitato/a"; "notice_room_third_party_invite_by_you" = "Hai mandato un invito a %@ a unirsi alla stanza"; "notice_room_third_party_registered_invite_by_you" = "Hai accettato l'invito per %@"; "notice_room_third_party_revoked_invite_by_you" = "Hai revocato l'invito per %@ a unirsi alla stanza"; -"notice_room_join_by_you" = "Sei entrato"; -"notice_room_leave_by_you" = "Sei uscito"; +"notice_room_join_by_you" = "Sei entrato/a"; +"notice_room_leave_by_you" = "Sei uscito/a"; "notice_room_reject_by_you" = "Hai rifiutato l'invito"; "notice_room_kick_by_you" = "Hai rimosso %@"; "notice_room_unban_by_you" = "Hai riammesso %@"; @@ -2049,7 +2049,7 @@ "notice_room_join_rule_public" = "%@ ha reso la stanza pubblica."; "notice_room_join_rule_public_by_you" = "Hai reso la stanza pubblica."; "notice_room_name_removed_for_dm" = "%@ ha rimosso il nome"; -"notice_room_created_for_dm" = "%@ è entrato."; +"notice_room_created_for_dm" = "%@ è entrato/a."; "notice_room_join_rule_invite_for_dm" = "%@ l'ha resa solo su invito."; "notice_room_join_rule_invite_by_you_for_dm" = "L'hai resa solo su invito."; "notice_room_join_rule_public_for_dm" = "%@ l'ha resa pubblica."; @@ -2059,7 +2059,7 @@ "notice_room_history_visible_to_members_for_dm" = "%@ ha reso visibili i messaggi futuri a tutti i membri della stanza."; "notice_room_history_visible_to_members_from_invited_point_for_dm" = "%@ ha reso visibili i messaggi futuri a chiunque, dal momento dell'invito."; "notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@ ha reso visibili i messaggi futuri a chiunque, dal momento dell'entrata."; -"room_left_for_dm" = "Sei uscito"; +"room_left_for_dm" = "Sei uscito/a"; "notice_room_third_party_invite_for_dm" = "%@ ha invitato %@"; "notice_room_third_party_revoked_invite_for_dm" = "%@ ha revocato l'invito per %@"; "notice_room_name_changed_for_dm" = "%@ ha cambiato il nome in %@."; @@ -2067,7 +2067,7 @@ "notice_room_third_party_revoked_invite_by_you_for_dm" = "Hai revocato l'invito per %@"; "notice_room_name_changed_by_you_for_dm" = "Hai cambiato il nome in %@."; "notice_room_name_removed_by_you_for_dm" = "Hai rimosso il nome"; -"notice_room_created_by_you_for_dm" = "Sei entrato."; +"notice_room_created_by_you_for_dm" = "Sei entrato/a."; "notice_room_history_visible_to_members_by_you_for_dm" = "Hai reso visibili i messaggi futuri a tutti i membri della stanza."; "notice_room_history_visible_to_members_from_invited_point_by_you_for_dm" = "Hai reso visibili i messaggi futuri a chiunque, dal momento dell'invito."; "notice_room_history_visible_to_members_from_joined_point_by_you_for_dm" = "Hai reso visibili i messaggi futuri a chiunque, dal momento dell'entrata."; @@ -2096,7 +2096,7 @@ "attachment_small_with_resolution" = "Piccolo %@ (~%@)"; "attachment_size_prompt_message" = "Puoi disattivarlo nelle impostazioni."; "attachment_size_prompt_title" = "Conferma dimensione da inviare"; -"room_displayname_all_other_members_left" = "%@ (Uscito)"; +"room_displayname_all_other_members_left" = "%@ (Uscito/a)"; "attachment_unsupported_preview_message" = "Questo tipo di file non è supportato."; "attachment_unsupported_preview_title" = "Anteprima non disponibile"; "message_reply_to_sender_sent_their_location" = "ha condiviso la sua posizione."; @@ -2273,7 +2273,7 @@ "location_sharing_live_list_item_last_update_invalid" = "Ultimo aggiornamento sconosciuto"; "location_sharing_live_list_item_last_update" = "Aggiornato %@ fa"; "location_sharing_live_list_item_sharing_expired" = "Condivisione scaduta"; -"location_sharing_live_list_item_time_left" = "%@ è uscito"; +"location_sharing_live_list_item_time_left" = "%@ è uscito/a"; "location_sharing_live_viewer_title" = "Posizione"; "location_sharing_live_map_callout_title" = "Condividi posizione"; "bug_report_logs_description" = "Per diagnosticare i problemi, i registri di questo client verranno inviati con questo rapporto di errore. Se preferisci inviare solo il testo soprastante, deseleziona:"; @@ -2737,3 +2737,8 @@ "room_command_change_display_name_description" = "Cambia il tuo nome visualizzato"; "notice_display_name_changed_to" = "%@ ha cambiato il suo nome visualizzato in %@"; "poll_timeline_loading" = "Caricamento..."; +"room_creation_user_not_found_prompt_title" = "Conferma"; +"room_creation_user_not_found_prompt_invite_action" = "Inizia il messaggio lo stesso"; +"room_participants_invite_anyway" = "Invita lo stesso"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Impossibile trovare profili con questo ID Matrix. Vuoi davvero invitare %@ in %@?"; +"room_creation_user_not_found_prompt_message" = "Impossibile trovare profili con questo ID Matrix. Vuoi comunque iniziare un messaggio diretto?"; diff --git a/Riot/Assets/new_features.html b/Riot/Assets/new_features.html index a379e5e7d..91e227f30 100644 --- a/Riot/Assets/new_features.html +++ b/Riot/Assets/new_features.html @@ -26,6 +26,34 @@ +
+

+ Version 2.10.0 +

+ +

+ Neue Funktionen +

+

+ +

+ Verbesserungen +

+

+ +

+ Behobene Bugs +

+

+

Version 2.9.1 diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index 2f016c5fd..3e96f0ad3 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -2265,3 +2265,91 @@ // Room Access Settings "room_access_settings_screen_nav_title" = "Доступ к комнате"; "spaces_coming_soon_detail" = "Эта функция еще не реализована здесь, но она в разработке. На данный момент вы можете сделать это с помощью %@ на своем компьютере."; + +// Unverified sessions +"key_verification_alert_title" = "У вас есть не подтвержденные сессии"; +"key_verification_scan_qr_code_title" = "Сканируйте QR-код"; +"home_context_menu_favourite" = "Любимые"; +"spaces_add_space_title" = "Создать пространство"; +"room_details_promote_room_title" = "Продвигать комнату"; +"room_suggestion_settings_screen_title" = "Сделать комнату предлагаемой в пространстве"; +"analytics_prompt_terms_link_new_user" = "здесь"; +"create_room_suggest_room" = "Предложения для пользователей пространства"; +"room_info_back_button_title" = "Информация о комнате"; +"home_context_menu_make_dm" = "Перейти к людям"; +"create_room_processing" = "Создание комнаты"; +"home_context_menu_unmute" = "Снять заглушку"; + +// MARK: - Room invite + +"room_invite_to_space_option_title" = "К %@"; +"analytics_prompt_yes" = "Да, все классно"; +"create_room_section_footer_type_private" = "Только приглашенные люди могут искать и вступать."; +"home_context_menu_make_room" = "Перейти к комнатам"; +"create_room_suggest_room_footer" = "Предлагаемые комнаты выдвинуты для участников как лучший выбор для вступления."; +"home_context_menu_notifications" = "Уведомления"; +"analytics_prompt_terms_link_upgrade" = "здесь"; +"device_verification_self_verify_open_on_other_device_title" = "Откройте %@ на другом вашем устройстве"; +"create_room_section_footer_type_public" = "Только приглашенные люди могут искать и вступать, не только люди из пространства."; +"home_context_menu_unfavourite" = "Удалить из любимых"; +"home_context_menu_normal_priority" = "Нормальный приоритет"; +"home_context_menu_mute" = "Заглушить"; +"room_access_settings_screen_upgrade_alert_upgrading" = "Улучшение комнаты"; +"room_access_settings_screen_upgrade_alert_upgrade_button" = "Улучшить"; +"key_backup_recover_from_private_key_progress" = "%@%% закончено"; +"room_access_settings_screen_upgrade_alert_title" = "Улучшить комнату"; +"share_extension_low_quality_video_message" = "Отправьте в %@ для лучшего качества или отправьте в пониженном качестве."; +"settings_acceptable_use" = "Принимаемые политики пользования"; +"room_suggestion_settings_screen_message" = "Предлагаемые комнаты рекламируются среди участников пространства как подходящие для присоединения."; +/* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ +"analytics_prompt_terms_new_user" = "Вы можете прочитать все наши условия %@."; +"key_verification_alert_body" = "Перепроверьте надежность сохранности аккаунта."; + +// MARK: - Launch loading + +"launch_loading_generic" = "Синхронизация ваших разговоров"; +"home_context_menu_mark_as_unread" = "Пометить как непрочитанное"; + +// MARK: Sign out warning + +"sign_out" = "Выйти"; +"room_command_unban_user_description" = "Разблокирует пользователя с указанным ID"; +"device_verification_self_verify_open_on_other_device_information" = "Вам нужно подтвердить эту сессию для того, чтобы прочитать защищенную историю сообщений\n\nОткройте Element на одном из ваших прочих устройств и действуйте по инструкции."; +"room_command_change_room_topic_description" = "Указывает тему комнаты"; +"room_access_settings_screen_upgrade_required" = "Требуется улучшение"; +"key_verification_scan_qr_code_information_other_user" = "Наведите камеру на QR-код, отображенном на вашем устройстве для подтверждения ваших сессий"; + +// Room suggestion Settings +"room_suggestion_settings_screen_nav_title" = "Предложить комнату"; +/* Note: The placeholder is for the contents of analytics_prompt_terms_link_upgrade */ +"analytics_prompt_terms_upgrade" = "Прочитать все наши условия %@. Они подходят?"; +"key_verification_scan_qr_code_information_other_session" = "Наведите камеру на QR-код, отображенном на другом вашем устройстве для подтверждения вашей сессии"; +"key_verification_scan_qr_code_information_other_device" = "Наведите камеру на QR-код, отображенном на другом вашем устройстве для подтверждения этой сессии"; +"create_room_type_restricted" = "Участники пространства"; +"home_context_menu_low_priority" = "Низкий приоритет"; +"share_invite_link_space_text" = "Хэй, вступай в это пространство на %@"; +"create_room_promotion_header" = "ПРОДВИЖЕНИЕ"; +"create_room_show_in_directory_footer" = "Это может помочь людям искать и вступать."; +"space_invite_not_enough_permission" = "У вас нет разрешения для приглашения людей в это пространство"; + +// Room commands descriptions +"room_command_change_display_name_description" = "Изменяет отображаемое имя пользователя"; +"room_command_emote_description" = "Отображает действие"; +"room_command_join_room_description" = "Присоединяться к комнате с указанным адресом"; +"room_command_part_room_description" = "Покинуть комнату"; +"room_command_invite_user_description" = "Добавляет пользователя с указанным ID в текущую комнату"; +"room_command_kick_user_description" = "Удаляет пользователя с указанным ID из этой комнаты"; +"room_command_ban_user_description" = "Блокирует пользователя с указанным ID"; +"room_command_set_user_power_level_description" = "Назначает уровень силы пользователя"; +"room_command_reset_user_power_level_description" = "Разжалует пользователя с указанным ID"; +"room_command_discard_session_description" = "Принудительно отбрасывает текущий исходящий групповой сеанс в зашифрованной комнате"; +"room_command_error_unknown_command" = "Некорректная или необработанная команда"; + +// Legacy to Rust security upgrade + +"key_verification_self_verify_security_upgrade_alert_title" = "Приложение обновлено"; +"key_verification_self_verify_security_upgrade_alert_message" = "Безопасное общение может быть улучшено с новым обновлением. Пожалуйста, проверьте ваше устройство."; +"device_verification_self_verify_wait_recover_secrets_additional_help" = "Нет доступа к существующей %@ сессии?"; +"key_verification_scan_qr_code_information_new_session" = "Наведите камеру на QR-код, отображенном на другом вашем устройстве для подтверждения вашей новой сессии"; +"create_room_section_footer_type_restricted" = "Все в пространстве могут искать и вступать."; +"launch_loading_delay_warning" = "Это может занимать больше времени.\nСпасибо за терпение."; diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index 91e52cee0..061991f16 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2960,3 +2960,8 @@ "room_command_change_display_name_description" = "Mení vaše zobrazované meno / prezývku"; "notice_display_name_changed_to" = "%@ zmenil/a svoje zobrazované meno na %@"; "poll_timeline_loading" = "Načítavanie…"; +"room_creation_user_not_found_prompt_title" = "Potvrdenie"; +"room_creation_user_not_found_prompt_invite_action" = "Spustiť konverzáciu aj tak"; +"room_participants_invite_anyway" = "Napriek tomu pozvať"; +"room_creation_user_not_found_prompt_message" = "Nie je možné nájsť používateľské profily pre toto Matrix ID. Chcete aj tak poslať priamu správu?"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Nie je možné nájsť profily pre toto Matrix ID. Ste si istí, že chcete pozvať %@ do %@?"; diff --git a/Riot/Assets/sv.lproj/Vector.strings b/Riot/Assets/sv.lproj/Vector.strings index bbb4b12c5..48d13aea8 100644 --- a/Riot/Assets/sv.lproj/Vector.strings +++ b/Riot/Assets/sv.lproj/Vector.strings @@ -2676,3 +2676,31 @@ "room_command_change_room_topic_description" = "Sätter rummets ämne"; "room_command_discard_session_description" = "Tvingar den aktuella utgående gruppsessionen i ett krypterat rum att kasseras"; "room_command_error_unknown_command" = "Ogiltigt eller obehandlat kommando"; + +// Legacy to Rust security upgrade + +"key_verification_self_verify_security_upgrade_alert_title" = "App uppdaterad"; +"key_verification_self_verify_security_upgrade_alert_message" = "Säker meddelandehantering har förbättrats med den senaste uppdateringen. Vänligen verifiera din enhet igen."; +"device_verification_self_verify_open_on_other_device_title" = "Öppna %@ på din andra enhet"; +"device_verification_self_verify_wait_recover_secrets_additional_help" = "Kan du inte komma åt en befintlig %@-session?"; +"key_verification_scan_qr_code_title" = "Skanna QR-kod"; +"device_verification_self_verify_open_on_other_device_information" = "Du behöver verifiera den här sessionen för att kunna läsa din säkra meddelandehistorik.\n\nÖppna Element på en av dina andra enheter och följ instruktionerna."; +"key_verification_scan_qr_code_information_other_user" = "Rikta kameran mot QR-koden som visas på deras enhet för att verifiera deras session"; +"room_waiting_other_participants_title" = "Väntar på att användare ska gå med i %@"; +"pill_message_from" = "Meddelande från %@"; +"pill_message_in" = "Meddelande i %@"; +"notice_display_name_changed_to" = "%@ bytte sitt visningsnamn till %@"; + +// MARK: - Launch loading + +"launch_loading_generic" = "Synkar dina konversationer"; +"launch_loading_delay_warning" = "Detta kan ta lite längre tid.\nTack för ditt tålamod."; +"key_verification_scan_qr_code_information_other_device" = "Rikta kameran mot QR-koden som visas på din andra enhet för att verifiera den här sessionen"; +"room_waiting_other_participants_message" = "När inbjudna användare har gått med i %@ kommer du att kunna chatta och rummet kommer att totalsträckskrypteras"; +"poll_timeline_loading" = "Laddar …"; + +// Pills +"pill_room_fallback_display_name" = "Utrymme/rum"; +"pill_message" = "Meddelande"; +"key_verification_scan_qr_code_information_new_session" = "Rikta kameran mot QR-koden som visas på din andra enhet för att verifiera din nya session"; +"key_verification_scan_qr_code_information_other_session" = "Rikta kameran mot QR-koden som visas på din andra enhet för att verifiera din session"; diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index bac3f4c30..d5113e3c7 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -184,7 +184,7 @@ "settings_remove_prompt_title" = "Підтвердження"; "settings_surname" = "Прізвище"; "settings_first_name" = "Ім’я"; -"settings_display_name" = "Показуване ім’я"; +"settings_display_name" = "Псевдонім"; "settings_profile_picture" = "Зображення профілю"; "settings_sign_out_e2e_warn" = "Ви втратите всі ваші ключі наскрізного шифрування. Це означає що ви більше не будете мати змогу читати старі повідомлення у зашифрованих кімнатах на цьому пристрої."; "settings_sign_out_confirmation" = "Ви впевнені?"; @@ -433,7 +433,7 @@ "room_widget_permission_theme_permission" = "Ваша тема"; "room_widget_permission_user_id_permission" = "Ваш ID користувача"; "room_widget_permission_avatar_url_permission" = "URL-адреса вашого аватара"; -"room_widget_permission_display_name_permission" = "Ваше показуване імʼя"; +"room_widget_permission_display_name_permission" = "Ваш псевдонім"; "room_widget_permission_creator_info_title" = "Цей віджет додано:"; // Room widget permissions @@ -1129,7 +1129,7 @@ "settings_show_url_previews" = "Попередній перегляд вебсайтів"; "settings_ui_theme_picker_message_match_system_theme" = "«Авто» застосовує тему вашого пристрою"; "settings_ui_theme_picker_message_invert_colours" = "«Авто» застосовує налаштування вашого пристрою «Інвертувати кольори»"; -"settings_messages_containing_display_name" = "Моє показуване імʼя"; +"settings_messages_containing_display_name" = "Мій псевдонім"; "settings_discovery_three_pid_details_title_phone_number" = "Керувати номером телефону"; "settings_discovery_three_pid_details_title_email" = "Керувати е-поштою"; "settings_discovery_error_message" = "Сталася помилка. Повторіть спробу."; @@ -1866,7 +1866,7 @@ "login_prompt_email_token" = "Введіть ключ підтвердження електронної пошти:"; "login_email_placeholder" = "Адреса е-пошти"; "login_email_info" = "Вкажіть адресу електронної пошти, щоб інші користувачі могли легше знаходити вас на Matrix і надання вам можливості відновити пароль у майбутньому."; -"login_display_name_placeholder" = "Видиме ім'я (наприклад Bob Obson)"; +"login_display_name_placeholder" = "Псевдонім (наприклад Bob Obson)"; "login_optional_field" = "необов'язково"; "login_password_placeholder" = "Пароль"; "login_user_id_placeholder" = "Ідентифікатор Matrix (наприклад @bob:matrix.org або bob)"; @@ -2113,10 +2113,10 @@ "notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@ робить майбутню історію повідомлень видимою всім від часу їхнього приєднання."; "notice_room_history_visible_to_members_from_joined_point" = "%@ робить майбутню історію кімнати видимою всім учасникам кімнати від часу їхнього приєднання."; "notice_room_history_visible_to_members_from_invited_point_for_dm" = "%@ робить майбутню історію повідомлень видимою всім від часу їхнього запрошення."; -"notice_room_history_visible_to_members_from_invited_point" = "%@ робить майбутню історію кімнати видимою усім учасникам кімнати від часу їхнього запрошення."; -"notice_room_history_visible_to_members_for_dm" = "%@ робить майбутню історію повідомлень видимою усім учасникам кімнати."; -"notice_room_history_visible_to_members" = "%@ робить майбутню історію кімнати видимою усім учасникам кімнати."; -"notice_room_history_visible_to_anyone" = "%@ робить майбутню історію кімнати видимою усім."; +"notice_room_history_visible_to_members_from_invited_point" = "%@ робить майбутню історію кімнати видимою всім учасникам кімнати від часу їхнього запрошення."; +"notice_room_history_visible_to_members_for_dm" = "%@ робить майбутню історію повідомлень видимою всім учасникам кімнати."; +"notice_room_history_visible_to_members" = "%@ робить майбутню історію кімнати видимою всім учасникам кімнати."; +"notice_room_history_visible_to_anyone" = "%@ робить майбутню історію кімнати видимою всім."; "notice_redaction" = "%@ редагує подію (id: %@)"; "notice_feedback" = "Подія відгуку (id: %@): %@"; "notice_room_related_groups" = "Групи пов'язані з цією кімнатою: %@"; @@ -2136,9 +2136,9 @@ "notice_room_name_changed_for_dm" = "%@ змінює назву на %@."; "notice_room_name_changed" = "%@ змінює назву кімнати на %@."; "notice_topic_changed" = "%@ змінює тему на «%@»."; -"notice_display_name_removed" = "%@ вилучає своє показуване ім'я"; -"notice_display_name_changed_from" = "%@ змінює своє показуване ім'я з %@ на %@"; -"notice_display_name_set" = "%@ встановлює своїм показуваним іменем %@"; +"notice_display_name_removed" = "%@ вилучає свій псевдонім"; +"notice_display_name_changed_from" = "%@ змінює свій псевдонім з %@ на %@"; +"notice_display_name_set" = "%@ встановлює своїм псевдонімом %@"; "notice_room_withdraw" = "%@ анульовує запрошення для %@"; "notice_room_kick" = "%@ вилучає %@"; "notice_room_reject" = "%@ відхиляє запрошення"; @@ -2194,7 +2194,7 @@ "notification_settings_per_room_notifications" = "Сповіщення від кожної кімнати"; "notification_settings_per_sender_notifications" = "Сповіщення про кожного відправника"; "notification_settings_contain_my_user_name" = "Сповіщати звуком про повідомлення, що містять моє ім'я користиувача"; -"notification_settings_contain_my_display_name" = "Сповіщати звуком про повідомлення, що містять моє показуване ім'я"; +"notification_settings_contain_my_display_name" = "Сповіщати звуком про повідомлення з моїм псевдонімом"; "notification_settings_just_sent_to_me" = "Сповіщати звуком про надіслані лише мені повідомлення"; "notification_settings_invite_to_a_new_room" = "Сповіщати про запрошення до нових кімнат"; "notification_settings_people_join_leave_rooms" = "Сповіщати, коли люди приєднуються чи виходять з кімнат"; @@ -2209,7 +2209,7 @@ "account_error_email_wrong_title" = "Неправильна адреса е-пошти"; "account_error_matrix_session_is_not_opened" = "Сеанс Matrix не відкрито"; "account_error_picture_change_failed" = "Не вдалося змінити зображення"; -"account_error_display_name_change_failed" = "Не вдалося змінити показуване ім'я"; +"account_error_display_name_change_failed" = "Не вдалося змінити псевдонім"; "account_msisdn_validation_error" = "Не вдалося перевірити номер телефону."; "account_email_validation_title" = "Очікування перевірки"; "account_msisdn_validation_title" = "Очікування перевірки"; @@ -2241,12 +2241,12 @@ "e2e_passphrase_enter" = "Введіть парольну фразу"; "e2e_passphrase_empty" = "Парольна фраза не повинна бути порожньою"; "e2e_passphrase_confirm" = "Підтвердити парольну фразу"; -"notice_room_history_visible_to_members_from_joined_point_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими будь-кому від часу їхнього приєднання."; -"notice_room_history_visible_to_members_from_joined_point_by_you" = "Ви зробили майбутню історію кімнати видимою усім учасникам кімнати від часу їхнього приєднання."; +"notice_room_history_visible_to_members_from_joined_point_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими всім від часу їхнього приєднання."; +"notice_room_history_visible_to_members_from_joined_point_by_you" = "Ви зробили майбутню історію кімнати видимою всім учасникам кімнати від часу їхнього приєднання."; "notice_room_history_visible_to_members_from_invited_point_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими будь-кому від часу запрошення їх."; -"notice_room_history_visible_to_members_from_invited_point_by_you" = "Ви зробили майбутню історію кімнати видимою усім учасникам кімнати від часу запрошення їх."; -"notice_room_history_visible_to_members_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими усім учасникам кімнати."; -"notice_room_history_visible_to_members_by_you" = "Ви зробили майбутню історію кімнати видимою усім учасникам кімнати."; +"notice_room_history_visible_to_members_from_invited_point_by_you" = "Ви зробили майбутню історію кімнати видимою всім учасникам кімнати від часу запрошення їх."; +"notice_room_history_visible_to_members_by_you_for_dm" = "Ви зробили майбутні повідомлення кімнати видимими всім учасникам кімнати."; +"notice_room_history_visible_to_members_by_you" = "Ви зробили майбутню історію кімнати видимою всім учасникам кімнати."; "notice_room_history_visible_to_anyone_by_you" = "Ви зробили майбутню історію кімнати видимою будь-кому."; "notice_room_created_by_you" = "Ви створили й сконфігурували кімнату."; "notice_profile_change_redacted_by_you" = "Ви оновили свій профіль %@"; @@ -2261,9 +2261,9 @@ "notice_room_name_changed_by_you_for_dm" = "Ви змінили назву на %@."; "notice_room_name_changed_by_you" = "Ви змінили назву кімнати на %@."; "notice_topic_changed_by_you" = "Ви змінили тему на «%@»."; -"notice_display_name_removed_by_you" = "Ви вилучили показуване ім'я"; -"notice_display_name_changed_from_by_you" = "Ви змінили показуване ім'я з %@ на %@"; -"notice_display_name_set_by_you" = "Ви вказали показуваним іменем %@"; +"notice_display_name_removed_by_you" = "Ви вилучили псевдонім"; +"notice_display_name_changed_from_by_you" = "Ви змінили псевдонім з %@ на %@"; +"notice_display_name_set_by_you" = "Ви налаштували псевдонімом %@"; "notice_conference_call_finished" = "Голосовий груповий виклик завершено"; "notice_conference_call_started" = "Груповий голосовий виклик розпочато"; "notice_conference_call_request" = "%@ запрошує до групового голосового виклику"; @@ -2467,11 +2467,11 @@ "onboarding_avatar_accessibility_label" = "Зображення профілю"; "onboarding_avatar_message" = "Час додати обличчя до імені"; "onboarding_avatar_title" = "Додати зображення профілю"; -"onboarding_display_name_max_length" = "Ваше показуване ім'я повинно складатися з менш ніж 256 символів"; +"onboarding_display_name_max_length" = "Ваш псевдонім повинен складатися з менш ніж 256 символів"; "onboarding_display_name_hint" = "Ви можете змінити його пізніше"; -"onboarding_display_name_placeholder" = "Показуване ім'я"; +"onboarding_display_name_placeholder" = "Псевдонім"; "onboarding_display_name_message" = "Його буде показано у надісланих повідомленнях."; -"onboarding_display_name_title" = "Виберіть показуване ім'я"; +"onboarding_display_name_title" = "Оберіть псевдонім"; "onboarding_personalization_skip" = "Пропустити цей крок"; "onboarding_personalization_save" = "Зберегти й продовжити"; "onboarding_congratulations_home_button" = "На головну"; @@ -2960,5 +2960,10 @@ // Room commands descriptions "room_command_change_display_name_description" = "Змінює ваш нік"; -"notice_display_name_changed_to" = "%@ змінили своє показуване ім'я на %@"; +"notice_display_name_changed_to" = "%@ змінили свій псевдонім на %@"; "poll_timeline_loading" = "Завантаження..."; +"room_creation_user_not_found_prompt_invite_action" = "Усе одно розпочати приватну бесіду"; +"room_participants_invite_anyway" = "Усе одно запросити"; +"room_creation_user_not_found_prompt_title" = "Підтвердження"; +"room_creation_user_not_found_prompt_message" = "Не вдалося знайти профілі для цього Matrix ID. Усе одно хочете розпочати приватну бесіду?"; +"room_participants_invite_unknown_participant_prompt_to_msg" = "Не вдалося знайти профілі для цього Matrix ID. Ви впевнені, що хочете запросити %@ до %@?"; diff --git a/Riot/Generated/BWIStrings.swift b/Riot/Generated/BWIStrings.swift index 7c7d639fb..fc0327740 100644 --- a/Riot/Generated/BWIStrings.swift +++ b/Riot/Generated/BWIStrings.swift @@ -15,6 +15,10 @@ public class BWIL10n: NSObject { public static var allChatsEditLayoutShowPersonalNotes: String { return BWIL10n.tr("Bwi", "all_chats_edit_layout_show_personal_notes") } + /// Suche + public static var allChatsSearchbarPrompt: String { + return BWIL10n.tr("Bwi", "all_chats_searchbar_prompt") + } /// Passwort vergessen? public static var authForgotPassword: String { return BWIL10n.tr("Bwi", "auth_forgot_password") @@ -207,6 +211,10 @@ public class BWIL10n: NSObject { public static var bwiEditPersonalStateTitle: String { return BWIL10n.tr("Bwi", "bwi_edit_personal_state_title") } + /// Falsches Passwort + public static var bwiErrorInvalidPassword: String { + return BWIL10n.tr("Bwi", "bwi_error_invalid_password") + } /// %@ ist bereits im Raum. public static func bwiErrorInviteAlreadyInRoom(_ p1: String) -> String { return BWIL10n.tr("Bwi", "bwi_error_invite_already_in_room", p1) @@ -231,7 +239,7 @@ public class BWIL10n: NSObject { public static var bwiErrorRoomNotAvailableTitle: String { return BWIL10n.tr("Bwi", "bwi_error_room_not_available_title") } - /// Mit dem neuen Feature Ruhezeiteneinstellung lassen sich Benachrichtigungen nach Wochentag und Uhrzeit ein- bzw stummschalten. + /// Beim Schreiben von Nachrichten kannst Du jetzt sehr einfach Textformatierungen verwenden. public static var bwiFeatureBannerAdvertisementText: String { return BWIL10n.tr("Bwi", "bwi_feature_banner_advertisement_text") } @@ -527,10 +535,38 @@ public class BWIL10n: NSObject { public static var bwiSettingsDeveloperApplyOrigAppConfig: String { return BWIL10n.tr("Bwi", "bwi_settings_developer_apply_orig_app_config") } + /// Capabilities + public static var bwiSettingsDeveloperCapabilities: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_capabilities") + } /// Neuen Notizen-Raum Raum erstellen public static var bwiSettingsDeveloperCreateNewPersonalNotesRoom: String { return BWIL10n.tr("Bwi", "bwi_settings_developer_create_new_personal_notes_room") } + /// Key Backup + public static var bwiSettingsDeveloperKeyBackup: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_key_backup") + } + /// Algorithmus + public static var bwiSettingsDeveloperKeyBackupAlgorithm: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_key_backup_algorithm") + } + /// Gespeicherte Schlüssel + public static var bwiSettingsDeveloperKeyBackupCount: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_key_backup_count") + } + /// ETag + public static var bwiSettingsDeveloperKeyBackupEtag: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_key_backup_etag") + } + /// Version + public static var bwiSettingsDeveloperKeyBackupVersion: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_key_backup_version") + } + /// Maintenance + public static var bwiSettingsDeveloperMaintenance: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_maintenance") + } /// Der alte Notizen-Raum Raum wurde durch einen neuen ersetzt. public static var bwiSettingsDeveloperNewPersonalNotesRoomCreated: String { return BWIL10n.tr("Bwi", "bwi_settings_developer_new_personal_notes_room_created") @@ -571,6 +607,18 @@ public class BWIL10n: NSObject { public static var bwiSettingsDeveloperUnrestrictUser: String { return BWIL10n.tr("Bwi", "bwi_settings_developer_unrestrict_user") } + /// Account Data + public static var bwiSettingsDeveloperUserAccountData: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_user_account_data") + } + /// Type + public static var bwiSettingsDeveloperUserAccountDataType: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_user_account_data_type") + } + /// Well-Known + public static var bwiSettingsDeveloperWellKnown: String { + return BWIL10n.tr("Bwi", "bwi_settings_developer_well_known") + } /// Einen Raum für private Notizen anzeigen public static var bwiSettingsEnableNotesRoomSummary: String { return BWIL10n.tr("Bwi", "bwi_settings_enable_notes_room_summary") @@ -739,6 +787,10 @@ public class BWIL10n: NSObject { public static var e2eKeyBackupWrongVersion: String { return BWIL10n.tr("Bwi", "e2e_key_backup_wrong_version") } + /// Notizen ausblenden + public static var homeContextMenuPersonalNotes: String { + return BWIL10n.tr("Bwi", "home_context_menu_personal_notes") + } /// Die Verbindung zum Heimserver ist fehlgeschlagen. public static var homeserverConnectionLost: String { return BWIL10n.tr("Bwi", "homeserver_connection_lost") @@ -1011,6 +1063,14 @@ public class BWIL10n: NSObject { public static var pollEditFormPollTypeOpen: String { return BWIL10n.tr("Bwi", "poll_edit_form_poll_type_open") } + /// Uhr + public static var pollParticipantDetailsClockString: String { + return BWIL10n.tr("Bwi", "poll_participant_details_clock_string") + } + /// Heute, + public static var pollParticipantDetailsDateStringToday: String { + return BWIL10n.tr("Bwi", "poll_participant_details_date_string_today") + } /// Alle ansehen (%lu weitere) public static func pollParticipantDetailsShowMore(_ p1: Int) -> String { return BWIL10n.tr("Bwi", "poll_participant_details_show_more", p1) @@ -1019,7 +1079,7 @@ public class BWIL10n: NSObject { public static var pollParticipantDetailsTitle: String { return BWIL10n.tr("Bwi", "poll_participant_details_title") } - /// Stimmen anzeigen + /// Stimmen ansehen public static var pollTimelineShowParticipantsButton: String { return BWIL10n.tr("Bwi", "poll_timeline_show_participants_button") } @@ -1587,6 +1647,10 @@ public class BWIL10n: NSObject { public static var settingsOther: String { return BWIL10n.tr("Bwi", "settings_other") } + /// Dein Passwort wurde erfolgreich geändert. + public static var settingsPasswordChanged: String { + return BWIL10n.tr("Bwi", "settings_password_changed") + } /// Regeln: Mindestlänge 8 Zeichen, mind. 1 Sonderzeichen, 1 Groß- und 1 Kleinbuchstaben und 1 Ziffer public static var settingsPasswordCondition: String { return BWIL10n.tr("Bwi", "settings_password_condition") @@ -1611,10 +1675,6 @@ public class BWIL10n: NSObject { public static var settingsPasswordTooShortMessage: String { return BWIL10n.tr("Bwi", "settings_password_too_short_message") } - /// Dein Passwort wurde aktualisiert - public static var settingsPasswordUpdated: String { - return BWIL10n.tr("Bwi", "settings_password_updated") - } /// Permalink Prefix public static var settingsPermalinkPrefixPickerTitle: String { return BWIL10n.tr("Bwi", "settings_permalink_prefix_picker_title") @@ -1755,7 +1815,7 @@ public class BWIL10n: NSObject { public static func voiceMessageRemainingRecordingTime(_ p1: String) -> String { return BWIL10n.tr("Bwi", "voice_message_remaining_recording_time", p1) } - /// Sliden zum Abbrechen + /// Wischen zum Abbrechen public static var voiceMessageSlideToCancel: String { return BWIL10n.tr("Bwi", "voice_message_slide_to_cancel") } diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index c59cb5e4d..b446b0823 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -215,7 +215,7 @@ public class VectorL10n: NSObject { public static var allChatsSectionTitle: String { return VectorL10n.tr("Vector", "all_chats_section_title") } - /// All chats + /// Chats public static var allChatsTitle: String { return VectorL10n.tr("Vector", "all_chats_title") } @@ -3667,6 +3667,14 @@ public class VectorL10n: NSObject { public static var manageSessionNotTrusted: String { return VectorL10n.tr("Vector", "manage_session_not_trusted") } + /// You will be redirected to your server's authentication provider to complete sign out. + public static var manageSessionRedirect: String { + return VectorL10n.tr("Vector", "manage_session_redirect") + } + /// Functionality currently unavailable. Please contact your homeserver admin + public static var manageSessionRedirectError: String { + return VectorL10n.tr("Vector", "manage_session_redirect_error") + } /// Rename session public static var manageSessionRename: String { return VectorL10n.tr("Vector", "manage_session_rename") @@ -7791,6 +7799,18 @@ public class VectorL10n: NSObject { public static var settingsLinks: String { return VectorL10n.tr("Vector", "settings_links") } + /// Manage account + public static var settingsManageAccountAction: String { + return VectorL10n.tr("Vector", "settings_manage_account_action") + } + /// Manage your account at %@ + public static func settingsManageAccountDescription(_ p1: String) -> String { + return VectorL10n.tr("Vector", "settings_manage_account_description", p1) + } + /// Account + public static var settingsManageAccountTitle: String { + return VectorL10n.tr("Vector", "settings_manage_account_title") + } /// Mark all messages as read public static var settingsMarkAllAsRead: String { return VectorL10n.tr("Vector", "settings_mark_all_as_read") diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 05734cc4d..f02377f34 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -192,7 +192,7 @@ final class RiotSettings: NSObject { var enableClientInformationFeature /// Flag indicating if the wysiwyg composer feature is enabled - @UserDefault(key: "enableWysiwygComposer", defaultValue: false, storage: defaults) + @UserDefault(key: "enableWysiwygComposer", defaultValue: BWIBuildSettings.shared.enableFeatureWYSIWYGByDefault, storage: defaults) var enableWysiwygComposer @UserDefault(key: "enableWysiwygTextFormatting", defaultValue: true, storage: defaults) diff --git a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift index f1f409f65..24dfe5c78 100644 --- a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift +++ b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift @@ -52,11 +52,14 @@ final class HomeserverConfigurationBuilder: NSObject { } else { secureBackupSetupMethods = [.passphrase] } + + let deviceDehydrationEnabled = wellKnown?.jsonDictionary()["org.matrix.msc3814"] as? Bool == true let encryptionConfiguration = HomeserverEncryptionConfiguration(isE2EEByDefaultEnabled: isE2EEByDefaultEnabled, isSecureBackupRequired: isSecureBackupRequired, secureBackupSetupMethods: secureBackupSetupMethods, - outboundKeysPreSharingMode: outboundKeysPreSharingMode) + outboundKeysPreSharingMode: outboundKeysPreSharingMode, + deviceDehydrationEnabled: deviceDehydrationEnabled) // Jitsi configuration let jitsiPreferredDomain: String? diff --git a/Riot/Model/HomeserverConfiguration/HomeserverEncryptionConfiguration.swift b/Riot/Model/HomeserverConfiguration/HomeserverEncryptionConfiguration.swift index 6672bff45..671756f63 100644 --- a/Riot/Model/HomeserverConfiguration/HomeserverEncryptionConfiguration.swift +++ b/Riot/Model/HomeserverConfiguration/HomeserverEncryptionConfiguration.swift @@ -23,15 +23,18 @@ final class HomeserverEncryptionConfiguration: NSObject { let isSecureBackupRequired: Bool let secureBackupSetupMethods: [VectorWellKnownBackupSetupMethod] let outboundKeysPreSharingMode: MXKKeyPreSharingStrategy + let deviceDehydrationEnabled: Bool init(isE2EEByDefaultEnabled: Bool, isSecureBackupRequired: Bool, secureBackupSetupMethods: [VectorWellKnownBackupSetupMethod], - outboundKeysPreSharingMode: MXKKeyPreSharingStrategy) { + outboundKeysPreSharingMode: MXKKeyPreSharingStrategy, + deviceDehydrationEnabled: Bool) { self.isE2EEByDefaultEnabled = isE2EEByDefaultEnabled self.isSecureBackupRequired = isSecureBackupRequired self.outboundKeysPreSharingMode = outboundKeysPreSharingMode self.secureBackupSetupMethods = secureBackupSetupMethods + self.deviceDehydrationEnabled = deviceDehydrationEnabled super.init() } diff --git a/Riot/Modules/Authentication/AuthenticationCoordinator.swift b/Riot/Modules/Authentication/AuthenticationCoordinator.swift index cb10aacfd..05b80ca30 100644 --- a/Riot/Modules/Authentication/AuthenticationCoordinator.swift +++ b/Riot/Modules/Authentication/AuthenticationCoordinator.swift @@ -144,7 +144,12 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc // bwi: force serverselection on test apps if (BWIBuildSettings.shared.authScreenShowTestServerOptions && BWIBuildSettings.shared.bwiLoginFlowLayout) || BWIBuildSettings.shared.bumLoginFlowLayout { - showServerSelectionScreen(for: flow) + // bwi #3811: when there is a valid url from app config don't show server selection + if BWIBuildSettings.shared.avoidServerSelectionOnAppConfig && AppConfigService.shared.isAppConfig { + self.useHomeserver(AppConfigService.shared.serverUrl()) + } else { + showServerSelectionScreen(for: flow) + } } else { do { // Start the flow using the default server (or a provisioning link if set). @@ -267,6 +272,21 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc callback?(.cancel(.register)) } + /// Updates the login flow using the supplied homeserver address, or shows an error when this isn't possible. + @MainActor private func useHomeserver(_ homeserverAddress: String) { + + let homeserverAddress = HomeserverAddress.sanitized(homeserverAddress) + + Task { + do { + try await authenticationService.startFlow(.login, for: homeserverAddress) + showLoginScreen() + } catch { + displayError(message: error.localizedDescription) + } + } + } + // MARK: - Login /// Shows the login screen. diff --git a/Riot/Modules/Authentication/SSO/SSOAuthenticationService.swift b/Riot/Modules/Authentication/SSO/SSOAuthenticationService.swift index a0282c742..706c3782d 100644 --- a/Riot/Modules/Authentication/SSO/SSOAuthenticationService.swift +++ b/Riot/Modules/Authentication/SSO/SSOAuthenticationService.swift @@ -50,7 +50,7 @@ final class SSOAuthenticationService: NSObject { var ssoRedirectPath = SSOURLConstants.Paths.redirect - if let identityProvider = identityProvider { + if let identityProvider = identityProvider, !identityProvider.isEmpty { ssoRedirectPath.append("/\(identityProvider)") } diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m index 3170fc2df..fffaa3fe3 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m @@ -129,23 +129,8 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou } - (NSArray> *)favoriteCellDataArray { - // bwi: for personal notes room - NSMutableArray> *filteredArray = [NSMutableArray arrayWithArray:self.recentsListService.favoritedRoomListData.rooms]; - - if( _recentsDataSourceMode == RecentsDataSourceModeFavourites) { - PersonalNotesDefaultService *service = [[PersonalNotesDefaultService alloc] initWithMxSession:self.mxSession]; - PersonalNotesSettings *settings = [[PersonalNotesSettings alloc] init]; - - if ([service personalNotesRoomId] && settings.personalNotesVisible) { - MXRoom* room = [self.mxSession roomWithRoomId:[service personalNotesRoomId]]; - if (room) { - MXRoomSummary *summary = [self.mxSession roomSummaryWithRoomId:[service personalNotesRoomId]]; - [filteredArray insertObject:summary atIndex:0]; - } - } - } - - return filteredArray; + // bwi #4802: for personal notes room + return [self filteredRoomCellDataArray:self.recentsListService.favoritedRoomListData.rooms]; } - (NSArray> *)peopleCellDataArray { @@ -153,17 +138,8 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou } - (NSArray> *)conversationCellDataArray { - // bwi: for personal notes room - NSMutableArray> *filteredArray = [NSMutableArray arrayWithCapacity:self.recentsListService.conversationRoomListData.rooms.count]; - - for ( id object in self.recentsListService.conversationRoomListData.rooms) { - MXRoom* room = [self.mxSession roomWithRoomId:object.roomId]; - if (!room.isPersonalNotesRoom) { - [filteredArray addObject:object]; - } - } - - return filteredArray; + // bwi #4802: for personal notes room + return [self filteredRoomCellDataArray:self.recentsListService.conversationRoomListData.rooms]; } - (NSArray> *)lowPriorityCellDataArray { @@ -179,26 +155,28 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou } - (NSArray> *)breadcrumbsRoomCellDataArray { - // bwi: for personal notes room - NSMutableArray> *filteredArray = [NSMutableArray arrayWithCapacity:self.recentsListService.breadcrumbsRoomListData.rooms.count]; - - for ( id object in self.recentsListService.breadcrumbsRoomListData.rooms) { - MXRoom* room = [self.mxSession roomWithRoomId:object.roomId]; - if (!room.isPersonalNotesRoom) { - [filteredArray addObject:object]; - } - } - - return filteredArray; + // bwi #4802: for personal notes room + return [self filteredRoomCellDataArray:self.recentsListService.breadcrumbsRoomListData.rooms]; } - (NSArray> *)allChatsRoomCellDataArray { - // bwi: for personal notes room - NSMutableArray> *filteredArray = [NSMutableArray arrayWithCapacity:self.recentsListService.allChatsRoomListData.rooms.count]; + // bwi #4802: for personal notes room + return [self filteredRoomCellDataArray:self.recentsListService.allChatsRoomListData.rooms]; +} + +- (NSArray> *)filteredRoomCellDataArray:(NSArray> *)arrayToFilter { + // bwi: #4802 for personal notes room + NSMutableArray> *filteredArray = [NSMutableArray arrayWithCapacity:arrayToFilter.count]; - for ( id object in self.recentsListService.allChatsRoomListData.rooms) { + PersonalNotesSettings *settings = [[PersonalNotesSettings alloc] init]; + + for ( id object in arrayToFilter) { MXRoom* room = [self.mxSession roomWithRoomId:object.roomId]; - if (!room.isPersonalNotesRoom) { + if (room.isPersonalNotesRoom ) { + if (settings.personalNotesVisible && BWIBuildSettings.shared.bwiPersonalNotesRoom) { + [filteredArray addObject:object]; + } + } else { [filteredArray addObject:object]; } } @@ -265,9 +243,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou } } - if (self.recentsListService.shouldShowBWIPersonalNotes) { - [types addObject:@(RecentsDataSourceSectionTypePersonalNotes)]; - } if (self.favoriteCellDataArray.count > 0 || _recentsDataSourceMode == RecentsDataSourceModeFavourites) { @@ -716,10 +691,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou { count = self.allChatsRoomCellDataArray.count ?: 1; } - else if (sectionType == RecentsDataSourceSectionTypePersonalNotes) - { - count = 1; - } // Adjust this count according to the potential dragged cell. if ([self isMovingCellSection:section]) @@ -744,7 +715,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou (sectionType == RecentsDataSourceSectionTypeInvites && self.recentsDataSourceMode == RecentsDataSourceModeAllChats) || (sectionType == RecentsDataSourceSectionTypeAllChats && !self.allChatsFilterOptions.optionsCount) || (sectionType == RecentsDataSourceSectionTypeAllChats && self.currentSpace != nil && self.currentSpace.childRoomIds.count == 0) || - sectionType == RecentsDataSourceSectionTypePersonalNotes || sectionType == RecentsDataSourceSectionTypeFeatureBanner) + sectionType == RecentsDataSourceSectionTypeFeatureBanner) { return 0.0; } @@ -1284,11 +1255,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou summary = self.allChatsRoomCellDataArray[cellDataIndex]; } } - else if (sectionType == RecentsDataSourceSectionTypePersonalNotes) { - PersonalNotesDefaultService* service = [[PersonalNotesDefaultService alloc] initWithMxSession:self.mxSession]; - - summary = [self.mxSession roomWithRoomId:[service personalNotesRoomId]].summary; - } if (summary) { diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSourceSectionType.swift b/Riot/Modules/Common/Recents/DataSources/RecentsDataSourceSectionType.swift index d0a3aa9f1..cc9d17b04 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSourceSectionType.swift +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSourceSectionType.swift @@ -28,7 +28,6 @@ import Foundation case serverNotice case suggestedRooms case breadcrumbs - case personalNotes case searchedRoom case allChats case unknown diff --git a/Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift b/Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift index 631103713..2ef2da675 100644 --- a/Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift +++ b/Riot/Modules/ContextMenu/ActionProviders/RoomActionProvider.swift @@ -38,7 +38,7 @@ class RoomActionProvider: RoomActionProviderProtocol { if BWIBuildSettings.shared.bwiFilteredContextMenu { if service.roomId == PersonalNotesDefaultService(mxSession: service.session).personalNotesRoomId() { return UIMenu(children: [ - self.notificationsAction + self.hidePersonalNotesAction ]) } else { return UIMenu(children: [ @@ -110,6 +110,16 @@ class RoomActionProvider: RoomActionProviderProtocol { self.service.isRoomFavourite = !self.service.isRoomFavourite } } + + // bwi #4802 + private var hidePersonalNotesAction: UIAction { + return UIAction( + title: BWIL10n.homeContextMenuPersonalNotes, + image: UIImage(systemName: "star.slash")) { _ in + PersonalNotesSettings().personalNotesVisible = false + NotificationCenter.default.post(name: AllChatsLayoutSettingsManager.didUpdateSettings, object: self) + } + } private var lowPriorityAction: UIAction { return UIAction( diff --git a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift index 82d1e4fc2..b5a6522ae 100644 --- a/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift +++ b/Riot/Modules/Home/AllChats/AllChatsCoordinator.swift @@ -148,6 +148,10 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { self.bwiCheckForMatomoPromt() BWIAnalytics.sharedTracker.readUserConfig() + + if BWIBuildSettings.shared.bwiPersonalNotesRoom { + self.bwiCheckForPersonalNotesRoom() + } } func bwiOnUnlockedByPin() { @@ -301,10 +305,6 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { private func addMatrixSessionToAllChatsController(_ matrixSession: MXSession) { MXLog.debug("[TabBarCoordinator] masterTabBarController.addMatrixSession") self.allChatsViewController.addMatrixSession(matrixSession) - - if BWIBuildSettings.shared.bwiPersonalNotesRoom { - self.bwiCheckForPersonalNotesRoom() - } } // TODO: Remove Matrix session handling from the view controller @@ -388,7 +388,11 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { let avatarInsets: UIEdgeInsets = .init(top: 7, left: 7, bottom: 7, right: 7) var button: UIButton = .init(frame: view.bounds) button.imageEdgeInsets = avatarInsets - button.setImage(Asset.Images.tabPeople.image, for: .normal) + + // bwi: 4704 - ui improvements + let gearshapeImage = Asset.Images.bwiSettingsFilled.image + button.setImage(gearshapeImage, for: .normal) + button.tintColor = ThemeService.shared().theme.tintColor if BWIBuildSettings.shared.enableSideMenu { button.menu = avatarMenu @@ -427,16 +431,18 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { } private func updateAvatarButtonItem() { - MXLog.info("[AllChatsCoordinator] updating avatar button item.") - if let avatar = userAvatarViewData(from: currentMatrixSession) { - if avatarMenuView == nil { - MXLog.warning("[AllChatsCoordinator] updateAvatarButtonItem: avatarMenuView is nil.") - } - avatarMenuView?.fill(with: avatar) - avatarMenuButton?.setImage(nil, for: .normal) - } else { - avatarMenuButton?.setImage(Asset.Images.tabPeople.image, for: .normal) - } + // bwi: 4704 - ui improvements (disable view updates) + +// MXLog.info("[AllChatsCoordinator] updating avatar button item.") +// if let avatar = userAvatarViewData(from: currentMatrixSession) { +// if avatarMenuView == nil { +// MXLog.warning("[AllChatsCoordinator] updateAvatarButtonItem: avatarMenuView is nil.") +// } +// avatarMenuView?.fill(with: avatar) +// avatarMenuButton?.setImage(nil, for: .normal) +// } else { +// avatarMenuButton?.setImage(Asset.Images.tabPeople.image, for: .normal) +// } } private func showRoom(withId roomId: String, eventId: String? = nil) { @@ -706,6 +712,7 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol { } service.createPersonalNotesRoomIfNeeded() + service.setAsFavoriteIfNeeded() } } @@ -777,6 +784,10 @@ extension AllChatsCoordinator: AllChatsViewControllerDelegate { self.bwiCheckForMatomoPromt() BWIAnalytics.sharedTracker.resetMatomo() + + if BWIBuildSettings.shared.bwiPersonalNotesRoom { + self.bwiCheckForPersonalNotesRoom() + } } func allChatsViewController(_ allChatsViewController: AllChatsViewController, didSelectRoomWithParameters roomNavigationParameters: RoomNavigationParameters, completion: @escaping () -> Void) { diff --git a/Riot/Modules/Home/AllChats/AllChatsViewController.swift b/Riot/Modules/Home/AllChats/AllChatsViewController.swift index 269b46aa5..58f0553f1 100644 --- a/Riot/Modules/Home/AllChats/AllChatsViewController.swift +++ b/Riot/Modules/Home/AllChats/AllChatsViewController.swift @@ -77,6 +77,9 @@ class AllChatsViewController: HomeViewController { private var isOnboardingCoordinatorPreparing: Bool = false + // bwi: 4807 + private var floatingButton: UIButton? + private var theme: Theme { ThemeService.shared().theme } @@ -92,10 +95,11 @@ class AllChatsViewController: HomeViewController { } private func setToolbarHidden(_ isHidden: Bool, animated: Bool) { - UIView.animate(withDuration: animated ? 0.3 : 0) { - self.isToolbarHidden = isHidden + if BWIBuildSettings.shared.enableAllChatsToolbar { + UIView.animate(withDuration: animated ? 0.3 : 0) { + self.isToolbarHidden = isHidden + } } - } // MARK: - SplitViewMasterViewControllerProtocol @@ -117,6 +121,8 @@ class AllChatsViewController: HomeViewController { private var toolbarHeight: CGFloat = 0 + private weak var roomFilterButton: UIButton? + // MARK: - Lifecycle override func viewDidLoad() { @@ -135,25 +141,46 @@ class AllChatsViewController: HomeViewController { toolbarHeight = toolbar.frame.height emptyViewBottomAnchor = toolbar.topAnchor - - // bwi: 4179 - + if BWIBuildSettings.shared.useNewBumColors { // bwi: #4883 toolbar.tintColor = ThemeService.shared().theme.tintColor toolbar.barTintColor = ThemeService.shared().theme.backgroundColor } else { toolbar.tintColor = theme.colors.accent } + + // bwi: 4807 - hide the toolbar and show a floating button for room create instead + if !BWIBuildSettings.shared.enableAllChatsToolbar { + // no toolbar then use a floating button instead + floatingButton = UIButton(frame: CGRect(x: 0, y: 0, width: 56, height: 56)) + updateFloatingButton() + if let floatingButton = floatingButton { + view.addSubview(floatingButton) + } + + // set constraint to make the floating button stay in the lower right corner + floatingButton?.translatesAutoresizingMaskIntoConstraints = false + floatingButton?.widthAnchor.constraint(equalToConstant: 56).isActive = true + floatingButton?.heightAnchor.constraint(equalToConstant: 56).isActive = true + floatingButton?.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -16).isActive = true + floatingButton?.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -40).isActive = true + } + if BWIBuildSettings.shared.roomFiltersToggle { + updateNewFilterSearchAndToggleButton() + } + updateUI() - navigationItem.largeTitleDisplayMode = .automatic - navigationController?.navigationBar.prefersLargeTitles = true - - searchController.obscuresBackgroundDuringPresentation = false - searchController.searchResultsUpdater = self - searchController.delegate = self + navigationItem.largeTitleDisplayMode = .never + navigationController?.navigationBar.prefersLargeTitles = false + if !BWIBuildSettings.shared.roomFiltersToggle { + searchController.obscuresBackgroundDuringPresentation = false + searchController.searchResultsUpdater = self + searchController.delegate = self + } + NotificationCenter.default.addObserver(self, selector: #selector(self.setupEditOptions), name: AllChatsLayoutSettingsManager.didUpdateSettings, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.updateBadgeButton), name: MXSpaceNotificationCounter.didUpdateNotificationCount, object: nil) @@ -161,6 +188,76 @@ class AllChatsViewController: HomeViewController { self.registerThemeServiceDidChangeThemeNotification() } + // bwi: 4806 + private func updateNewFilterSearchAndToggleButton() { + let customHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: recentsTableView.frame.width, height: 50)) + let searchBar = UISearchBar(frame: CGRect(x: 8, y: 0, width: customHeaderView.frame.width - 48, height: 50)) + let button = UIButton(frame: CGRect(x: customHeaderView.frame.width - 48 + 8, y: 0, width: 40, height: 50)) + + if ThemeService.shared().isCurrentThemeDark() { + button.setImage(Asset.Images.roomFilterToggleDarkOff.image, for: .normal) + button.setImage(Asset.Images.roomFilterToggleDarkOn.image, for: .selected) + } else { + button.setImage(Asset.Images.roomFilterToggleLightOff.image, for: .normal) + button.setImage(Asset.Images.roomFilterToggleLightOn.image, for: .selected) + } + let settings = AllChatsLayoutSettingsManager.shared.allChatLayoutSettings + let areFiltersVisible = !settings.filters.isEmpty + button.isSelected = !AllChatsLayoutSettingsManager.shared.allChatLayoutSettings.filters.isEmpty + button.addTarget(self, action: #selector(onFilterToggleTapped), for: .touchUpInside) + + roomFilterButton = button // keep a reference to handle theme changes + + searchBar.placeholder = BWIL10n.allChatsSearchbarPrompt + searchBar.backgroundImage = UIImage() // this hides the separator lines above and below + searchBar.delegate = self + + customHeaderView.addSubview(searchBar) + customHeaderView.addSubview(button) + + recentsTableView.tableHeaderView = customHeaderView + + // set some constraints for landscape and iPad + customHeaderView.translatesAutoresizingMaskIntoConstraints = false + let constraints = [ + searchBar.leadingAnchor.constraint(equalTo: customHeaderView.leadingAnchor), + searchBar.centerYAnchor.constraint(equalTo: button.centerYAnchor), + searchBar.widthAnchor.constraint(equalToConstant: 40), + searchBar.heightAnchor.constraint(equalToConstant: 40), + button.leadingAnchor.constraint(equalTo: searchBar.trailingAnchor), + button.trailingAnchor.constraint(equalTo: customHeaderView.trailingAnchor), + button.topAnchor.constraint(equalTo: customHeaderView.topAnchor), + button.bottomAnchor.constraint(equalTo: customHeaderView.bottomAnchor), + customHeaderView.centerXAnchor.constraint(equalTo: recentsTableView.centerXAnchor), + customHeaderView.widthAnchor.constraint(equalTo: recentsTableView.widthAnchor), + customHeaderView.topAnchor.constraint(equalTo: recentsTableView.topAnchor) + ] + NSLayoutConstraint.activate(constraints) + } + + @objc + func onSearchTextChanged(searchBar: UISearchBar) { + guard let searchText = searchBar.text, !searchText.isEmpty else { + self.dataSource?.search(withPatterns: nil) + return + } + + self.dataSource?.search(withPatterns: [searchText]) + } + + @objc + func onFilterToggleTapped() { + let settings = AllChatsLayoutSettingsManager.shared.allChatLayoutSettings + let areFiltersVisible = !settings.filters.isEmpty + let newSettings = AllChatsLayoutSettings(sections: settings.sections, + filters: areFiltersVisible ? [] : [.unreads, .favourites, .people], + sorting: settings.sorting) + AllChatsLayoutSettingsManager.shared.allChatLayoutSettings = newSettings + Analytics.shared.trackInteraction(areFiltersVisible ? .allChatsFiltersDisabled : .allChatsFiltersEnabled) + + roomFilterButton?.isSelected = !AllChatsLayoutSettingsManager.shared.allChatLayoutSettings.filters.isEmpty + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -172,8 +269,24 @@ class AllChatsViewController: HomeViewController { self.toolbar.tintColor = theme.colors.accent } - if self.navigationItem.searchController == nil { - self.navigationItem.searchController = searchController + // bwi: 4807 - hide the toolbar and show a floating button for room create instead + if !BWIBuildSettings.shared.enableAllChatsToolbar { + toolbar.transform = CGAffineTransform(translationX: 0, y: 2 * toolbarHeight) + self.view.layoutIfNeeded() + } + + if BWIBuildSettings.shared.roomFiltersToggle { + if ThemeService.shared().isCurrentThemeDark() { + roomFilterButton?.setImage(Asset.Images.roomFilterToggleDarkOff.image, for: .normal) + roomFilterButton?.setImage(Asset.Images.roomFilterToggleDarkOn.image, for: .selected) + } else { + roomFilterButton?.setImage(Asset.Images.roomFilterToggleLightOff.image, for: .normal) + roomFilterButton?.setImage(Asset.Images.roomFilterToggleLightOn.image, for: .selected) + } + } else { + if self.navigationItem.searchController == nil { + self.navigationItem.searchController = searchController + } } NotificationCenter.default.addObserver(self, selector: #selector(self.spaceListDidChange), name: MXSpaceService.didInitialise, object: nil) @@ -217,7 +330,7 @@ class AllChatsViewController: HomeViewController { coordinator.animate { context in self.recentsTableView?.tableHeaderView?.layoutIfNeeded() - self.recentsTableView?.tableHeaderView = self.recentsTableView?.tableHeaderView +// self.recentsTableView?.tableHeaderView = self.recentsTableView?.tableHeaderView } } @@ -319,7 +432,6 @@ class AllChatsViewController: HomeViewController { RecentsDataSourceSectionType.serverNotice.rawValue, RecentsDataSourceSectionType.suggestedRooms.rawValue, RecentsDataSourceSectionType.breadcrumbs.rawValue, - RecentsDataSourceSectionType.personalNotes.rawValue, RecentsDataSourceSectionType.featureBanner.rawValue ] } @@ -343,8 +455,20 @@ class AllChatsViewController: HomeViewController { alert.addAction(UIAlertAction(title: BWIL10n.bwiAnalyticsAlertInfoButton, style: .default, handler: { [self] action in - if let url = URL(string: BWIBuildSettings.shared.applicationPrivacyPolicyWithMatomoSectionUrlString) { - UIApplication.shared.open(url) + if let defaultURL = URL(string: BWIBuildSettings.shared.applicationPrivacyPolicyWithMatomoSectionUrlString) { + if BWIBuildSettings.shared.bwiUseWellKnownPrivacyPolicyLink { + guard let wellKnownDataPrivacyURL = URL(string: self.mainSession.homeserverWellknown.dataPrivacyURL() ?? ""), let defaultHost = defaultURL.host else { + UIApplication.shared.open(defaultURL) + return + } + if !wellKnownDataPrivacyURL.absoluteString.contains(defaultHost) { + UIApplication.shared.open(wellKnownDataPrivacyURL) + } else { + UIApplication.shared.open(defaultURL) + } + } else { + UIApplication.shared.open(defaultURL) + } } showMatomoConsentAlertOnCloseModal = true })) @@ -541,14 +665,16 @@ class AllChatsViewController: HomeViewController { override func shouldShowEmptyView() -> Bool { let shouldShowEmptyView = super.shouldShowEmptyView() && !AllChatsLayoutSettingsManager.shared.hasAnActiveFilter - if shouldShowEmptyView { - self.navigationItem.searchController = nil - navigationItem.largeTitleDisplayMode = .never - } else { - self.navigationItem.searchController = searchController - navigationItem.largeTitleDisplayMode = .automatic + if !BWIBuildSettings.shared.roomFiltersToggle { + if shouldShowEmptyView { + self.navigationItem.searchController = nil + navigationItem.largeTitleDisplayMode = .never + } else { + self.navigationItem.searchController = searchController + navigationItem.largeTitleDisplayMode = .automatic + } } - + return shouldShowEmptyView } @@ -580,6 +706,17 @@ class AllChatsViewController: HomeViewController { } else { self.navigationController?.toolbar?.tintColor = theme.colors.accent } + + // bwi: 4807 + updateFloatingButton() + } + + private func updateFloatingButton() { + if ThemeService.shared().isCurrentThemeDark() { + floatingButton?.setImage(Asset.Images.buttonNewDark.image, for: .normal) + } else { + floatingButton?.setImage(Asset.Images.buttonNewLight.image, for: .normal) + } } // MARK: - Private @@ -589,10 +726,10 @@ class AllChatsViewController: HomeViewController { return } - tableView.tableHeaderView = tableHeadeView - tableView.tableHeaderView?.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true - tableView.tableHeaderView?.layoutIfNeeded() - tableView.tableHeaderView = self.recentsTableView?.tableHeaderView +// tableView.tableHeaderView = tableHeadeView +// tableView.tableHeaderView?.widthAnchor.constraint(equalTo: tableView.widthAnchor).isActive = true +// tableView.tableHeaderView?.layoutIfNeeded() +// tableView.tableHeaderView = self.recentsTableView?.tableHeaderView } @objc private func setupEditOptions() { @@ -611,15 +748,31 @@ class AllChatsViewController: HomeViewController { self.title = currentSpace?.summary?.displayName ?? VectorL10n.allChatsTitle setupEditOptions() - updateToolbar(with: editActionProvider.updateMenu(with: mainSession, parentSpace: currentSpace, completion: { [weak self] menu in - self?.updateToolbar(with: menu) - })) + + let menu = editActionProvider.updateMenu(with: mainSession, parentSpace: currentSpace, completion: { [weak self] menu in + if BWIBuildSettings.shared.enableAllChatsToolbar { + self?.updateToolbar(with: menu) + } else { + } + }) + + if BWIBuildSettings.shared.enableAllChatsToolbar { + updateToolbar(with: menu) + } else { + updateFloatingButton() + self.floatingButton?.menu = menu + self.floatingButton?.showsMenuAsPrimaryAction = true + } + updateEmptyView() updateBadgeButton() } private func updateRightNavigationItem(with menu: UIMenu) { - self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis.circle"), menu: menu) + // bwi 4704 - hide right navigation bar button + if BWIBuildSettings.shared.showAllChatsFilterMenu { + self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis.circle"), menu: menu) + } } private lazy var spacesButton: BadgedBarButtonItem = { @@ -846,7 +999,7 @@ extension AllChatsViewController: UISearchResultsUpdating { self.dataSource?.search(withPatterns: nil) return } - + self.dataSource?.search(withPatterns: [searchText]) } } @@ -859,6 +1012,17 @@ extension AllChatsViewController: UISearchControllerDelegate { } } +extension AllChatsViewController { + override func searchBar(_ searchBar: UISearchBar, textDidChange: String) { + guard let searchText = searchBar.text, !searchText.isEmpty else { + self.dataSource?.search(withPatterns: nil) + return + } + + self.dataSource?.search(withPatterns: [searchText]) + } +} + // MARK: - UIAdaptivePresentationControllerDelegate extension AllChatsViewController: UIAdaptivePresentationControllerDelegate { diff --git a/Riot/Modules/Home/HomeViewController.m b/Riot/Modules/Home/HomeViewController.m index cf379f4fa..d178e9cc4 100644 --- a/Riot/Modules/Home/HomeViewController.m +++ b/Riot/Modules/Home/HomeViewController.m @@ -398,7 +398,6 @@ || (sectionType == RecentsDataSourceSectionTypePeople && !recentsDataSource.recentsListService.peopleRoomListData.counts.numberOfRooms) || (sectionType == RecentsDataSourceSectionTypeSecureBackupBanner) || (sectionType == RecentsDataSourceSectionTypeCrossSigningBanner) - || (sectionType == RecentsDataSourceSectionTypePersonalNotes) ) { return [recentsDataSource tableView:tableView cellForRowAtIndexPath:indexPath]; @@ -502,8 +501,7 @@ RecentsDataSourceSectionType sectionType = [recentsDataSource.sections sectionTypeForSectionIndex:indexPath.section]; if ((sectionType == RecentsDataSourceSectionTypeConversation && !recentsDataSource.recentsListService.conversationRoomListData.counts.numberOfRooms) - || (sectionType == RecentsDataSourceSectionTypePeople && !recentsDataSource.recentsListService.peopleRoomListData.counts.numberOfRooms) - || (sectionType == RecentsDataSourceSectionTypePersonalNotes)) + || (sectionType == RecentsDataSourceSectionTypePeople && !recentsDataSource.recentsListService.peopleRoomListData.counts.numberOfRooms)) { return [recentsDataSource cellHeightAtIndexPath:indexPath]; } @@ -582,7 +580,7 @@ { [self showCrossSigningSetup]; } - else if (sectionType == RecentsDataSourceSectionTypeAllChats || sectionType == RecentsDataSourceSectionTypePersonalNotes) + else if (sectionType == RecentsDataSourceSectionTypeAllChats) { [super tableView:tableView didSelectRowAtIndexPath:indexPath]; } diff --git a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.h b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.h index 4b218086f..bbfbfed06 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.h +++ b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.h @@ -305,15 +305,5 @@ */ - (void)showAuthenticationFallBackView; -#pragma mark - Device rehydration - -/** - Call this method at an appropriate time to attempt rehydrating from an existing dehydrated device - @param keyData Secret key data - @param credentials Account credentials - */ - -- (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData credentials:(MXCredentials *)credentials; - @end diff --git a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m index f9874a96a..35dfd8271 100644 --- a/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m +++ b/Riot/Modules/MatrixKit/Controllers/MXKAuthenticationViewController.m @@ -1513,68 +1513,6 @@ [self _createAccountWithCredentials:credentials]; } -- (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData - credentials:(MXCredentials *)credentials -{ - [self attemptDeviceRehydrationWithKeyData:keyData - credentials:credentials - retry:YES]; -} - -- (void)attemptDeviceRehydrationWithKeyData:(NSData *)keyData - credentials:(MXCredentials *)credentials - retry:(BOOL)retry -{ - MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: starting device rehydration"); - - if (keyData == nil) - { - MXLogError(@"[MXKAuthenticationViewController] attemptDeviceRehydration: no key provided for device rehydration"); - [self _createAccountWithCredentials:credentials]; - return; - } - - MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:credentials andOnUnrecognizedCertificateBlock:^BOOL(NSData *certificate) { - return NO; - } andPersistentTokenDataHandler:^(void (^handler)(NSArray *credentials, void (^completion)(BOOL didUpdateCredentials))) { - [[MXKAccountManager sharedManager] readAndWriteCredentials:handler]; - } andUnauthenticatedHandler: nil]; - - MXWeakify(self); - [[MXKAccountManager sharedManager].dehydrationService rehydrateDeviceWithMatrixRestClient:mxRestClient dehydrationKey:keyData success:^(NSString * deviceId) { - MXStrongifyAndReturnIfNil(self); - - if (deviceId) - { - MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device %@ rehydrated successfully.", deviceId); - credentials.deviceId = deviceId; - } - else - { - MXLogDebug(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration has been canceled."); - } - - [self _createAccountWithCredentials:credentials]; - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - if (retry) - { - MXLogErrorDetails(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration failed due to error: Retrying", @{ - @"error": error ?: @"unknown" - }); - [self attemptDeviceRehydrationWithKeyData:keyData credentials:credentials retry:NO]; - return; - } - - MXLogErrorDetails(@"[MXKAuthenticationViewController] attemptDeviceRehydration: device rehydration failed due to error", @{ - @"error": error ?: @"unknown" - }); - - [self _createAccountWithCredentials:credentials]; - }]; -} - - (void)_createAccountWithCredentials:(MXCredentials *)credentials { MXKAccount *account = [[MXKAccount alloc] initWithCredentials:credentials]; diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h index ce6d58b7f..19a210110 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.h @@ -375,13 +375,6 @@ typedef BOOL (^MXKAccountOnCertificateChange)(MXKAccount *mxAccount, NSData *cer #pragma mark - Sync filter -/** - Call this method at an appropriate time to attempt dehydrating to a new backup device - */ -- (void)attemptDeviceDehydrationWithKeyData:(NSData *)keyData - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure; - /** Handle unauthenticated errors from the server triggering hard/soft logouts as appropriate. */ diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m index 1cd659943..d3669af6e 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccount.m @@ -1727,70 +1727,6 @@ static NSArray *initialSyncSilentErrorsHTTPStatusCodes; }]; } -- (void)attemptDeviceDehydrationWithKeyData:(NSData *)keyData - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - [self attemptDeviceDehydrationWithKeyData:keyData retry:YES success:success failure:failure]; -} - -- (void)attemptDeviceDehydrationWithKeyData:(NSData *)keyData - retry:(BOOL)retry - success:(void (^)(void))success - failure:(void (^)(NSError *error))failure -{ - if (keyData == nil) - { - MXLogWarning(@"[MXKAccount] attemptDeviceDehydrationWithRetry: no key provided for device dehydration"); - - if (failure) - { - failure(nil); - } - - return; - } - - if (![mxSession.crypto.crossSigning isKindOfClass:[MXLegacyCrossSigning class]]) { - MXLogFailure(@"Device dehydratation is currently only supported by legacy cross signing, add support to all implementations"); - if (failure) - { - failure(nil); - } - return; - } - MXLegacyCrossSigning *crossSigning = (MXLegacyCrossSigning *)mxSession.crypto.crossSigning;; - - MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: starting device dehydration"); - [[MXKAccountManager sharedManager].dehydrationService dehydrateDeviceWithMatrixRestClient:mxRestClient crossSigning:crossSigning dehydrationKey:keyData success:^(NSString *deviceId) { - MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device successfully dehydrated"); - - if (success) - { - success(); - } - } failure:^(NSError *error) { - if (retry) - { - [self attemptDeviceDehydrationWithKeyData:keyData retry:NO success:success failure:failure]; - MXLogErrorDetails(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device dehydration failed due to error: Retrying.", @{ - @"error": error ?: @"unknown" - }); - } - else - { - MXLogErrorDetails(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device dehydration failed due to error", @{ - @"error": error ?: @"unknown" - }); - - if (failure) - { - failure(error); - } - } - }]; -} - - (void)onMatrixSessionStateChange { // Check if pause has been requested diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h index 7881832e0..2e21bd9a4 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.h @@ -104,8 +104,6 @@ extern NSString *const MXKAccountManagerDataType; */ @property (nonatomic) BOOL isPushAvailable; -@property (nonatomic, readonly) MXDehydrationService *dehydrationService; - /** Retrieve the MXKAccounts manager. diff --git a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m index 6a69ee6a7..c779e5c47 100644 --- a/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m +++ b/Riot/Modules/MatrixKit/Models/Account/MXKAccountManager.m @@ -71,7 +71,6 @@ NSString *const MXKAccountManagerDataType = @"org.matrix.kit.MXKAccountManagerDa if (self) { _storeClass = [MXFileStore class]; - _dehydrationService = [MXDehydrationService new]; _savingAccountsEnabled = YES; // Migrate old account file to new format diff --git a/Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m b/Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m index 077fb7001..c4119f883 100644 --- a/Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m +++ b/Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m @@ -41,8 +41,14 @@ } else { - title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; - message = [error.userInfo valueForKey:NSLocalizedDescriptionKey]; + // bwi: #4857 translate invalid password error message + if ([error.domain isEqualToString:kMXNSErrorDomain] && [[error.userInfo valueForKey:NSLocalizedDescriptionKey] isEqualToString: @"Invalid username or password"]) { + title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; + message = [BWIL10n bwiErrorInvalidPassword]; + } else { + title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; + message = [error.userInfo valueForKey:NSLocalizedDescriptionKey]; + } if (!title) { diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m index f4f109ff5..f55db8ff7 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m @@ -830,38 +830,37 @@ static NSString *const kRepliedTextPattern = @".*

.*
(.* } case MXEventTypeRoomCreate: { - NSString *creatorId; - MXJSONModelSetString(creatorId, event.content[@"creator"]); + // Room version 11 removes `creator` in favour of `sender`. + // https://github.com/matrix-org/matrix-spec-proposals/pull/2175 + // Just use the sender as it is possible to create a v11 room and spoof the `creator`. + NSString *creatorId = event.sender; - if (creatorId) + if ([creatorId isEqualToString:mxSession.myUserId]) { - if ([creatorId isEqualToString:mxSession.myUserId]) + if (isRoomDirect) { - if (isRoomDirect) - { - displayText = [VectorL10n noticeRoomCreatedByYouForDm]; - } - else - { - displayText = [VectorL10n noticeRoomCreatedByYou]; - } + displayText = [VectorL10n noticeRoomCreatedByYouForDm]; } else { - if (isRoomDirect) - { - displayText = [VectorL10n noticeRoomCreatedForDm:(roomState ? [roomState.members memberName:creatorId] : creatorId)]; - } - else - { - displayText = [VectorL10n noticeRoomCreated:(roomState ? [roomState.members memberName:creatorId] : creatorId)]; - } + displayText = [VectorL10n noticeRoomCreatedByYou]; } - // Append redacted info if any - if (redactedInfo) + } + else + { + if (isRoomDirect) { - displayText = [NSString stringWithFormat:@"%@ %@", displayText, redactedInfo]; + displayText = [VectorL10n noticeRoomCreatedForDm:(roomState ? [roomState.members memberName:creatorId] : creatorId)]; } + else + { + displayText = [VectorL10n noticeRoomCreated:(roomState ? [roomState.members memberName:creatorId] : creatorId)]; + } + } + // Append redacted info if any + if (redactedInfo) + { + displayText = [NSString stringWithFormat:@"%@ %@", displayText, redactedInfo]; } break; } diff --git a/Riot/Modules/Onboarding/OnboardingCoordinator.swift b/Riot/Modules/Onboarding/OnboardingCoordinator.swift index 7e17b32ad..ef638a2c2 100644 --- a/Riot/Modules/Onboarding/OnboardingCoordinator.swift +++ b/Riot/Modules/Onboarding/OnboardingCoordinator.swift @@ -189,7 +189,7 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol { } private func showWelcomeExperience() { - let welcomeVC = WelcomeExperienceViewController.makeViewController(completion: { [weak self] in + let welcomeVC = WelcomeExperienceViewController.makeViewController(completion: { [weak self] in if BWIBuildSettings.shared.onboardingEnableNewAuthenticationFlow { self?.startLoading() self?.beginAuthentication(with: .login) { [weak self] in diff --git a/Riot/Modules/Pills/PillsFormatter.swift b/Riot/Modules/Pills/PillsFormatter.swift index 1b6256835..ecdfac5fe 100644 --- a/Riot/Modules/Pills/PillsFormatter.swift +++ b/Riot/Modules/Pills/PillsFormatter.swift @@ -197,12 +197,14 @@ class PillsFormatter: NSObject { guard let roomMember = roomState.members.member(withUserId: userId) else { return } + + let displayName = roomMember.displayname ?? userId pill.data?.items = [ .avatar(url: roomMember.avatarUrl, - string: roomMember.displayname, - matrixId: roomMember.userId), - .text(roomMember.displayname) + string: displayName, + matrixId: userId), + .text(displayName) ] default: break diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 9e33cee1d..a714b0a4e 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4108,8 +4108,10 @@ static CGSize kThreadListBarButtonItemImageSize; [self cancelEventSelection]; }]]; } - - if (!isJitsiCallEvent && !selectedEvent.isTimelinePollEvent && + + // bwi: #5160 disable quote function for new wysiwyg composer because it does not work + if (!([RoomViewController mainToolbarClass] == WysiwygInputToolbarView.class) && + !isJitsiCallEvent && !selectedEvent.isTimelinePollEvent && selectedEvent.eventType != MXEventTypeBeaconInfo) { [self.eventMenuBuilder addItemWithType:EventMenuItemTypeQuote diff --git a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift index edd951fd6..f3b4d0bb7 100644 --- a/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift +++ b/Riot/Modules/Room/Views/WYSIWYGInputToolbar/WysiwygInputToolbarView.swift @@ -207,8 +207,14 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp } func mention(_ member: MXRoomMember) { - self.wysiwygViewModel.setMention(url: MXTools.permalinkToUser(withUserId: member.userId), - name: member.displayname, + guard let userId = member.userId else { + return + } + + let displayName = member.displayname ?? userId + + self.wysiwygViewModel.setMention(url: MXTools.permalinkToUser(withUserId: userId), + name: displayName, mentionType: .user) } @@ -373,7 +379,8 @@ class WysiwygInputToolbarView: MXKRoomInputToolbarView, NibLoadable, HtmlRoomInp } private func sendWysiwygMessage(content: WysiwygComposerContent) { - if content.markdown.prefix(while: { $0 == "/" }).count == 1 { + // bwi: #4955 disable WYSIWYG commands + if BWIBuildSettings.shared.enableWYSIWYGCommands && content.markdown.prefix(while: { $0 == "/" }).count == 1 { let commandText: String if content.markdown.hasPrefix(MXKSlashCommand.emote.cmd) { // `/me` command works with markdown content diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift index e26439230..350ce3e94 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift @@ -134,6 +134,8 @@ class VoiceMessageToolbarView: PassthroughView, NibLoadable, Themable, UIGesture self.tapGesture = tapGesture primaryRecordButton.accessibilityLabel = VectorL10n.roomAccessibilityRecordVoiceMessage primaryRecordButton.accessibilityHint = VectorL10n.roomAccessibilityRecordVoiceMessageHint + // bwi: #5017 translate voice message cancel text + slideToCancelLabel.text = BWIL10n.voiceMessageSlideToCancel updateUIWithDetails(VoiceMessageToolbarViewDetails(), animated: false) } diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift index 5d883def2..4f6f48e99 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinator.swift @@ -35,9 +35,9 @@ final class SecretsRecoveryWithKeyCoordinator: SecretsRecoveryWithKeyCoordinator // MARK: - Setup - init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, cancellable: Bool) { + init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, cancellable: Bool, dehydrationService: DehydrationService?) { - let secretsRecoveryWithKeyViewModel = SecretsRecoveryWithKeyViewModel(recoveryService: recoveryService, recoveryGoal: recoveryGoal) + let secretsRecoveryWithKeyViewModel = SecretsRecoveryWithKeyViewModel(recoveryService: recoveryService, recoveryGoal: recoveryGoal, dehydrationService: dehydrationService) let secretsRecoveryWithKeyViewController = SecretsRecoveryWithKeyViewController.instantiate(with: secretsRecoveryWithKeyViewModel, cancellable: cancellable) self.secretsRecoveryWithKeyViewController = secretsRecoveryWithKeyViewController self.secretsRecoveryWithKeyViewModel = secretsRecoveryWithKeyViewModel diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift index f56562f7d..c18222bd0 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModel.swift @@ -24,6 +24,8 @@ final class SecretsRecoveryWithKeyViewModel: SecretsRecoveryWithKeyViewModelType private let recoveryService: MXRecoveryService + private let dehydrationService: DehydrationService? + // MARK: Public let recoveryGoal: SecretsRecoveryGoal @@ -39,8 +41,9 @@ final class SecretsRecoveryWithKeyViewModel: SecretsRecoveryWithKeyViewModelType // MARK: - Setup - init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal) { + init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, dehydrationService: DehydrationService?) { self.recoveryService = recoveryService + self.dehydrationService = dehydrationService self.recoveryGoal = recoveryGoal } @@ -83,6 +86,10 @@ final class SecretsRecoveryWithKeyViewModel: SecretsRecoveryWithKeyViewModelType } self.update(viewState: .loaded) self.coordinatorDelegate?.secretsRecoveryWithKeyViewModelDidRecover(self) + + Task { + await self.dehydrationService?.runDeviceDehydrationFlow(privateKeyData: privateKey) + } }, failure: { [weak self] error in guard let self = self else { return diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift index 3c0ad1dc3..a53929d12 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinator.swift @@ -35,8 +35,8 @@ final class SecretsRecoveryWithPassphraseCoordinator: SecretsRecoveryWithPassphr // MARK: - Setup - init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, cancellable: Bool) { - let secretsRecoveryWithPassphraseViewModel = SecretsRecoveryWithPassphraseViewModel(recoveryService: recoveryService, recoveryGoal: recoveryGoal) + init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, cancellable: Bool, dehydrationService: DehydrationService?) { + let secretsRecoveryWithPassphraseViewModel = SecretsRecoveryWithPassphraseViewModel(recoveryService: recoveryService, recoveryGoal: recoveryGoal, dehydrationService: dehydrationService) let secretsRecoveryWithPassphraseViewController = SecretsRecoveryWithPassphraseViewController.instantiate(with: secretsRecoveryWithPassphraseViewModel, cancellable: cancellable) self.secretsRecoveryWithPassphraseViewController = secretsRecoveryWithPassphraseViewController self.secretsRecoveryWithPassphraseViewModel = secretsRecoveryWithPassphraseViewModel diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift index 71e51e270..dd05f8425 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModel.swift @@ -24,6 +24,8 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras private let recoveryService: MXRecoveryService + private let dehydrationService: DehydrationService? + // MARK: Public let recoveryGoal: SecretsRecoveryGoal @@ -39,8 +41,9 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras // MARK: - Setup - init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal) { + init(recoveryService: MXRecoveryService, recoveryGoal: SecretsRecoveryGoal, dehydrationService: DehydrationService?) { self.recoveryService = recoveryService + self.dehydrationService = dehydrationService self.recoveryGoal = recoveryGoal } @@ -103,6 +106,10 @@ final class SecretsRecoveryWithPassphraseViewModel: SecretsRecoveryWithPassphras } self.update(viewState: .loaded) self.coordinatorDelegate?.secretsRecoveryWithPassphraseViewModelDidRecover(self) + + Task { + await self.dehydrationService?.runDeviceDehydrationFlow(privateKeyData: privateKey) + } }, failure: { [weak self] error in guard let self = self else { return diff --git a/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift index 593f10139..315e19721 100644 --- a/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift +++ b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinator.swift @@ -86,14 +86,28 @@ final class SecretsRecoveryCoordinator: SecretsRecoveryCoordinatorType { // MARK: - Private + private var dehydrationService: DehydrationService? { + if self.session.vc_homeserverConfiguration().encryption.deviceDehydrationEnabled { + return self.session.crypto.dehydrationService + } + + return nil + } + private func createRecoverFromKeyCoordinator() -> SecretsRecoveryWithKeyCoordinator { - let coordinator = SecretsRecoveryWithKeyCoordinator(recoveryService: self.session.crypto.recoveryService, recoveryGoal: self.recoveryGoal, cancellable: self.cancellable) + let coordinator = SecretsRecoveryWithKeyCoordinator(recoveryService: self.session.crypto.recoveryService, + recoveryGoal: self.recoveryGoal, + cancellable: self.cancellable, + dehydrationService: dehydrationService) coordinator.delegate = self return coordinator } private func createRecoverFromPassphraseCoordinator() -> SecretsRecoveryWithPassphraseCoordinator { - let coordinator = SecretsRecoveryWithPassphraseCoordinator(recoveryService: self.session.crypto.recoveryService, recoveryGoal: self.recoveryGoal, cancellable: self.cancellable) + let coordinator = SecretsRecoveryWithPassphraseCoordinator(recoveryService: self.session.crypto.recoveryService, + recoveryGoal: self.recoveryGoal, + cancellable: self.cancellable, + dehydrationService: dehydrationService) coordinator.delegate = self return coordinator } diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift index 58ebf79ba..9dc055e49 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinator.swift @@ -42,8 +42,13 @@ final class SecretsSetupRecoveryKeyCoordinator: SecretsSetupRecoveryKeyCoordinat passphrase: String?, passphraseOnly: Bool, allowOverwrite: Bool = false, - cancellable: Bool) { - let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly, allowOverwrite: allowOverwrite) + cancellable: Bool, + dehydrationService: DehydrationService?) { + let secretsSetupRecoveryKeyViewModel = SecretsSetupRecoveryKeyViewModel(recoveryService: recoveryService, + passphrase: passphrase, + passphraseOnly: passphraseOnly, + allowOverwrite: allowOverwrite, + dehydrationService: dehydrationService) let secretsSetupRecoveryKeyViewController = SecretsSetupRecoveryKeyViewController.instantiate(with: secretsSetupRecoveryKeyViewModel, cancellable: cancellable) self.secretsSetupRecoveryKeyViewModel = secretsSetupRecoveryKeyViewModel self.secretsSetupRecoveryKeyViewController = secretsSetupRecoveryKeyViewController diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift index af85b7eb7..c91cb6787 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModel.swift @@ -28,6 +28,7 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy private let passphrase: String? private let passphraseOnly: Bool private let allowOverwrite: Bool + private let dehydrationService: DehydrationService? // MARK: Public @@ -36,11 +37,12 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy // MARK: - Setup - init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool, allowOverwrite: Bool = false) { + init(recoveryService: MXRecoveryService, passphrase: String?, passphraseOnly: Bool, allowOverwrite: Bool = false, dehydrationService: DehydrationService?) { self.recoveryService = recoveryService self.passphrase = passphrase self.passphraseOnly = passphraseOnly self.allowOverwrite = allowOverwrite + self.dehydrationService = dehydrationService } // MARK: - Public @@ -76,6 +78,10 @@ final class SecretsSetupRecoveryKeyViewModel: SecretsSetupRecoveryKeyViewModelTy self.recoveryService.createRecovery(forSecrets: nil, withPassphrase: self.passphrase, createServicesBackups: true, success: { secretStorageKeyCreationInfo in self.update(viewState: .recoveryCreated(secretStorageKeyCreationInfo.recoveryKey)) + + Task { + await self.dehydrationService?.runDeviceDehydrationFlow(privateKeyData: secretStorageKeyCreationInfo.privateKey) + } }, failure: { error in self.update(viewState: .error(error)) }) diff --git a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift index d58444206..b3a5b06d5 100644 --- a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift +++ b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinator.swift @@ -104,9 +104,22 @@ final class SecureBackupSetupCoordinator: SecureBackupSetupCoordinatorType { introViewController.delegate = self return introViewController } - + + private var dehydrationService: DehydrationService? { + if self.session.vc_homeserverConfiguration().encryption.deviceDehydrationEnabled { + return self.session.crypto.dehydrationService + } + + return nil + } + private func showSetupKey(passphraseOnly: Bool, passphrase: String? = nil) { - let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, passphrase: passphrase, passphraseOnly: passphraseOnly, allowOverwrite: allowOverwrite, cancellable: self.cancellable) + let coordinator = SecretsSetupRecoveryKeyCoordinator(recoveryService: self.recoveryService, + passphrase: passphrase, + passphraseOnly: passphraseOnly, + allowOverwrite: allowOverwrite, + cancellable: self.cancellable, + dehydrationService: dehydrationService) coordinator.delegate = self coordinator.start() diff --git a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m index 74128bff0..7b93bd843 100644 --- a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m +++ b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m @@ -676,6 +676,53 @@ enum { } - (void)removeDevice +{ + MXWellKnownAuthentication *authentication = self.mainSession.homeserverWellknown.authentication; + if (BWIBuildSettings.shared.isOIDCEnabled && + authentication) + { + NSURL *logoutURL = [authentication getLogoutDeviceURLFromID:device.deviceId]; + if (logoutURL) + { + [self removeDeviceRedirectWithURL:logoutURL]; + } + else + { + [self showRemoveDeviceRedirectError]; + } + } + else + { + [self removeDeviceThroughAPI]; + } +} + +-(void) removeDeviceRedirectWithURL: (NSURL * _Nonnull) url +{ + UIAlertController *alert = [UIAlertController alertControllerWithTitle: [VectorL10n manageSessionRedirect] message: nil preferredStyle:UIAlertControllerStyleAlert]; + + __weak typeof(self) weakSelf = self; + UIAlertAction *action = [UIAlertAction actionWithTitle:[VectorL10n ok] + style:UIAlertActionStyleDefault + handler: ^(UIAlertAction * action) { + [UIApplication.sharedApplication openURL:url options:@{} completionHandler:^(BOOL success) { + if (success && weakSelf) + { + [weakSelf withdrawViewControllerAnimated:YES completion:nil]; + } + }]; + }]; + [alert addAction: action]; + [self presentViewController:alert animated:YES completion:nil]; +} + +-(void) showRemoveDeviceRedirectError +{ + UIAlertController *alert = [UIAlertController alertControllerWithTitle: [VectorL10n manageSessionRedirectError] message: nil preferredStyle:UIAlertControllerStyleAlert]; + [self presentViewController:alert animated:YES completion:nil]; +} + +-(void) removeDeviceThroughAPI { [self startActivityIndicator]; self.view.userInteractionEnabled = NO; diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 3d6002e74..66f8afe94 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -55,6 +55,7 @@ typedef NS_ENUM(NSUInteger, SECTION_TAG) SECTION_TAG_USER_SETTINGS, SECTION_TAG_NEW_FEATURES, SECTION_TAG_LOCATION_SHARING, + SECTION_TAG_ACCOUNT, SECTION_TAG_SENDING_MEDIA, SECTION_TAG_LINKS, SECTION_TAG_SECURITY, @@ -171,7 +172,8 @@ typedef NS_ENUM(NSUInteger, USER_INTERFACE) TIMELINE_USER_AVATAR_INDEX, TIMELINE_ENTER_ROOM_INDEX, USER_INTERFACE_LANGUAGE_INDEX, - USER_INTERFACE_TIMELINE_STYLE_INDEX + USER_INTERFACE_TIMELINE_STYLE_INDEX, + PERSONAL_NOTES_INDEX }; typedef NS_ENUM(NSUInteger, TIMELINE) @@ -252,6 +254,11 @@ enum OUTDATED_WARNING = 0 }; +typedef NS_ENUM(NSUInteger, ACCOUNT) +{ + ACCOUNT_MANAGE_INDEX = 0, +}; + typedef void (^blockSettingsViewController_onReadyToDestroy)(void); #pragma mark - SettingsViewController @@ -466,7 +473,11 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [sectionUserSettings addRowWithTag: USER_SETTINGS_PHONENUMBERS_OFFSET + index]; } } - if (BWIBuildSettings.shared.settingsScreenAllowAddingEmailThreepids) + if (BWIBuildSettings.shared.settingsScreenAllowAddingEmailThreepids && + BuildSettings.settingsScreenAllowAddingEmailThreepids && + // If the threePidChanges is nil we assume the capability to be true + (!self.mainSession.homeserverCapabilities.threePidChanges || + self.mainSession.homeserverCapabilities.threePidChanges.enabled)) { [sectionUserSettings addRowWithTag:USER_SETTINGS_ADD_EMAIL_INDEX]; } @@ -493,7 +504,19 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [tmpSections addObject:sectionNewFeatures]; } - if (BWIBuildSettings.shared.settingsScreenShowConfirmMediaSize) + NSString *manageAccountURL = self.mainSession.homeserverWellknown.authentication.account; + if (BWIBuildSettings.shared.isOIDCEnabled && + manageAccountURL) + { + Section *account = [Section sectionWithTag: SECTION_TAG_ACCOUNT]; + [account addRowWithTag:ACCOUNT_MANAGE_INDEX]; + account.headerTitle = [VectorL10n settingsManageAccountTitle]; + account.footerTitle = [VectorL10n settingsManageAccountDescription:manageAccountURL]; + [tmpSections addObject:account]; + } + + if (BWIBuildSettings.shared.settingsScreenShowConfirmMediaSize && + BuildSettings.settingsScreenShowConfirmMediaSize) { Section *sectionMedia = [Section sectionWithTag:SECTION_TAG_SENDING_MEDIA]; [sectionMedia addRowWithTag:SENDING_MEDIA_CONFIRM_SIZE]; @@ -644,6 +667,9 @@ ChangePasswordCoordinatorBridgePresenterDelegate> //[sectionUserInterface addRowWithTag:USER_INTERFACE_LANGUAGE_INDEX]; [sectionUserInterface addRowWithTag:USER_INTERFACE_THEME_INDEX]; + if (BWIBuildSettings.shared.bwiPersonalNotesVisibilityInSettings) { + [sectionUserInterface addRowWithTag:PERSONAL_NOTES_INDEX]; + } sectionUserInterface = [self updateTimeLine:sectionUserInterface]; [tmpSections addObject: sectionUserInterface]; @@ -733,7 +759,11 @@ ChangePasswordCoordinatorBridgePresenterDelegate> { [sectionAbout addRowWithTag:ABOUT_ACCEPTABLE_USE_INDEX]; } - if (BWIBuildSettings.shared.applicationPrivacyPolicyUrlString.length) + if (self.mainSession.homeserverWellknown.dataPrivacyURL != nil && BWIBuildSettings.shared.bwiUseWellKnownPrivacyPolicyLink) + { + [sectionAbout addRowWithTag:ABOUT_PRIVACY_INDEX]; + } + else if (BWIBuildSettings.shared.applicationPrivacyPolicyUrlString.length && !BWIBuildSettings.shared.bwiUseWellKnownPrivacyPolicyLink) { [sectionAbout addRowWithTag:ABOUT_PRIVACY_INDEX]; } @@ -779,18 +809,17 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [sectionLabs addRowWithTag:LABS_ENABLE_LIVE_LOCATION_SHARING]; */ - if (BWIBuildSettings.shared.enableLabFeatureWYSIWYG) { + /* bwi: #4941 WYSIWYG is now live if (@available(iOS 15.0, *)) { [sectionLabs addRowWithTag:LABS_ENABLE_WYSIWYG_COMPOSER]; } - } + */ - // bwi: disabled for our apps - if (BWIBuildSettings.shared.enableLabFeatureVoiceBroadcasts) + /* bwi: #4941 disabled for our apps { [sectionLabs addRowWithTag:LABS_ENABLE_VOICE_BROADCAST]; - } + */ sectionLabs.headerTitle = [VectorL10n settingsLabs]; if (sectionLabs.hasAnyRows) { @@ -798,14 +827,15 @@ ChangePasswordCoordinatorBridgePresenterDelegate> } } - if (BWIBuildSettings.shared.settingsScreenAllowDeactivatingAccount) + if (BWIBuildSettings.shared.settingsScreenAllowDeactivatingAccount && !BWIBuildSettings.shared.isOIDCEnabled && + BuildSettings.settingsScreenAllowDeactivatingAccount && !self.mainSession.homeserverWellknown.authentication) { Section *sectionDeactivate = [Section sectionWithTag:SECTION_TAG_DEACTIVATE_ACCOUNT]; [sectionDeactivate addRowWithTag:0]; sectionDeactivate.headerTitle = [VectorL10n settingsDeactivateMyAccount]; [tmpSections addObject:sectionDeactivate]; } - + Section *sectionSignOut = [Section sectionWithTag:SECTION_TAG_SIGN_OUT]; [sectionSignOut addRowWithTag:0]; [tmpSections addObject:sectionSignOut]; @@ -2537,6 +2567,9 @@ ChangePasswordCoordinatorBridgePresenterDelegate> } else if (row == USER_INTERFACE_TIMELINE_STYLE_INDEX) { cell = [self buildMessageBubblesCellForTableView:tableView atIndexPath:indexPath]; + } else if (row == PERSONAL_NOTES_INDEX) + { + cell = [self cellForPersonalNotesVisibility:tableView indexPath:indexPath row:row]; } else { cell = [self cellForTimeline:tableView indexPath:indexPath row:row]; } @@ -3113,6 +3146,17 @@ ChangePasswordCoordinatorBridgePresenterDelegate> cell = showSettingsBtnCell; } } + else if (section == SECTION_TAG_ACCOUNT) + { + switch (row) + { + case ACCOUNT_MANAGE_INDEX: + cell = [self getDefaultTableViewCell:tableView]; + cell.textLabel.text = [VectorL10n settingsManageAccountAction]; + [cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme]; + break; + } + } return cell; } @@ -3375,7 +3419,14 @@ ChangePasswordCoordinatorBridgePresenterDelegate> } else if (row == ABOUT_PRIVACY_INDEX) { - [[UIApplication sharedApplication] openURL:[NSURL URLWithString:BWIBuildSettings.shared.applicationPrivacyPolicyUrlString] options:@{} completionHandler:nil]; + if (BWIBuildSettings.shared.bwiUseWellKnownPrivacyPolicyLink) + { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.mainSession.homeserverWellknown.dataPrivacyURL] options:@{} completionHandler:nil]; + } + else + { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:BWIBuildSettings.shared.applicationPrivacyPolicyUrlString] options:@{} completionHandler:nil]; + } } else if (row == ABOUT_ACCESSIBILITY_DECLARATION_INDEX) { @@ -3497,6 +3548,14 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [self pushViewController:webViewViewController]; } } + else if (section == SECTION_TAG_ACCOUNT) + { + switch(row) { + case ACCOUNT_MANAGE_INDEX: + [self onManageAccountTap]; + break; + } + } [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @@ -4435,6 +4494,14 @@ ChangePasswordCoordinatorBridgePresenterDelegate> } } +- (void)onManageAccountTap +{ + NSURL *url = [NSURL URLWithString: self.mainSession.homeserverWellknown.authentication.account]; + if (url) { + [UIApplication.sharedApplication openURL:url options:@{} completionHandler:nil]; + } +} + - (void)showThemePicker { __weak typeof(self) weakSelf = self; @@ -5359,6 +5426,24 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [self updateSections]; } +- (UITableViewCell*) cellForPersonalNotesVisibility:(UITableView*)tableView indexPath:(NSIndexPath*)indexPath row:(NSInteger)row { + MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; + + labelAndSwitchCell.mxkLabel.text = [BWIL10n bwiSettingsEnableNotesRoomTitle]; + labelAndSwitchCell.mxkSwitch.on = [[[PersonalNotesSettings alloc] init] personalNotesVisible]; + labelAndSwitchCell.mxkSwitch.thumbTintColor = ThemeService.shared.theme.backgroundColor; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; + labelAndSwitchCell.mxkSwitch.enabled = YES; + [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(togglePersonalNotesVisibility:) forControlEvents:UIControlEventTouchUpInside]; + + return labelAndSwitchCell; +} + +- (void) togglePersonalNotesVisibility:(UISwitch* )sender { + PersonalNotesSettings* settings = [[PersonalNotesSettings alloc] init]; + settings.personalNotesVisible = sender.on; +} + #pragma mark - ThreadsBetaCoordinatorBridgePresenterDelegate - (void)threadsBetaCoordinatorBridgePresenterDelegateDidTapEnable:(ThreadsBetaCoordinatorBridgePresenter *)coordinatorBridgePresenter diff --git a/Riot/Modules/TabBar/MasterTabBarController.m b/Riot/Modules/TabBar/MasterTabBarController.m index 3cdd0bfb9..72cb22927 100644 --- a/Riot/Modules/TabBar/MasterTabBarController.m +++ b/Riot/Modules/TabBar/MasterTabBarController.m @@ -1079,22 +1079,6 @@ if (BWIBuildSettings.shared.bwiPersonalNotesRoom) { [service createPersonalNotesRoomIfNeeded]; } - if (BWIBuildSettings.shared.bwiUseCustomPersonalNotesAvatar) { - MXSession *mainSession = mxSessionArray.firstObject; - - if (mainSession) { - for (MXRoom* room in [mainSession roomsWithTag:PersonalNotesDefaultService.roomTag]) { - if ([room.summary.avatar length] > 0) { - [room setAvatar:@"" success:nil failure:nil]; - } - } - - MXRoom* room = [mainSession roomWithRoomId:[service personalNotesRoomId]]; - if (room && [room.summary.avatar length] > 0) { - [room setAvatar:@"" success:nil failure:nil]; - } - } - } } #pragma mark - bwi matomo tracking @@ -1105,8 +1089,14 @@ message:[BWIL10n bwiAnalyticsAlertBody:AppInfo.current.displayName] preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:BWIL10n.bwiAnalyticsAlertInfoButton style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + [alert addAction:[UIAlertAction actionWithTitle:BWIL10n.bwiAnalyticsAlertInfoButton style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + if (BWIBuildSettings.shared.bwiUseWellKnownPrivacyPolicyLink) { + if (self->mxSessionArray.firstObject.homeserverWellknown.dataPrivacyURL != nil) { + [[UIApplication sharedApplication] openURL:[NSURL URLWithString:self->mxSessionArray.firstObject.homeserverWellknown.dataPrivacyURL] options:@{} completionHandler:nil]; + } + } else { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:BWIBuildSettings.shared.applicationPrivacyPolicyUrlString] options:@{} completionHandler:nil]; + } }]]; [alert addAction:[UIAlertAction actionWithTitle:BWIL10n.bwiAnalyticsAlertCancelButton style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { BWIAnalytics.sharedTracker.running = NO; diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index d144ac1db..66bb9bdfd 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -44,7 +44,7 @@ #import "ContactDetailsViewController.h" #import "RoomInputToolbarView.h" #import "NSArray+Element.h" -#import "ShareItemSender.h" +#import "ShareItemSenderProtocol.h" #import "Contact.h" #import "RoomTimelineCellProvider.h" #import "PlainRoomTimelineCellProvider.h" diff --git a/Riot/Utils/PasswordValidator.swift b/Riot/Utils/PasswordValidator.swift index 59c6e15d4..cb70c880e 100644 --- a/Riot/Utils/PasswordValidator.swift +++ b/Riot/Utils/PasswordValidator.swift @@ -71,7 +71,8 @@ enum PasswordValidatorRule: CustomStringConvertible, Hashable { case .containNumber: return password.range(of: "[0-9]", options: .regularExpression) != nil case .containSymbol: - return password.range(of: "[!\"#$%&'()*+,-.:;<=>?@\\_`{|}~\\[\\]]", + // bwi: #4952 support more special characters + return password.range(of: "[!\"#$§€%&'()*+,-.:;<=>?@\\_`{|}~\\[\\]]", options: .regularExpression) != nil } } diff --git a/Riot/target.yml b/Riot/target.yml index 791c2cb47..7ecc5fd7e 100644 --- a/Riot/target.yml +++ b/Riot/target.yml @@ -34,8 +34,10 @@ targets: platform: iOS dependencies: - - target: RiotShareExtension -# - target: SiriIntents + # Disabled due to crypto corruption issues. + # https://github.com/vector-im/element-ios/issues/7618 + # - target: RiotShareExtension + # - target: SiriIntents // BWI: disabled - target: RiotNSE - target: BroadcastUploadExtension - target: DesignKit diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index eb606666e..fb92033ce 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -994,7 +994,9 @@ class NotificationService: UNNotificationServiceExtension { return Constants.toBeRemovedNotificationCategoryIdentifier } - return "QUICK_REPLY" + // Don't return QUICK_REPLY here as there is an issue + // with crypto corruption when sending from extensions. + return nil } /// Attempts to send trigger a VoIP push for the given event diff --git a/RiotNSE/target.yml b/RiotNSE/target.yml index 8f65bf50d..67e644b4a 100644 --- a/RiotNSE/target.yml +++ b/RiotNSE/target.yml @@ -59,6 +59,7 @@ targets: - path: ../Config/BWIBuildSettings.swift - path: ../Config/BuM/BWIBuildSettings+BuM.swift - path: ../bwi/UserAgent/UserAgentService.swift + - path: ../bwi/LoginProtection/LoginProtectionService.swift - path: ../Riot/Utils/DataProtectionHelper.swift - path: ../Config/CommonConfiguration.swift - path: ../Riot/Experiments/ diff --git a/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift b/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift index 356bd5fd3..fba209da9 100644 --- a/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift +++ b/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginModels.swift @@ -88,11 +88,12 @@ struct AuthenticationLoginViewState: BindableState { hasValidCredentials && !isLoading } - var dataPrivacyString: String { - guard let urlString = AuthenticationService.shared.wellknown?.dataPrivacyURL() else { + var dataPrivacyString: String? { + if BWIBuildSettings.shared.bwiUseWellKnownPrivacyPolicyLink { + return AuthenticationService.shared.wellknown?.dataPrivacyURL() + } else { return BWIBuildSettings.shared.applicationPrivacyPolicyUrlString } - return urlString } } @@ -133,4 +134,6 @@ enum AuthenticationLoginErrorType: Hashable { case invalidHomeserver /// The response from the homeserver was unexpected. case unknown + /// Appversion is too old + case appVersion } diff --git a/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginViewModel.swift b/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginViewModel.swift index 07879621c..c3085dfa0 100644 --- a/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginViewModel.swift +++ b/RiotSwiftUI/Modules/Authentication/Login/AuthenticationLoginViewModel.swift @@ -80,6 +80,16 @@ class AuthenticationLoginViewModel: AuthenticationLoginViewModelType, Authentica message: VectorL10n.authenticationServerSelectionGenericError) case .unknown: state.bindings.alertInfo = AlertInfo(id: type) + case .appVersion: + state.bindings.alertInfo = AlertInfo(id: type, + title: BWIL10n.bwiDeprecatedVersionWarningTitle, + message: BWIL10n.bwiDeprecatedVersionWarningMessage, + primaryButton: (BWIL10n.bwiDeprecatedVersionAppstoreButton, { + + UIApplication.shared.open(URL(string: BWIBuildSettings.shared.itunesAppLink)!) + }), + secondaryButton: (VectorL10n.ok, {})) + } } diff --git a/RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift b/RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift index df7f688f4..764a42c21 100644 --- a/RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift +++ b/RiotSwiftUI/Modules/Authentication/Login/Coordinator/AuthenticationLoginCoordinator.swift @@ -130,7 +130,11 @@ final class AuthenticationLoginCoordinator: Coordinator, Presentable { self.showForgotPasswordScreen() } case .login(let username, let password): - self.login(username: username, password: password) + if BWIBuildSettings.shared.bwiCheckAppVersion && ValidAppVersionsDefaultService().isCurrentAppVersionDeprecated() { + self.authenticationLoginViewModel.displayError(.appVersion) + } else { + self.login(username: username, password: password) + } case .continueWithSSO(let identityProvider): self.callback?(.continueWithSSO(identityProvider)) case .fallback: @@ -191,14 +195,14 @@ final class AuthenticationLoginCoordinator: Coordinator, Presentable { @MainActor private func handleError(_ error: Error) { if let mxError = MXError(nsError: error as NSError) { let message = mxError.authenticationErrorMessage() - authenticationLoginViewModel.displayError(.mxError(message)) + self.authenticationLoginViewModel.displayError(.mxError(message)) return } if let authenticationError = error as? AuthenticationError { switch authenticationError { case .invalidHomeserver: - authenticationLoginViewModel.displayError(.invalidHomeserver) + self.authenticationLoginViewModel.displayError(.invalidHomeserver) case .loginFlowNotCalled: #warning("Reset the flow") case .missingMXRestClient: @@ -207,7 +211,7 @@ final class AuthenticationLoginCoordinator: Coordinator, Presentable { return } - authenticationLoginViewModel.displayError(.unknown) + self.authenticationLoginViewModel.displayError(.unknown) } @MainActor private func parseUsername(_ username: String) { diff --git a/RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift b/RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift index 60e1b2d35..fead5cd83 100644 --- a/RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift +++ b/RiotSwiftUI/Modules/Authentication/Login/View/AuthenticationLoginScreen.swift @@ -209,22 +209,31 @@ struct AuthenticationLoginScreen: View { } var dataPrivacyForm: some View { - Button(action: { - let tosURL = URL.init(string: viewModel.viewState.dataPrivacyString)! // add your link here - if UIApplication.shared.canOpenURL(tosURL) { - UIApplication.shared.open(tosURL) + VStack() { + if viewModel.viewState.dataPrivacyString != nil { + Button(action: { + guard let urlString = viewModel.viewState.dataPrivacyString else { + return + } + let tosURL = URL.init(string: urlString)! // add your link here + if UIApplication.shared.canOpenURL(tosURL) { + UIApplication.shared.open(tosURL) + } + }, label: { + Text(BWIL10n.authenticationDataprivacyText) + .font(theme.fonts.footnote) + .foregroundColor(theme.colors.primaryContent) + + + Text(BWIL10n.authenticationDataprivacyLink) + .font(theme.fonts.footnote) + .foregroundColor(.blue) + .underline() + }) + .padding([.horizontal], 20) + } else { + EmptyView() } - }, label: { - Text(BWIL10n.authenticationDataprivacyText) - .font(theme.fonts.footnote) - .foregroundColor(theme.colors.primaryContent) - + - Text(BWIL10n.authenticationDataprivacyLink) - .font(theme.fonts.footnote) - .foregroundColor(.blue) - .underline() - }) - .padding([.horizontal], 20) + } } var loginDescription: some View { diff --git a/RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift b/RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift index 43fc1d1eb..b42eb0eb3 100644 --- a/RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift +++ b/RiotSwiftUI/Modules/Authentication/ServerSelection/View/AuthenticationServerSelectionScreen.swift @@ -17,10 +17,6 @@ import SwiftUI import AVKit -enum AuthenticationServerSelectionScreenAlertType { - case showServerMaintenanceInfoMessageAlert, showServerMaintenanceDefaultAlert, showInvalidAppVersionAlert, showDowntimeTimeAlert, showInvalidServerAlert -} - struct AuthenticationServerSelectionScreen: View { // MARK: - Properties @@ -39,7 +35,8 @@ struct AuthenticationServerSelectionScreen: View { // bwi #4976 show maintenance alert @State private var isFetchingDowntime = false @State private var showAlert = false - @State private var activeAlert: AuthenticationServerSelectionScreenAlertType = .showInvalidAppVersionAlert + @State private var isInvalidServerAlert = false + @State private var activeAlert: ServerMaintenanceAlertType = .showInvalidAppVersionAlert private var textFieldFooterColor: Color { viewModel.viewState.hasValidationError ? theme.colors.alert : theme.colors.tertiaryContent @@ -98,67 +95,12 @@ struct AuthenticationServerSelectionScreen: View { } } .alert(isPresented: $showAlert, content: { - switch activeAlert { - case .showInvalidAppVersionAlert: - return Alert(title: Text(BWIL10n.bwiOutdatedVersionWarningTitle), - message: Text(BWIL10n.bwiOutdatedVersionWarningMessage(AppInfo.current.displayName)), - dismissButton: .destructive(Text(BWIL10n.bwiOutdatedVersionAppstoreButton), action: { - let iTunesLink = BWIBuildSettings.shared.itunesAppLink - UIApplication.shared.open(URL(string: iTunesLink)!, options: [:], completionHandler: nil) - })) - case .showDowntimeTimeAlert: - if BWIBuildSettings.shared.ignoreBlockingMaintenance && service.isBlocking() { - return Alert( title: Text(""), - message: Text(ServerDowntimeDefaultService().downtimeText()), - primaryButton: .cancel(Text(BWIL10n.blockingDowntimeAlertIgnoreButton)) { - UserDefaults.standard.set(false, forKey: "ServerDownTimeBlockingKey") - service.setManuallyIgnored() - self.submit() - }, - secondaryButton: .destructive(Text(BWIL10n.blockingDowntimeAlertDismissButton)) - ) - - } else { - return Alert(title: Text(BWIL10n.downtimeTitle), - message: Text(ServerDowntimeDefaultService().downtimeText() != "" ? BWIL10n.downtimeDefaultMessage + "\n\n" + ServerDowntimeDefaultService().downtimeText() : BWIL10n.downtimeDefaultMessage), - dismissButton: .destructive(Text(service.isBlocking() ? BWIL10n.blockingDowntimeAlertDismissButton : BWIL10n.downtimeAlertDismissButton)) { - if service.isBlocking() { - return - } else { - self.submit() - } - }) - } - - case .showServerMaintenanceInfoMessageAlert: - if BWIBuildSettings.shared.ignoreBlockingMaintenance && service.isBlocking() { - return Alert( title: Text(""), - message: Text(ServerDowntimeDefaultService().downtimeText()), - primaryButton: .cancel(Text(BWIL10n.blockingDowntimeAlertIgnoreButton)) { - self.submit() - }, - secondaryButton: .destructive(Text(BWIL10n.blockingDowntimeAlertDismissButton)) - ) - - } else { - return Alert(title: Text(""), - message: Text(ServerDowntimeDefaultService().downtimeText()), - dismissButton: .destructive(Text(service.isBlocking() ? BWIL10n.blockingDowntimeAlertDismissButton : BWIL10n.downtimeAlertDismissButton)) { - if service.isBlocking() { - return - } else { - self.submit() - } - }) - } - case .showServerMaintenanceDefaultAlert: - return Alert(title: Text(BWIL10n.downtimeTitle), - message: Text(BWIL10n.downtimeDefaultMessage), - dismissButton: .destructive(Text(BWIL10n.downtimeAlertDismissButton)) { - self.submit() - }) - case .showInvalidServerAlert: + if isInvalidServerAlert { return self.invalidServerAlert() + } else { + return service.alert(alertType: activeAlert) { + self.submit() + } } }) @@ -274,7 +216,7 @@ struct AuthenticationServerSelectionScreen: View { if isHomeserverAddressValid(viewModel.homeserverAddress) { viewModel.send(viewAction: .confirm) } else { - activeAlert = .showInvalidServerAlert + isInvalidServerAlert = true showAlert = true } } @@ -362,18 +304,11 @@ struct AuthenticationServerSelectionScreen: View { } private func showAlertIfNeeded() { - switch service.nextDowntimeStatus() { - case .none, .warning: - self.submit() - break - case .ongoing: - if service.downtimeType() == .adhocMessage { - activeAlert = .showServerMaintenanceInfoMessageAlert - } else { - activeAlert = .showDowntimeTimeAlert - } + if service.showAlert() { + activeAlert = service.alertType() showAlert = true - break + } else { + self.submit() } } } diff --git a/RiotSwiftUI/Modules/Common/Avatar/Service/MatrixSDK/AvatarService.swift b/RiotSwiftUI/Modules/Common/Avatar/Service/MatrixSDK/AvatarService.swift index ea4488244..743fec34f 100644 --- a/RiotSwiftUI/Modules/Common/Avatar/Service/MatrixSDK/AvatarService.swift +++ b/RiotSwiftUI/Modules/Common/Avatar/Service/MatrixSDK/AvatarService.swift @@ -29,10 +29,10 @@ class AvatarService: AvatarServiceProtocol { static let mimeType = "image/jpeg" static let thumbnailMethod = MXThumbnailingMethodCrop } + // bwi: #5133 fix crash: make mediaManager optional + private let mediaManager: MXMediaManager? - private let mediaManager: MXMediaManager - - init(mediaManager: MXMediaManager) { + init(mediaManager: MXMediaManager?) { self.mediaManager = mediaManager } @@ -59,7 +59,7 @@ class AvatarService: AvatarServiceProtocol { promise(.success(imageUp)) } - self.mediaManager.downloadThumbnail( + self.mediaManager?.downloadThumbnail( fromMatrixContentURI: mxContentUri, withType: Constants.mimeType, inFolder: nil, diff --git a/RiotSwiftUI/Modules/Common/Util/ListBackground.swift b/RiotSwiftUI/Modules/Common/Util/ListBackground.swift index d4e087da8..ddfc362c2 100644 --- a/RiotSwiftUI/Modules/Common/Util/ListBackground.swift +++ b/RiotSwiftUI/Modules/Common/Util/ListBackground.swift @@ -49,10 +49,4 @@ extension View { func listBackgroundColor(_ color: Color) -> some View { modifier(ListBackgroundModifier(color: color)) } - - /// Finds a `UICollectionView` from a `SwiftUI.List`, or `SwiftUI.List` child. - /// Stop gap until https://github.com/siteline/SwiftUI-Introspect/pull/169 - func introspectCollectionView(customize: @escaping (UICollectionView) -> Void) -> some View { - introspect(selector: TargetViewSelector.ancestorOrSiblingContaining, customize: customize) - } } diff --git a/RiotSwiftUI/Modules/Room/CompletionSuggestion/Service/CompletionSuggestionService.swift b/RiotSwiftUI/Modules/Room/CompletionSuggestion/Service/CompletionSuggestionService.swift index 5ded36c2c..c52af9506 100644 --- a/RiotSwiftUI/Modules/Room/CompletionSuggestion/Service/CompletionSuggestionService.swift +++ b/RiotSwiftUI/Modules/Room/CompletionSuggestion/Service/CompletionSuggestionService.swift @@ -128,7 +128,12 @@ class CompletionSuggestionService: CompletionSuggestionServiceProtocol { items.send([]) currentTextTriggerSubject.send(nil) case .slash: - currentTextTriggerSubject.send(TextTrigger(key: .slash, text: suggestionPattern.text)) + // bwi: #4955 disable WYSIWYG commands + if BWIBuildSettings.shared.enableWYSIWYGCommands { + currentTextTriggerSubject.send(TextTrigger(key: .slash, text: suggestionPattern.text)) + } else { + break + } } } diff --git a/RiotSwiftUI/Modules/Room/Composer/CreateActionList/View/ComposerCreateActionList.swift b/RiotSwiftUI/Modules/Room/Composer/CreateActionList/View/ComposerCreateActionList.swift index 706c2f1d9..d2852cd77 100644 --- a/RiotSwiftUI/Modules/Room/Composer/CreateActionList/View/ComposerCreateActionList.swift +++ b/RiotSwiftUI/Modules/Room/Composer/CreateActionList/View/ComposerCreateActionList.swift @@ -39,7 +39,7 @@ struct ComposerCreateActionList: View { HStack(spacing: 16) { Image(action.icon) .renderingMode(.template) - .foregroundColor(theme.colors.accent) + .foregroundColor(Color(ThemeService.shared().theme.tintColor)) // bwi: #5160 color changes Text(action.title) .foregroundColor(theme.colors.primaryContent) .font(theme.fonts.body) @@ -58,7 +58,7 @@ struct ComposerCreateActionList: View { HStack(spacing: 16) { Image(textFormattingIcon) .renderingMode(.template) - .foregroundColor(theme.colors.accent) + .foregroundColor(Color(ThemeService.shared().theme.tintColor)) // bwi: #5160 color changes Text(VectorL10n.wysiwygComposerStartActionTextFormatting) .foregroundColor(theme.colors.primaryContent) .font(theme.fonts.body) @@ -66,7 +66,7 @@ struct ComposerCreateActionList: View { Spacer() Toggle("", isOn: $viewModel.textFormattingEnabled) .labelsHidden() - .toggleStyle(SwitchToggleStyle(tint: theme.colors.accent)) + .toggleStyle(SwitchToggleStyle(tint: Color(ThemeService.shared().theme.tintColor))) // bwi: #5160 color changes .onChange(of: viewModel.textFormattingEnabled) { isOn in viewModel.send(viewAction: .toggleTextFormatting(isOn)) } diff --git a/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordModels.swift b/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordModels.swift index 6796dac14..5a4a27606 100644 --- a/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordModels.swift +++ b/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordModels.swift @@ -68,6 +68,8 @@ enum ChangePasswordViewAction { } enum ChangePasswordErrorType: Hashable { + /// The password has been changed on the server + case passwordChangeSucceeded /// An error response from the homeserver. case mxError(String) /// User entered new passwords do not match diff --git a/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordViewModel.swift b/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordViewModel.swift index 6cfcea0e6..e9d0125c8 100644 --- a/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordViewModel.swift +++ b/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordViewModel.swift @@ -49,19 +49,25 @@ class ChangePasswordViewModel: ChangePasswordViewModelType, ChangePasswordViewMo switch viewAction { case .submit: guard state.bindings.newPassword1 == state.bindings.newPassword2 else { - Task { await displayError(.passwordsDontMatch) } + Task { await displayError(.passwordsDontMatch, primaryButtonCallback: {}) } return } Task { await callback?(.submit(oldPassword: state.bindings.oldPassword, newPassword: state.bindings.newPassword1, - signoutAllDevices: state.bindings.signoutAllDevices)) } + signoutAllDevices: state.bindings.signoutAllDevices)) + } case .toggleSignoutAllDevices: state.bindings.signoutAllDevices.toggle() } } - @MainActor func displayError(_ type: ChangePasswordErrorType) { + @MainActor func displayError(_ type: ChangePasswordErrorType, primaryButtonCallback: @escaping (() -> Void)) { switch type { + case .passwordChangeSucceeded: + state.bindings.alertInfo = AlertInfo(id: .passwordChangeSucceeded, + title: BWIL10n.settingsPasswordChanged, + message: "", + primaryButton: (VectorL10n.ok, action: { primaryButtonCallback() })) case .mxError(let message): state.bindings.alertInfo = AlertInfo(id: type, title: VectorL10n.error, diff --git a/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordViewModelProtocol.swift b/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordViewModelProtocol.swift index db6848c9c..1c9ce23d9 100644 --- a/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordViewModelProtocol.swift +++ b/RiotSwiftUI/Modules/Settings/ChangePassword/ChangePasswordViewModelProtocol.swift @@ -21,5 +21,5 @@ protocol ChangePasswordViewModelProtocol { var context: ChangePasswordViewModelType.Context { get } /// Display an error to the user. - @MainActor func displayError(_ type: ChangePasswordErrorType) + @MainActor func displayError(_ type: ChangePasswordErrorType, primaryButtonCallback: @escaping (() -> Void)) } diff --git a/RiotSwiftUI/Modules/Settings/ChangePassword/Coordinator/ChangePasswordCoordinator.swift b/RiotSwiftUI/Modules/Settings/ChangePassword/Coordinator/ChangePasswordCoordinator.swift index ef6f8a16d..522b082a3 100644 --- a/RiotSwiftUI/Modules/Settings/ChangePassword/Coordinator/ChangePasswordCoordinator.swift +++ b/RiotSwiftUI/Modules/Settings/ChangePassword/Coordinator/ChangePasswordCoordinator.swift @@ -110,13 +110,18 @@ final class ChangePasswordCoordinator: Coordinator, Presentable { currentTask = Task { [weak self] in do { - try passwordValidator.validate(password: newPassword) - try await parameters.restClient.changePassword(from: oldPassword, to: newPassword, logoutDevices: signoutAllDevices) + try self?.passwordValidator.validate(password: newPassword) + try await self?.parameters.restClient.changePassword(from: oldPassword, to: newPassword, logoutDevices: signoutAllDevices) guard !Task.isCancelled else { return } self?.stopLoading() - self?.callback?() + + if BWIBuildSettings.shared.showPasswordChangedConfirmation { + self?.handleSuccess() + } else { + self?.callback?() + } } catch { self?.stopLoading() self?.handleError(error) @@ -128,14 +133,21 @@ final class ChangePasswordCoordinator: Coordinator, Presentable { @MainActor private func handleError(_ error: Error) { if let mxError = MXError(nsError: error as NSError) { let message = mxError.authenticationErrorMessage() - changePasswordViewModel.displayError(.mxError(message)) + changePasswordViewModel.displayError(.mxError(message), primaryButtonCallback: {}) return } if let error = error as? PasswordValidatorError { - changePasswordViewModel.displayError(.mxError(error.localizedDescription)) + changePasswordViewModel.displayError(.mxError(error.localizedDescription), primaryButtonCallback: {}) } else { - changePasswordViewModel.displayError(.unknown) + changePasswordViewModel.displayError(.unknown, primaryButtonCallback: {}) } } + + // bwi: 4951 - password changed confirmation + @MainActor private func handleSuccess() { + changePasswordViewModel.displayError(.passwordChangeSucceeded, primaryButtonCallback: { [weak self] in + self?.callback?() + }) + } } diff --git a/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift b/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift index 17361759b..4c6de9815 100644 --- a/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift +++ b/RiotSwiftUI/Modules/UserSessions/Coordinator/UserSessionsFlowCoordinator.swift @@ -120,17 +120,29 @@ final class UserSessionsFlowCoordinator: NSObject, Coordinator, Presentable { case let .renameSession(sessionInfo): self.showRenameSessionScreen(for: sessionInfo) case let .logoutOfSession(sessionInfo): - if sessionInfo.isCurrent { - self.showLogoutConfirmationForCurrentSession() - } else { - self.showLogoutConfirmation(for: [sessionInfo]) - } + self.handleLogoutOfSession(sessionInfo: sessionInfo) case let .showSessionStateInfo(sessionInfo): self.showInfoSheet(parameters: .init(userSessionInfo: sessionInfo, parentSize: self.toPresentable().view.bounds.size)) } } pushScreen(with: coordinator) } + + private func handleLogoutOfSession(sessionInfo: UserSessionInfo) { + if sessionInfo.isCurrent { + self.showLogoutConfirmationForCurrentSession() + } else { + if let authentication = self.parameters.session.homeserverWellknown.authentication, BWIBuildSettings.shared.isOIDCEnabled { + if let logoutURL = authentication.getLogoutDeviceURL(fromID: sessionInfo.id) { + self.openDeviceLogoutRedirectURL(logoutURL) + } else { + self.showDeviceLogoutRedirectError() + } + } else { + self.showLogoutConfirmation(for: [sessionInfo]) + } + } + } /// Shows the QR login screen. private func openQRLoginScreen() { @@ -182,6 +194,26 @@ final class UserSessionsFlowCoordinator: NSObject, Coordinator, Presentable { return UserOtherSessionsCoordinator(parameters: parameters) } + private func openDeviceLogoutRedirectURL(_ url: URL) { + let alert = UIAlertController(title: VectorL10n.manageSessionRedirect, message: nil, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: VectorL10n.ok, style: .default) { [weak self] _ in + UIApplication.shared.open(url) { [weak self] success in + guard success else { + return + } + self?.popToSessionsOverview() + } + }) + alert.popoverPresentationController?.sourceView = toPresentable().view + navigationRouter.present(alert, animated: true) + } + + private func showDeviceLogoutRedirectError() { + let alert = UIAlertController(title: VectorL10n.manageSessionRedirectError, message: nil, preferredStyle: .alert) + alert.popoverPresentationController?.sourceView = toPresentable().view + navigationRouter.present(alert, animated: true) + } + /// Shows a confirmation dialog to the user to sign out of a session. private func showLogoutConfirmation(for sessionInfos: [UserSessionInfo]) { // Use a UIAlertController as we don't have confirmationDialog in SwiftUI on iOS 14. diff --git a/bwi/AppConfig/AppConfigService.swift b/bwi/AppConfig/AppConfigService.swift index fbbbf517d..c3357464d 100644 --- a/bwi/AppConfig/AppConfigService.swift +++ b/bwi/AppConfig/AppConfigService.swift @@ -35,7 +35,7 @@ extension UserDefaults @objcMembers class AppConfigService : NSObject { static let shared = AppConfigService() - private let serverUrlKey = "serverUrl" + private let serverUrlKey = "home_server_url" private let contentScannerKey = "contentScanner" private let pusherUrlKey = "pusherUrl" private let permalinkUrlKey = "permalinkUrl" @@ -50,10 +50,14 @@ extension UserDefaults var session: MXSession? + var isAppConfig: Bool + private func loadAppConfig() { + isAppConfig = false do { if let dataIn = UserDefaults.standard.value(forKey: savedConfig) as? Data { appConfig = try JSONDecoder().decode(AppConfig.self, from: dataIn) + isAppConfig = true } } catch { @@ -70,17 +74,18 @@ extension UserDefaults } private func checkUrlSavety(_ serverUrl: String) -> Bool { - if serverUrl.hasSuffix("bwi.de") - || serverUrl.hasSuffix("example.com") - || serverUrl.hasSuffix("example.com") - || serverUrl.hasSuffix("example.com") { - return true + if BWIBuildSettings.shared.bwiEnableLoginProtection { + let protectionService = LoginProtectionService() + protectionService.hashes = BWIBuildSettings.shared.bwiHashes + + return protectionService.isValid(serverUrl) } else { - return false + return true } } override init() { + isAppConfig = false super.init() self.loadAppConfig() } @@ -93,12 +98,13 @@ extension UserDefaults } func handleAppConfig() { - if let dict = UserDefaults.standard.dictionary(forKey: configKey) { var config = AppConfig() if let serverUrl = dict[serverUrlKey] as? String { - if checkUrlSavety(serverUrl) { + if serverUrl.count == 0 { + config.serverUrl = nil + } else if checkUrlSavety(serverUrl) { config.serverUrl = serverUrl } } @@ -118,6 +124,9 @@ extension UserDefaults } } + // app config needs at least a valid server url + isAppConfig = (config.serverUrl != nil) + if config != appConfig { appConfig = config self.saveAppConfig() @@ -125,6 +134,8 @@ extension UserDefaults completion(true) } } + + UserDefaults.standard.removeObject(forKey: configKey) } } diff --git a/bwi/DeveloperSettings/DeveloperSettingsView.swift b/bwi/DeveloperSettings/DeveloperSettingsView.swift index 7fb41e534..25a790b1e 100644 --- a/bwi/DeveloperSettings/DeveloperSettingsView.swift +++ b/bwi/DeveloperSettings/DeveloperSettingsView.swift @@ -94,6 +94,33 @@ struct DeveloperSettingsView: View { } } } + SwiftUI.Section { + NavigationLink { + WellKnownView(session: session) + } label: { + Text(BWIL10n.bwiSettingsDeveloperWellKnown) + } + NavigationLink { + ServerCapabilitiesView(session: session) + } label: { + Text(BWIL10n.bwiSettingsDeveloperCapabilities) + } + NavigationLink { + MaintenanceView(session: session) + } label: { + Text(BWIL10n.bwiSettingsDeveloperMaintenance) + } + NavigationLink { + UserAccountDataView(session: session) + } label: { + Text(BWIL10n.bwiSettingsDeveloperUserAccountData) + } + NavigationLink { + ServerSideKeyBackupView(session: session) + } label: { + Text(BWIL10n.bwiSettingsDeveloperKeyBackup) + } + } } .listStyle(.grouped) .navigationTitle(BWIL10n.bwiSettingsDeveloper) diff --git a/bwi/DeveloperSettings/MaintenanceView.swift b/bwi/DeveloperSettings/MaintenanceView.swift new file mode 100644 index 000000000..236110943 --- /dev/null +++ b/bwi/DeveloperSettings/MaintenanceView.swift @@ -0,0 +1,73 @@ +// +/* + * Copyright (c) 2022 BWI GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import SwiftUI + +struct MaintenanceView: View { + @State var jsonString = "" + var session: MXSession? + + var body: some View { + ScrollView { + VStack(alignment: .leading) { + HStack { + Text(jsonString) + .font(.caption) + .padding() + Spacer() + } + } + } + .navigationTitle(BWIL10n.bwiSettingsDeveloperMaintenance) + .navigationBarTitleDisplayMode(.inline) + .onAppear { + Task { + await fetchData() + } + } + } + + private func fetchData() async { + guard let restClient = session?.matrixRestClient else { + return + } + + let responseDict = await withCheckedContinuation { continuation in + restClient.getDowntime { jsonResponse, error in + continuation.resume(returning: jsonResponse) + } + } + await MainActor.run { + do { + if let responseDict = responseDict { + let data = try JSONSerialization.data(withJSONObject: responseDict as Any, options: [.prettyPrinted]) + jsonString = String(data: data, encoding: .utf8) ?? "" + } else { + jsonString = "" + } + } catch { + jsonString = "" + } + } + } +} + +struct MaintenanceView_Previews: PreviewProvider { + static var previews: some View { + MaintenanceView() + } +} diff --git a/bwi/DeveloperSettings/ServerCapabilitiesView.swift b/bwi/DeveloperSettings/ServerCapabilitiesView.swift new file mode 100644 index 000000000..8b05f48df --- /dev/null +++ b/bwi/DeveloperSettings/ServerCapabilitiesView.swift @@ -0,0 +1,73 @@ +// +/* + * Copyright (c) 2022 BWI GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import SwiftUI + +struct ServerCapabilitiesView: View { + @State var jsonString = "" + var session: MXSession? + + var body: some View { + ScrollView { + VStack(alignment: .leading) { + HStack { + Text(jsonString) + .font(.caption) + .padding() + Spacer() + } + } + } + .navigationTitle(BWIL10n.bwiSettingsDeveloperCapabilities) + .navigationBarTitleDisplayMode(.inline) + .onAppear { + Task { + await fetchData() + } + } + } + + private func fetchData() async { + guard let restClient = session?.matrixRestClient else { + return + } + + let responseDict = await withCheckedContinuation { continuation in + restClient.getServerCapabilities { jsonResponse, error in + continuation.resume(returning: jsonResponse) + } + } + await MainActor.run { + do { + if let responseDict = responseDict { + let data = try JSONSerialization.data(withJSONObject: responseDict as Any, options: [.prettyPrinted]) + jsonString = String(data: data, encoding: .utf8) ?? "" + } else { + jsonString = "" + } + } catch { + jsonString = "" + } + } + } +} + +struct ServerCapabilitiesView_Previews: PreviewProvider { + static var previews: some View { + ServerCapabilitiesView() + } +} diff --git a/bwi/DeveloperSettings/ServerSideKeyBackupView.swift b/bwi/DeveloperSettings/ServerSideKeyBackupView.swift new file mode 100644 index 000000000..3a867b6c7 --- /dev/null +++ b/bwi/DeveloperSettings/ServerSideKeyBackupView.swift @@ -0,0 +1,96 @@ +// +/* + * Copyright (c) 2022 BWI GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import SwiftUI + +struct ServerSideKeyBackupView: View { + @State var algorithm: String? + @State var count: Int? + @State var etag: String? + @State var version: String? + var session: MXSession? + + var body: some View { + Form { + if let algorithm = algorithm { + TitleAndValueView(title: NSLocalizedString(BWIL10n.bwiSettingsDeveloperKeyBackupAlgorithm, comment: ""), value: algorithm) + } + if let count = count { + TitleAndValueView(title: NSLocalizedString(BWIL10n.bwiSettingsDeveloperKeyBackupCount, comment: ""), value: "\(count)") + } + if let etag = etag { + TitleAndValueView(title: NSLocalizedString(BWIL10n.bwiSettingsDeveloperKeyBackupEtag, comment: ""), value: etag) + } + if let version = version { + TitleAndValueView(title: NSLocalizedString(BWIL10n.bwiSettingsDeveloperKeyBackupVersion, comment: ""), value: version) + } + } + .navigationTitle(BWIL10n.bwiSettingsDeveloperKeyBackup) + .navigationBarTitleDisplayMode(.inline) + .onAppear { + Task { + await fetchData() + } + } + } + + private func fetchData() async { + guard let restClient = session?.matrixRestClient else { + return + } + + let responseDict = await withCheckedContinuation { continuation in + restClient.getKeyBackupVersion { jsonResponse, error in + continuation.resume(returning: jsonResponse) + } + } + await MainActor.run { + if let responseDict = responseDict { + algorithm = responseDict["algorithm"] as? String + count = responseDict["count"] as? Int + etag = responseDict["etag"] as? String + version = responseDict["version"] as? String + } else { + algorithm = nil + count = nil + etag = nil + version = nil + } + } + } + +} + +struct TitleAndValueView: View { + var title: String + var value: String + + var body: some View { + HStack(alignment: .firstTextBaseline) { + Text(title) + Spacer() + Text(value) + .foregroundColor(.secondary) + } + } +} + +struct ServerSideKeyBackupView_Previews: PreviewProvider { + static var previews: some View { + ServerSideKeyBackupView() + } +} diff --git a/bwi/DeveloperSettings/UserAccountDataView.swift b/bwi/DeveloperSettings/UserAccountDataView.swift new file mode 100644 index 000000000..ec423dd99 --- /dev/null +++ b/bwi/DeveloperSettings/UserAccountDataView.swift @@ -0,0 +1,96 @@ +// +/* + * Copyright (c) 2022 BWI GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import SwiftUI + +fileprivate let availableAccountDataTypes: [String] = [ + "de.bwi.personal_notes_room", + "de.bwi.should_show_ios_release_notes", + "de.bwi.notification_times", + "de.bwi.top_banner_features", + "de.bwi.notifications", + "im.vector.setting.integration_provisioning", + "im.vector.setting.allowed_widgets" +] + +struct UserAccountDataView: View { + @State var selectedAccountDataType = "de.bwi.personal_notes_room" + @State var jsonString = "" + var session: MXSession? + + var body: some View { + Form { + SwiftUI.Section { + Picker(BWIL10n.bwiSettingsDeveloperUserAccountDataType, selection: $selectedAccountDataType) { + ForEach(availableAccountDataTypes, id:\.self) { type in + Text(type).tag(type) + } + } + .onChange(of: selectedAccountDataType) { newValue in + Task { + await fetchData(forType: newValue) + } + } + } + if !jsonString.isEmpty { + SwiftUI.Section { + Text(jsonString) + .font(.caption) + .padding() + } + } + } + .navigationTitle(BWIL10n.bwiSettingsDeveloperUserAccountData) + .navigationBarTitleDisplayMode(.inline) + .onAppear { + Task { + await fetchData(forType: selectedAccountDataType) + } + } + } + + private func fetchData(forType accountDataType: String) async { + guard let restClient = session?.matrixRestClient else { + return + } + + let responseDict = await withCheckedContinuation { continuation in + restClient.getAccountData(forType: accountDataType) { jsonResponse, error in + continuation.resume(returning: jsonResponse) + } + } + await MainActor.run { + do { + if let responseDict = responseDict { + print(responseDict) + let data = try JSONSerialization.data(withJSONObject: responseDict as Any, options: [.prettyPrinted]) + jsonString = String(data: data, encoding: .utf8) ?? "" + } else { + jsonString = "" + } + } catch { + jsonString = "" + } + } + } +} + +struct UserAccountDataView_Previews: PreviewProvider { + static var previews: some View { + UserAccountDataView() + } +} diff --git a/bwi/DeveloperSettings/WellKnownView.swift b/bwi/DeveloperSettings/WellKnownView.swift new file mode 100644 index 000000000..df63e02eb --- /dev/null +++ b/bwi/DeveloperSettings/WellKnownView.swift @@ -0,0 +1,73 @@ +// +/* + * Copyright (c) 2022 BWI GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import SwiftUI + +struct WellKnownView: View { + @State var jsonString = "" + var session: MXSession? + + var body: some View { + ScrollView { + VStack(alignment: .leading) { + HStack { + Text(jsonString) + .font(.caption) + .padding() + Spacer() + } + } + } + .navigationTitle(BWIL10n.bwiSettingsDeveloperWellKnown) + .navigationBarTitleDisplayMode(.inline) + .onAppear { + Task { + await fetchData() + } + } + } + + private func fetchData() async { + guard let restClient = session?.matrixRestClient else { + return + } + + let responseDict = await withCheckedContinuation { continuation in + restClient.getWellKnown { jsonResponse, error in + continuation.resume(returning: jsonResponse) + } + } + await MainActor.run { + do { + if let responseDict = responseDict { + let data = try JSONSerialization.data(withJSONObject: responseDict as Any, options: [.prettyPrinted]) + jsonString = String(data: data, encoding: .utf8) ?? "" + } else { + jsonString = "" + } + } catch { + jsonString = "" + } + } + } +} + +struct WellKnownView_Previews: PreviewProvider { + static var previews: some View { + WellKnownView() + } +} diff --git a/bwi/PersonalNotes/PersonalNotesDefaultService.swift b/bwi/PersonalNotes/PersonalNotesDefaultService.swift index b6f9023b4..67a222e11 100644 --- a/bwi/PersonalNotes/PersonalNotesDefaultService.swift +++ b/bwi/PersonalNotes/PersonalNotesDefaultService.swift @@ -90,7 +90,7 @@ import MatrixSDK } extension PersonalNotesDefaultService : PersonalNotesService { - + func personalNotesRoomId() -> String? { guard let personalNotesRoom = self.personalNotesRoom() else { return nil @@ -119,12 +119,12 @@ extension PersonalNotesDefaultService : PersonalNotesService { case .success(let room): _ = self.setRoomId(roomId: room.roomId) - let tagOrder = self.session.tagOrderToBe(at: 0, from: UInt(NSNotFound), withTag: PersonalNotesDefaultService.roomTag) ?? "" - room.replaceTag(PersonalNotesDefaultService.roomTag, with: PersonalNotesDefaultService.roomTag, withOrder: tagOrder) { (response) in + let tagOrder = self.session.tagOrderToBe(at: 0, from: UInt(NSNotFound), withTag: kMXRoomTagFavourite) ?? "" + room.replaceTag(kMXRoomTagFavourite, with: kMXRoomTagFavourite, withOrder: tagOrder) { (response) in } - case .failure(let error): - MXLog.error("**BWI** Personal Notes creation failure:") + case .failure(_ ): + MXLog.error("**BWI** Personal Notes creation failure") } } } @@ -138,4 +138,17 @@ extension PersonalNotesDefaultService : PersonalNotesService { func avatarImageUrl() -> String { return self.defaultAvatar } + + func setAsFavoriteIfNeeded() { + if let room = self.session.room(withRoomId: self.personalNotesRoomId()) { + if room.accountData.tags[kMXRoomTagFavourite] == nil { + let tagOrder = self.session.tagOrderToBe(at: 0, from: UInt(NSNotFound), withTag: kMXRoomTagFavourite) ?? "" + room.replaceTag(kMXRoomTagFavourite, with: kMXRoomTagFavourite, withOrder: tagOrder) { (response) in + // check old settings + let layoutSettings = AllChatsLayoutSettingsManager.shared.allChatLayoutSettings + PersonalNotesSettings().personalNotesVisible = layoutSettings.sections.contains(.bwiPersonalNotes) + } + } + } + } } diff --git a/bwi/PersonalNotes/PersonalNotesService.swift b/bwi/PersonalNotes/PersonalNotesService.swift index 64a92d80f..5b33efca5 100644 --- a/bwi/PersonalNotes/PersonalNotesService.swift +++ b/bwi/PersonalNotes/PersonalNotesService.swift @@ -24,4 +24,6 @@ protocol PersonalNotesService { func resetPersonalNotesRoom() func avatarImageUrl() -> String + + func setAsFavoriteIfNeeded() } diff --git a/bwi/PollParticipantDetails/PollParticipantDetailsModels.swift b/bwi/PollParticipantDetails/PollParticipantDetailsModels.swift index 2a8f11599..192b0363d 100644 --- a/bwi/PollParticipantDetails/PollParticipantDetailsModels.swift +++ b/bwi/PollParticipantDetails/PollParticipantDetailsModels.swift @@ -49,8 +49,15 @@ struct PollParticipantVoter: Identifiable, BindableState { let dateFormatter = DateFormatter() dateFormatter.timeZone = TimeZone.current dateFormatter.calendar = Calendar.current - dateFormatter.dateFormat = "dd. MMMM, yyyy HH:mm" - let strDate = dateFormatter.string(from: votingTime) + var strDate = "" + if Calendar.current.isDateInToday(votingTime) { + dateFormatter.dateFormat = "HH:mm" + strDate = BWIL10n.pollParticipantDetailsDateStringToday.appending(dateFormatter.string(from: votingTime)) + } else { + dateFormatter.dateFormat = "E, d. MMM yyyy, HH:mm" + strDate = dateFormatter.string(from: votingTime) + } + strDate.append(contentsOf: BWIL10n.pollParticipantDetailsClockString) return PollParticipantVoter(displayName: user.displayname, userAvatarData: avatarData, formattedVotingTime: strDate) } else { diff --git a/bwi/PollParticipantDetails/PollParticipantDetailsView.swift b/bwi/PollParticipantDetails/PollParticipantDetailsView.swift index 7b9bb6c4a..0990f411a 100644 --- a/bwi/PollParticipantDetails/PollParticipantDetailsView.swift +++ b/bwi/PollParticipantDetails/PollParticipantDetailsView.swift @@ -86,6 +86,7 @@ struct PollParticipantVoterView: View { .font(theme.fonts.footnote) .foregroundColor(theme.colors.secondaryContent) } + Spacer() } } } diff --git a/bwi/ServerMaintenance/ServerDownTimeBadge.swift b/bwi/ServerMaintenance/ServerDownTimeBadge.swift index 90e72efcf..ec2a94303 100644 --- a/bwi/ServerMaintenance/ServerDownTimeBadge.swift +++ b/bwi/ServerMaintenance/ServerDownTimeBadge.swift @@ -21,7 +21,7 @@ import UIKit @objcMembers class ServerDowntimeBadge : NSObject { func applyBadgeToButton(button: UIButton, color: UIColor) -> UIButton { - let badge = UILabel.init(frame: CGRect.init(x: 15, y: 0, width: 15, height: 15)) + let badge = UILabel.init(frame: CGRect.init(x: 20, y: 0, width: 15, height: 15)) badge.backgroundColor = color badge.clipsToBounds = true badge.layer.cornerRadius = 7 diff --git a/bwi/ServerMaintenance/ServerDownTimeService.swift b/bwi/ServerMaintenance/ServerDownTimeService.swift index 904bd0468..5ae327438 100644 --- a/bwi/ServerMaintenance/ServerDownTimeService.swift +++ b/bwi/ServerMaintenance/ServerDownTimeService.swift @@ -16,6 +16,7 @@ */ import Foundation +import SwiftUI enum ServerDowntimeStatus { case none @@ -23,6 +24,10 @@ enum ServerDowntimeStatus { case ongoing } +enum ServerMaintenanceAlertType { + case showServerMaintenanceInfoMessageAlert, showServerMaintenanceDefaultAlert, showInvalidAppVersionAlert, showDowntimeTimeAlert +} + enum ServerDowntimeType: String { case adhocMessage = "ADHOC_MESSAGE" case maintenance = "MAINTENANCE" @@ -38,4 +43,7 @@ protocol ServerDowntimeService { func isBlocking() -> Bool func setManuallyIgnored() func isManuallyIgnored() -> Bool + func alert( alertType:ServerMaintenanceAlertType, completion: @escaping () -> Void) -> Alert + func showAlert() -> Bool + func alertType() -> ServerMaintenanceAlertType } diff --git a/bwi/ServerMaintenance/ServerDowntimeDefaultService.swift b/bwi/ServerMaintenance/ServerDowntimeDefaultService.swift index 11bba57aa..919e0071b 100644 --- a/bwi/ServerMaintenance/ServerDowntimeDefaultService.swift +++ b/bwi/ServerMaintenance/ServerDowntimeDefaultService.swift @@ -18,6 +18,7 @@ import Foundation import UIKit import MatrixSDK +import SwiftUI fileprivate let BWI_ServerDowntimes = "BWI_ServerDowntimes" @@ -150,6 +151,80 @@ fileprivate let maintenanceURL = "/_matrix/cmaintenance" extension ServerDowntimeDefaultService : ServerDowntimeService { + func alert(alertType: ServerMaintenanceAlertType, completion: @escaping () -> Void) -> Alert { + switch alertType { + case .showInvalidAppVersionAlert: + return Alert(title: Text(BWIL10n.bwiOutdatedVersionWarningTitle), + message: Text(BWIL10n.bwiOutdatedVersionWarningMessage(AppInfo.current.displayName)), + dismissButton: .destructive(Text(BWIL10n.bwiOutdatedVersionAppstoreButton), action: { + let iTunesLink = BWIBuildSettings.shared.itunesAppLink + UIApplication.shared.open(URL(string: iTunesLink)!, options: [:], completionHandler: nil) + })) + case .showDowntimeTimeAlert: + if BWIBuildSettings.shared.ignoreBlockingMaintenance && isBlocking() { + return Alert( title: Text(""), + message: Text(ServerDowntimeDefaultService().downtimeText()), + primaryButton: .cancel(Text(BWIL10n.blockingDowntimeAlertIgnoreButton)) { + UserDefaults.standard.set(false, forKey: "ServerDownTimeBlockingKey") + self.setManuallyIgnored() + completion() + }, + secondaryButton: .destructive(Text(BWIL10n.blockingDowntimeAlertDismissButton)) + ) + + } else { + return Alert(title: Text(BWIL10n.downtimeTitle), + message: Text(ServerDowntimeDefaultService().downtimeText() != "" ? BWIL10n.downtimeDefaultMessage + "\n\n" + ServerDowntimeDefaultService().downtimeText() : BWIL10n.downtimeDefaultMessage), + dismissButton: .destructive(Text(isBlocking() ? BWIL10n.blockingDowntimeAlertDismissButton : BWIL10n.downtimeAlertDismissButton)) { + if self.isBlocking() { + return + } else { + completion() + } + }) + } + case .showServerMaintenanceInfoMessageAlert: + if BWIBuildSettings.shared.ignoreBlockingMaintenance && isBlocking() { + return Alert( title: Text(""), + message: Text(ServerDowntimeDefaultService().downtimeText()), + primaryButton: .cancel(Text(BWIL10n.blockingDowntimeAlertIgnoreButton)) { + completion() + }, + secondaryButton: .destructive(Text(BWIL10n.blockingDowntimeAlertDismissButton)) + ) + + } else { + return Alert(title: Text(""), + message: Text(ServerDowntimeDefaultService().downtimeText()), + dismissButton: .destructive(Text(isBlocking() ? BWIL10n.blockingDowntimeAlertDismissButton : BWIL10n.downtimeAlertDismissButton)) { + if self.isBlocking() { + return + } else { + completion() + } + }) + } + case .showServerMaintenanceDefaultAlert: + return Alert(title: Text(BWIL10n.downtimeTitle), + message: Text(BWIL10n.downtimeDefaultMessage), + dismissButton: .destructive(Text(BWIL10n.downtimeAlertDismissButton)) { + completion() + }) + } + } + + func showAlert() -> Bool { + return nextDowntimeStatus() == .ongoing + } + + func alertType() -> ServerMaintenanceAlertType { + if downtimeType() == .adhocMessage { + return .showServerMaintenanceInfoMessageAlert + } else { + return .showDowntimeTimeAlert + } + } + func fetchDowntimes(session: MXSession, completion: @escaping () -> Void) { session.matrixRestClient.getDowntime(completion: { (jsonResponse, error) in do { diff --git a/bwi/SplashScreen/View/OnboardingBwiSplashScreen.swift b/bwi/SplashScreen/View/OnboardingBwiSplashScreen.swift index ad508412a..d9aa25ce4 100644 --- a/bwi/SplashScreen/View/OnboardingBwiSplashScreen.swift +++ b/bwi/SplashScreen/View/OnboardingBwiSplashScreen.swift @@ -16,10 +16,6 @@ import SwiftUI -enum OnBoardingSplashScreenAlertType { - case showServerMaintenanceInfoMessageAlert, showServerMaintenanceDefaultAlert, showInvalidAppVersionAlert, showDowntimeTimeAlert -} - @available(iOS 14.0, *) /// The splash screen shown at the beginning of the onboarding flow. struct OnboardingBwiSplashScreen: View { @@ -36,7 +32,7 @@ struct OnboardingBwiSplashScreen: View { @State private var overlayFrame: CGRect = .zero @State private var isFetchingDowntime = false @State private var showAlert = false - @State private var activeAlert: OnBoardingSplashScreenAlertType = .showInvalidAppVersionAlert + @State private var activeAlert: ServerMaintenanceAlertType = .showInvalidAppVersionAlert // MARK: Public @@ -79,40 +75,8 @@ struct OnboardingBwiSplashScreen: View { .navigationTitle("") .navigationBarHidden(true) .alert(isPresented: $showAlert, content: { - switch activeAlert { - case .showInvalidAppVersionAlert: - return Alert(title: Text(BWIL10n.bwiOutdatedVersionWarningTitle), - message: Text(BWIL10n.bwiOutdatedVersionWarningMessage(AppInfo.current.displayName)), - dismissButton: .destructive(Text(BWIL10n.bwiOutdatedVersionAppstoreButton), action: { - let iTunesLink = BWIBuildSettings.shared.itunesAppLink - UIApplication.shared.open(URL(string: iTunesLink)!, options: [:], completionHandler: nil) - })) - case .showDowntimeTimeAlert: - return Alert(title: Text(BWIL10n.downtimeTitle), - message: Text(ServerDowntimeDefaultService().downtimeText() != "" ? BWIL10n.downtimeDefaultMessage + "\n\n" + ServerDowntimeDefaultService().downtimeText() : BWIL10n.downtimeDefaultMessage), - dismissButton: .destructive(Text(service.isBlocking() ? BWIL10n.blockingDowntimeAlertDismissButton : BWIL10n.downtimeAlertDismissButton)) { - if service.isBlocking() { - return - } else { - viewModel.send(viewAction: .login) - } - }) - case .showServerMaintenanceInfoMessageAlert: - return Alert(title: Text(""), - message: Text(ServerDowntimeDefaultService().downtimeText()), - dismissButton: .destructive(Text(service.isBlocking() ? BWIL10n.blockingDowntimeAlertDismissButton : BWIL10n.downtimeAlertDismissButton)) { - if service.isBlocking() { - return - } else { - viewModel.send(viewAction: .login) - } - }) - case .showServerMaintenanceDefaultAlert: - return Alert(title: Text(BWIL10n.downtimeTitle), - message: Text(BWIL10n.downtimeDefaultMessage), - dismissButton: .destructive(Text(BWIL10n.downtimeAlertDismissButton)) { - viewModel.send(viewAction: .login) - }) + service.alert(alertType: activeAlert) { + viewModel.send(viewAction: .login) } }) } @@ -146,24 +110,11 @@ struct OnboardingBwiSplashScreen: View { private func showAlertIfNeeded() { - switch service.nextDowntimeStatus() { - case .none: - viewModel.send(viewAction: .login) - break - case .ongoing: - if service.downtimeType() == .adhocMessage { - activeAlert = .showServerMaintenanceInfoMessageAlert - } else { - activeAlert = .showDowntimeTimeAlert - } + if service.showAlert() { + activeAlert = service.alertType() showAlert = true - break - case .warning: - // only show active downtimes + } else { viewModel.send(viewAction: .login) - // showAlert = true - // activeAlert = .showServerMaintenanceAlert - break } } } diff --git a/bwi/Tools/BWIRestClient.swift b/bwi/Tools/BWIRestClient.swift index ff71310dc..21b49bae4 100644 --- a/bwi/Tools/BWIRestClient.swift +++ b/bwi/Tools/BWIRestClient.swift @@ -17,7 +17,49 @@ import Foundation public extension MXRestClient { + + @discardableResult + func getWellKnown(completion: @escaping (_ jsonResponse: Dictionary?, _ error: Error?) -> Void) -> MXHTTPOperation? { + + guard let httpClient = httpClient else { + completion(nil, "HTTPClient not available") + return nil + } + + let path = ".well-known/matrix/client" + + return httpClient.request(withMethod: "GET", path: path, parameters: nil, success: { response in + guard response != nil else { + completion(nil, "Response empty") + return + } + completion(response, nil) + }, failure: { (error) in + completion(nil, error) + }) + } + @discardableResult + func getServerCapabilities(completion: @escaping (_ jsonResponse: Dictionary?, _ error: Error?) -> Void) -> MXHTTPOperation? { + + guard let httpClient = httpClient else { + completion(nil, "HTTPClient not available") + return nil + } + + let path = apiPathPrefix + "/capabilities" + + return httpClient.request(withMethod: "GET", path: path, parameters: nil, success: { response in + guard response != nil else { + completion(nil, "Response empty") + return + } + completion(response, nil) + }, failure: { (error) in + completion(nil, error) + }) + } + @discardableResult func getAccountData(forType type: String, completion: @escaping (_ jsonResponse: Dictionary?, _ error: Error?) -> Void) -> MXHTTPOperation? { @@ -73,7 +115,26 @@ public extension MXRestClient { return nil } - let path = "_matrix/cmaintenance" + let path = "/_matrix/cmaintenance" + + return httpClient.request(withMethod: "GET", path: path, parameters: nil, success: { response in + guard response != nil else { + completion(nil, "Response empty") + return + } + completion(response, nil) + }, failure: { (error) in + completion(nil, error) + }) + } + + @discardableResult + func getKeyBackupVersion(completion: @escaping (_ jsonResponse: Dictionary?, _ error: Error?) -> Void) -> MXHTTPOperation? { + guard let httpClient = httpClient else { + return nil + } + + let path = "/_matrix/client/v3/room_keys/version" return httpClient.request(withMethod: "GET", path: path, parameters: nil, success: { response in guard response != nil else { diff --git a/bwi/WelcomeExperience/WelcomeExperienceView.swift b/bwi/WelcomeExperience/WelcomeExperienceView.swift index 10fe77093..a50e8330f 100644 --- a/bwi/WelcomeExperience/WelcomeExperienceView.swift +++ b/bwi/WelcomeExperience/WelcomeExperienceView.swift @@ -36,6 +36,11 @@ fileprivate struct Page: View { @State var buttonCallback: (() -> Void)? + private let service = ServerDowntimeDefaultService() + @State private var isFetchingDowntime = false + @State private var showAlert = false + @State private var activeAlert: ServerMaintenanceAlertType = .showInvalidAppVersionAlert + var body: some View { GeometryReader { geometry in VStack(alignment: .center, spacing: 40) { @@ -54,8 +59,9 @@ fileprivate struct Page: View { .multilineTextAlignment(.center) Spacer() + // bwi #3811: check for maintenance when the server selection is skipped if let doneButton = doneButton, let buttonCallback = buttonCallback { - Button(action: buttonCallback) { + Button(action: startButtonAction) { Text(doneButton) .foregroundColor(.white) .padding(.vertical, 10) @@ -64,6 +70,11 @@ fileprivate struct Page: View { .clipShape(RoundedRectangle(cornerRadius: 10)) } .padding(.bottom, 100) + .alert(isPresented: $showAlert, content: { + service.alert(alertType: activeAlert) { + buttonCallback() + } + }) } } .padding(.top, 100) @@ -71,6 +82,49 @@ fileprivate struct Page: View { .frame(maxWidth: .infinity, maxHeight: .infinity) } } + + private func startButtonAction() { + guard let buttonCallback = buttonCallback else { + return + } + + if BWIBuildSettings.shared.enableMaintenanceInfoOnLogin && BWIBuildSettings.shared.avoidServerSelectionOnAppConfig && AppConfigService.shared.isAppConfig { + isFetchingDowntime = true // show progresview + + if BWIBuildSettings.shared.useTestDataForDowntime { + service.fetchDowntimes { + self.isFetchingDowntime = false // hide progressview + self.showAlertIfNeeded() + } + } else { + service.fetchDowntimesWithDirectRequest { success in + DispatchQueue.main.async { + self.isFetchingDowntime = false // hide progressview + if success { + self.showAlertIfNeeded() + } else { + + } + } + } + } + } else { + buttonCallback() + } + } + + private func showAlertIfNeeded() { + guard let buttonCallback = buttonCallback else { + return + } + + if service.showAlert() { + activeAlert = service.alertType() + showAlert = true + } else { + buttonCallback() + } + } } struct WelcomeExperienceView_Previews: PreviewProvider { diff --git a/project.yml b/project.yml index 4fa385c7c..05309f4e2 100644 --- a/project.yml +++ b/project.yml @@ -32,8 +32,6 @@ include: - path: Riot/target-bum-beta.yml - path: Riot/target-bum-open.yml - path: RiotTests/target.yml - - path: RiotShareExtension/target.yml - - path: SiriIntents/target.yml - path: RiotNSE/target.yml - path: BroadcastUploadExtension/target.yml - path: DesignKit/target.yml @@ -42,6 +40,10 @@ include: - path: RiotSwiftUI/targetUITests.yml - path: CommonKit/target.yml - path: CommonKit/targetUnitTests.yml + # Disabled due to crypto corruption issues. + # https://github.com/vector-im/element-ios/issues/7618 + # - path: RiotShareExtension/target.yml + # - path: SiriIntents/target.yml packages: AnalyticsEvents: