diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..c6b229539 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'bug' +assignees: '' + +--- + +#### Describe the bug. +A clear and concise description of what the bug is. + +#### Steps to reproduce: +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +#### Expected behavior +A clear and concise description of what you expected to happen. + +#### Screenshots +If applicable, add screenshots to help explain your problem. + +#### Contextual information: + + - Device: + - OS: + + - App Version: \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 000000000..b30282798 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1,8 @@ +blank_issues_enabled: true +contact_links: + - name: Element iOS Community Support + url: "https://matrix.to/#/#element-ios:matrix.org" + about: General Element iOS support questions can be asked here. + - name: Matrix Security Policy + url: https://www.matrix.org/security-disclosure-policy/ + about: Learn more about our security disclosure policy. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..5a372c146 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: 'feature' +assignees: '' + +--- + +#### Is your feature request related to a problem? Please describe. +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +#### Describe the solution you'd like. +A clear and concise description of what you want to happen. + +#### Describe alternatives you've considered. +A clear and concise description of any alternative solutions or features you've considered. + +#### Additional context. +Add any other context or screenshots about the feature request here. \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci-build.yml similarity index 56% rename from .github/workflows/ci.yml rename to .github/workflows/ci-build.yml index 3e9e33f5c..d1cd1b3a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci-build.yml @@ -1,4 +1,4 @@ -name: CI +name: Build CI on: # Triggers the workflow on any pull request and push to develop @@ -52,43 +52,3 @@ jobs: # Main step - name: Build iOS simulator run: bundle exec fastlane build - - - tests: - name: Tests - runs-on: macos-latest - - steps: - - uses: actions/checkout@v2 - - # Common cache - # Note: GH actions do not support yaml anchor yet. We need to duplicate this for every job - - uses: actions/cache@v2 - with: - path: Pods - key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} - restore-keys: | - ${{ runner.os }}-pods- - - uses: actions/cache@v2 - with: - path: vendor/bundle - key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} - restore-keys: | - ${{ runner.os }}-gems- - - # Make sure we use the latest version of MatrixKit - - name: Reset MatrixKit pod - run: rm -rf Pods/MatrixKit - - # Common setup - # Note: GH actions do not support yaml anchor yet. We need to duplicate this for every job - - name: Bundle install - run: | - bundle config path vendor/bundle - bundle install --jobs 4 --retry 3 - - name: Use right MatrixKit and MatrixSDK versions - run: bundle exec fastlane point_dependencies_to_related_branches - - # Main step - - name: Unit tests - run: bundle exec fastlane test diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml new file mode 100644 index 000000000..eac0c885d --- /dev/null +++ b/.github/workflows/ci-tests.yml @@ -0,0 +1,54 @@ +name: Tests CI + +on: + # Triggers the workflow on any pull request and push to develop + push: + branches: [ develop ] + pull_request: + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +env: + # Make the git branch for a PR available to our Fastfile + MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }} + +jobs: + tests: + name: Tests + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + + # Common cache + # Note: GH actions do not support yaml anchor yet. We need to duplicate this for every job + - uses: actions/cache@v2 + with: + path: Pods + key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} + restore-keys: | + ${{ runner.os }}-pods- + - uses: actions/cache@v2 + with: + path: vendor/bundle + key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} + restore-keys: | + ${{ runner.os }}-gems- + + # Make sure we use the latest version of MatrixKit + - name: Reset MatrixKit pod + run: rm -rf Pods/MatrixKit + + # Common setup + # Note: GH actions do not support yaml anchor yet. We need to duplicate this for every job + - name: Bundle install + run: | + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + - name: Use right MatrixKit and MatrixSDK versions + run: bundle exec fastlane point_dependencies_to_related_branches + + # Main step + - name: Unit tests + run: bundle exec fastlane test diff --git a/.swiftlint.yml b/.swiftlint.yml index 8682b5912..765c414b4 100755 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -2,7 +2,6 @@ disabled_rules: - trailing_whitespace - todo - - conditional_binding_cascade - mark - vertical_whitespace - syntactic_sugar diff --git a/CHANGES.rst b/CHANGES.rst index 575f22768..be091fc8c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,8 +5,36 @@ Changes to be released in next version * ๐Ÿ™Œ Improvements + * Room: Added support for Voice Messages (#4090, #4091, #4092, #4094, #4095, #4096) + * Remove the directory section from the Rooms tab. * Notifications: Show decrypted content is enabled by default (#4519). + +๐Ÿ› Bugfix + * Room: Fixed mentioning users from room info member details (#4583) + +โš ๏ธ API Changes + * + +๐Ÿ—ฃ Translations + * + +๐Ÿงฑ Build + * + +Others + * Separated CI jobs into individual actions + * Update Gemfile.lock + +Changes in 1.4.7 (2021-07-22) +================================================= + +โœจ Features + * + +๐Ÿ™Œ Improvements + * + ๐Ÿ› Bugfix * @@ -20,8 +48,72 @@ Changes to be released in next version * Others + * Updated issue templates. + +Improvements: + * Upgrade MatrixKit version ([v0.15.5](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.15.5)). + +Changes in 1.4.6 (2021-07-16) +================================================= + +โœจ Features * +๐Ÿ™Œ Improvements + * Room Notification Settings: Ability to change between "All Messages", "Mentions and Keywords" and "None". Not yet exposed in Element UI. (#4458). + * Add support for sending slow motion videos (#4483). + +๐Ÿ› Bugfix + * VoIP: Do not present ended calls. + * More fixes to Main.storyboard layout on iPhone 12 Pro Max (#4527) + * Fix crash on Apple Silicon Macs. + * Media Picker: Generate video thumbnails with the correct orientation (#4515). + * Directory List (pop-up one): Fix duplicate rooms being shown (#4537). + * Use different title for scan button for self verification (#4525). + * it's easy for the back button to trigger a leftpanel reveal (#4438). + * Show / hide reset button in secrets recovery screen (#4546). + * Share Extension: Fix layout when searching (#4258). + * Timeline: Fix incorrect crop of media thumbnails (#4552). + +โš ๏ธ API Changes + * + +๐Ÿ—ฃ Translations + * + +๐Ÿงฑ Build + * + +Others + * Silenced some documentation, deprecations and SwiftLint warnings. + +Improvements: + * Upgrade MatrixKit version ([v0.15.4](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.15.4)). + +Changes in 1.4.5 (2021-07-07) +================================================= + +โœจ Features + * + +๐Ÿ™Œ Improvements + * + +๐Ÿ› Bugfix + * Notifications: Fix an issue where the app is unresponsive after getting some notifications (#4534). + +โš ๏ธ API Changes + * + +๐Ÿ—ฃ Translations + * + +๐Ÿงฑ Build + * + +Others + * + Changes in 1.4.4 (2021-06-30) ================================================= diff --git a/Config/AppIdentifiers.xcconfig b/Config/AppIdentifiers.xcconfig index 391931749..1628e83d8 100644 --- a/Config/AppIdentifiers.xcconfig +++ b/Config/AppIdentifiers.xcconfig @@ -22,8 +22,8 @@ APPLICATION_GROUP_IDENTIFIER = group.im.vector APPLICATION_SCHEME = element // Version -MARKETING_VERSION = 1.4.5 -CURRENT_PROJECT_VERSION = 1.4.5 +MARKETING_VERSION = 1.4.8 +CURRENT_PROJECT_VERSION = 1.4.8 // Team diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 76cf11fd0..ed613fa9b 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -148,7 +148,7 @@ final class BuildSettings: NSObject { // MARK: - Public rooms Directory - static let publicRoomsShowDirectory: Bool = true + #warning("Unused build setting: should this be implemented in ShowDirectory?") static let publicRoomsAllowServerChange: Bool = true // List of homeservers for the public rooms directory static let publicRoomsDirectoryServers = [ @@ -295,6 +295,7 @@ final class BuildSettings: NSObject { static let roomSettingsScreenShowFlairSettings: Bool = true static let roomSettingsScreenShowAdvancedSettings: Bool = true static let roomSettingsScreenAdvancedShowEncryptToVerifiedOption: Bool = true + static let roomSettingsScreenShowNotificationsV2: Bool = false // MARK: - Room Member Screen @@ -308,6 +309,10 @@ final class BuildSettings: NSObject { static let messageDetailsAllowCopyMedia: Bool = true static let messageDetailsAllowPasteMedia: Bool = true + // MARK: - Voice Message + + static let voiceMessagesEnabled = false + // MARK: - Notifications static let decryptNotificationsByDefault: Bool = true @@ -323,6 +328,9 @@ final class BuildSettings: NSObject { static let authScreenShowForgotPassword = true static let authScreenShowCustomServerOptions = true - // Mark: - Unified Search + // MARK: - Unified Search static let unifiedSearchScreenShowPublicDirectory = true + + // MARK: - Secrets Recovery + static let secretsRecoveryAllowReset = true } diff --git a/DesignKit/Source/Colors.swift b/DesignKit/Source/Colors.swift index b5dc66261..fc96f3608 100644 --- a/DesignKit/Source/Colors.swift +++ b/DesignKit/Source/Colors.swift @@ -45,6 +45,10 @@ import UIKit /// - Icons var quarterlyContent: UIColor { get } + /// - Text + /// - Icons + var quinaryContent: UIColor { get } + /// Separating line var separator: UIColor { get } diff --git a/DesignKit/Variants/Colors/Dark/DarkColors.swift b/DesignKit/Variants/Colors/Dark/DarkColors.swift index a1cf2ac54..39a3b0893 100644 --- a/DesignKit/Variants/Colors/Dark/DarkColors.swift +++ b/DesignKit/Variants/Colors/Dark/DarkColors.swift @@ -32,6 +32,8 @@ public class DarkColors: Colors { public let quarterlyContent: UIColor = UIColor(rgb: 0x6F7882) + public let quinaryContent: UIColor = UIColor(rgb: 0x394049) + public let separator: UIColor = UIColor(rgb: 0x21262C) public let tile: UIColor = UIColor(rgb: 0x394049) diff --git a/DesignKit/Variants/Colors/Light/LightColors.swift b/DesignKit/Variants/Colors/Light/LightColors.swift index d8b5e108b..53a9566e2 100644 --- a/DesignKit/Variants/Colors/Light/LightColors.swift +++ b/DesignKit/Variants/Colors/Light/LightColors.swift @@ -32,6 +32,8 @@ public class LightColors: Colors { public let quarterlyContent: UIColor = UIColor(rgb: 0xC1C6CD) + public let quinaryContent: UIColor = UIColor(rgb: 0xE3E8F0) + public let separator: UIColor = UIColor(rgb: 0xE3E8F0) public let tile: UIColor = UIColor(rgb: 0xF3F8FD) diff --git a/Gemfile.lock b/Gemfile.lock index 31e7491fe..3548311ee 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,7 +7,7 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.7.0) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) @@ -252,4 +252,4 @@ DEPENDENCIES xcode-install BUNDLED WITH - 2.2.14 + 2.2.21 diff --git a/Podfile b/Podfile index d008c31f2..aa6b4d8f5 100644 --- a/Podfile +++ b/Podfile @@ -11,7 +11,7 @@ use_frameworks! # - `{ {kit spec hash} => {sdk spec hash}` to depend on specific pod options (:git => โ€ฆ, :podspec => โ€ฆ) for each 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 -$matrixKitVersion = '= 0.15.3' +$matrixKitVersion = '= 0.15.5' # $matrixKitVersion = :local # $matrixKitVersion = {'develop' => 'develop'} @@ -69,6 +69,8 @@ abstract_target 'RiotPods' do pod 'SwiftBase32', '~> 0.9.0' pod 'SwiftJWT', '~> 3.6.200' pod 'SideMenu', '~> 6.5' + pod 'DSWaveformImage', '~> 6.1.1' + pod 'ffmpeg-kit-ios-audio', '~> 4.4.LTS' pod 'FLEX', '~> 4.4.1', :configurations => ['Debug'] diff --git a/Podfile.lock b/Podfile.lock index 8c6193dfa..8a8b129b9 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -19,6 +19,7 @@ PODS: - BlueRSA (1.0.34) - DGCollectionViewLeftAlignFlowLayout (1.0.4) - Down (0.11.0) + - DSWaveformImage (6.1.1) - DTCoreText (1.6.26): - DTCoreText/Core (= 1.6.26) - DTFoundation/Core (~> 1.7.5) @@ -36,6 +37,7 @@ PODS: - DTFoundation/Core - DTFoundation/UIKit (1.7.18): - DTFoundation/Core + - ffmpeg-kit-ios-audio (4.4) - FLEX (4.4.1) - FlowCommoniOS (1.10.0) - GBDeviceInfo (6.6.0): @@ -56,29 +58,29 @@ PODS: - MatomoTracker (7.4.1): - MatomoTracker/Core (= 7.4.1) - MatomoTracker/Core (7.4.1) - - MatrixKit (0.15.3): + - MatrixKit (0.15.5): - Down (~> 0.11.0) - DTCoreText (~> 1.6.25) - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixKit/Core (= 0.15.3) - - MatrixSDK (= 0.19.3) - - MatrixKit/Core (0.15.3): + - MatrixKit/Core (= 0.15.5) + - MatrixSDK (= 0.19.5) + - MatrixKit/Core (0.15.5): - Down (~> 0.11.0) - DTCoreText (~> 1.6.25) - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.19.3) - - MatrixSDK (0.19.3): - - MatrixSDK/Core (= 0.19.3) - - MatrixSDK/Core (0.19.3): + - MatrixSDK (= 0.19.5) + - MatrixSDK (0.19.5): + - MatrixSDK/Core (= 0.19.5) + - MatrixSDK/Core (0.19.5): - AFNetworking (~> 4.0.0) - GZIP (~> 1.3.0) - libbase58 (~> 0.1.4) - OLMKit (~> 3.2.4) - Realm (= 10.7.6) - SwiftyBeaver (= 1.9.5) - - MatrixSDK/JingleCallStack (0.19.3): + - MatrixSDK/JingleCallStack (0.19.5): - JitsiMeetSDK (= 3.5.0) - MatrixSDK/Core - OLMKit (3.2.4): @@ -104,7 +106,7 @@ PODS: - BlueRSA (~> 1.0) - KituraContracts (~> 1.2) - LoggerAPI (~> 1.7) - - SwiftLint (0.43.0) + - SwiftLint (0.43.1) - SwiftyBeaver (1.9.5) - zxcvbn-ios (1.0.4) - ZXingObjC (3.6.5): @@ -113,6 +115,8 @@ PODS: DEPENDENCIES: - DGCollectionViewLeftAlignFlowLayout (~> 1.0.4) + - DSWaveformImage (~> 6.1.1) + - ffmpeg-kit-ios-audio (~> 4.4.LTS) - FLEX (~> 4.4.1) - FlowCommoniOS (~> 1.10.0) - GBDeviceInfo (~> 6.6.0) @@ -120,7 +124,7 @@ DEPENDENCIES: - KeychainAccess (~> 4.2.2) - KTCenterFlowLayout (~> 1.3.1) - MatomoTracker (~> 7.4.1) - - MatrixKit (= 0.15.3) + - MatrixKit (= 0.15.5) - MatrixSDK - MatrixSDK/JingleCallStack - OLMKit @@ -142,8 +146,10 @@ SPEC REPOS: - BlueRSA - DGCollectionViewLeftAlignFlowLayout - Down + - DSWaveformImage - DTCoreText - DTFoundation + - ffmpeg-kit-ios-audio - FLEX - FlowCommoniOS - GBDeviceInfo @@ -180,8 +186,10 @@ SPEC CHECKSUMS: BlueRSA: 6f9776d62d9773502415a7db3bcbb2bbb3f71fc3 DGCollectionViewLeftAlignFlowLayout: a0fa58797373ded039cafba8133e79373d048399 Down: b6ba1bc985c9d2f4e15e3b293d2207766fa12612 + DSWaveformImage: 3c718a0cf99291887ee70d1d0c18d80101d3d9ce DTCoreText: ec749e013f2e1f76de5e7c7634642e600a7467ce DTFoundation: a53f8cda2489208cbc71c648be177f902ee17536 + ffmpeg-kit-ios-audio: ddfc3dac6f574e83d53f8ae33586711162685d3e FLEX: 7ca2c8cd3a435ff501ff6d2f2141e9bdc934eaab FlowCommoniOS: bcdf81a5f30717e711af08a8c812eb045411ba94 GBDeviceInfo: ed0db16230d2fa280e1cbb39a5a7f60f6946aaec @@ -196,8 +204,8 @@ SPEC CHECKSUMS: LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d Logging: beeb016c9c80cf77042d62e83495816847ef108b MatomoTracker: 24a846c9d3aa76933183fe9d47fd62c9efa863fb - MatrixKit: 6cbe65db11a5450ec8cc02d51660f43b5e95a141 - MatrixSDK: c15663c67bfd2991d897d973c1551ba4de900e25 + MatrixKit: 7606227237cf58c1a1a2235547222c5d75b464c4 + MatrixSDK: 9fa30f9ca2504c4251b99212dcf4ff569bbf45b1 OLMKit: 2d73cd67d149b5c3e3a8eb8ecae93d0b429d8a02 ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d Realm: ed860452717c8db8f4bf832b6807f7f2ce708839 @@ -206,11 +214,11 @@ SPEC CHECKSUMS: SwiftBase32: 9399c25a80666dc66b51e10076bf591e3bbb8f17 SwiftGen: 67860cc7c3cfc2ed25b9b74cfd55495fc89f9108 SwiftJWT: 88c412708f58c169d431d344c87bc79a87c830ae - SwiftLint: 0c645fdc6feed3e390c1701ab3cc669f88b42752 + SwiftLint: 99f82d07b837b942dd563c668de129a03fc3fb52 SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82 zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: c39d88adc5ec2df412af32b64ceb99a9a1ee92a8 +PODFILE CHECKSUM: c7386ecfb38fc4302613c915aef79eebdb98a53d COCOAPODS: 1.10.1 diff --git a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme index 84ecb908a..a9bea1d96 100644 --- a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme +++ b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme @@ -4,7 +4,8 @@ version = "1.3"> + buildImplicitDependencies = "YES" + runPostActionsOnFailure = "NO"> - + - + @@ -151,7 +151,7 @@ - + @@ -177,7 +177,7 @@ - + @@ -356,22 +356,6 @@ - - - - - - - - - - - - - - - - @@ -463,7 +447,7 @@ - + @@ -479,7 +463,6 @@ - @@ -560,28 +543,9 @@ - - - - - - - - - - - - - - - - - - - - + diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/Contents.json b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/Contents.json new file mode 100644 index 000000000..f60c4497c --- /dev/null +++ b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "room_action_notification_muted.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "room_action_notification_muted@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "room_action_notification_muted@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted.png new file mode 100644 index 000000000..02ca4c76f Binary files /dev/null and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted@2x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted@2x.png new file mode 100644 index 000000000..c264ba430 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted@3x.png b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted@3x.png new file mode 100644 index 000000000..b9738ab8b Binary files /dev/null and b/Riot/Assets/Images.xcassets/Home/RoomContextualMenu/room_action_notification_muted.imageset/room_action_notification_muted@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_delete.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_delete.imageset/Contents.json index bb4344e92..a255919c5 100644 --- a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_delete.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_delete.imageset/Contents.json @@ -19,5 +19,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" } } diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark.png index cb0316cf9..e48bf546b 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark.png and b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@2x.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@2x.png index b58c78907..b35cce7f6 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@2x.png and b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@3x.png b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@3x.png index b04f2a977..4c3eb73ea 100644 Binary files a/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@3x.png and b/Riot/Assets/Images.xcassets/Room/Input/upload_icon_dark.imageset/upload_icon_dark@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/Contents.json new file mode 100644 index 000000000..246bbbbf8 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "voice_message_cancel_gradient.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_message_cancel_gradient@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_message_cancel_gradient@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient.png new file mode 100644 index 000000000..aad31deb7 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient@2x.png new file mode 100644 index 000000000..5d0595c22 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient@3x.png new file mode 100644 index 000000000..6c08777d3 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_cancel_gradient.imageset/voice_message_cancel_gradient@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/Contents.json new file mode 100644 index 000000000..6b40d80e4 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "voice_message_lock_chevron.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_message_lock_chevron@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_message_lock_chevron@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron.png new file mode 100644 index 000000000..332200ef8 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron@2x.png new file mode 100644 index 000000000..ff29e0808 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron@3x.png new file mode 100644 index 000000000..b9f5c04d5 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_chevron.imageset/voice_message_lock_chevron@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/Contents.json new file mode 100644 index 000000000..93de75077 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "voice_message_lock_icon_locked.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_message_lock_icon_locked@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_message_lock_icon_locked@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked.png new file mode 100644 index 000000000..35a6569c0 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked@2x.png new file mode 100644 index 000000000..615ab682d Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked@3x.png new file mode 100644 index 000000000..3108b7652 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_locked.imageset/voice_message_lock_icon_locked@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/Contents.json new file mode 100644 index 000000000..43a311cf4 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "voice_message_lock_icon_unlocked.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_message_lock_icon_unlocked@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_message_lock_icon_unlocked@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked.png new file mode 100644 index 000000000..1a3c05594 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked@2x.png new file mode 100644 index 000000000..bcd1b2a44 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked@3x.png new file mode 100644 index 000000000..a2087f349 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_lock_icon_unlocked.imageset/voice_message_lock_icon_unlocked@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/Contents.json new file mode 100644 index 000000000..bfc1545db --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "voice_message_pause_button.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_message_pause_button@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_message_pause_button@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button.png new file mode 100644 index 000000000..aa185b04b Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button@2x.png new file mode 100644 index 000000000..321565890 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button@3x.png new file mode 100644 index 000000000..93bdf2f28 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_pause_button.imageset/voice_message_pause_button@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/Contents.json new file mode 100644 index 000000000..3c88e866c --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "voice_message_play_button.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_message_play_button@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_message_play_button@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button.png new file mode 100644 index 000000000..274211f74 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button@2x.png new file mode 100644 index 000000000..0c8da2212 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button@3x.png new file mode 100644 index 000000000..22176d475 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_play_button.imageset/voice_message_play_button@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/Contents.json new file mode 100644 index 000000000..ead86edbb --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "action_voice_message.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "action_voice_message@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "action_voice_message@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message.png new file mode 100644 index 000000000..b969cb3aa Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message@2x.png new file mode 100644 index 000000000..32c6236a6 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message@3x.png new file mode 100644 index 000000000..e8cc54c29 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_default.imageset/action_voice_message@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/Contents.json new file mode 100644 index 000000000..900874ca1 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "voice_message_record_button_recording.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_message_record_button_recording@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_message_record_button_recording@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording.png new file mode 100644 index 000000000..8fa147c18 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording@2x.png new file mode 100644 index 000000000..f00a46204 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording@3x.png new file mode 100644 index 000000000..7fdf91c21 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_button_recording.imageset/voice_message_record_button_recording@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/Contents.json new file mode 100644 index 000000000..521b5c2dc --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "voice_message_record_icon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "voice_message_record_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "voice_message_record_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon.png new file mode 100644 index 000000000..fb7a1660c Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon@2x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon@2x.png new file mode 100644 index 000000000..72830fabe Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon@3x.png b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon@3x.png new file mode 100644 index 000000000..c792f8173 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/VoiceMessages/voice_message_record_icon.imageset/voice_message_record_icon@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/notifications.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/notifications.imageset/Contents.json new file mode 100644 index 000000000..66a053751 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/notifications.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "notifications.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "notifications@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "notifications@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications.png b/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications.png new file mode 100644 index 000000000..6b100741d Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications.png differ diff --git a/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications@2x.png b/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications@2x.png new file mode 100644 index 000000000..98d86c152 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications@3x.png b/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications@3x.png new file mode 100644 index 000000000..ddc1a6e7a Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/notifications.imageset/notifications@3x.png differ diff --git a/Riot/Assets/ar.lproj/Vector.strings b/Riot/Assets/ar.lproj/Vector.strings index 8687e7f26..7aa1dddd8 100644 --- a/Riot/Assets/ar.lproj/Vector.strings +++ b/Riot/Assets/ar.lproj/Vector.strings @@ -261,3 +261,88 @@ "room_participants_filter_room_members_for_dm" = "ุชูŽุตูููŠูŽุฉู ุงู„ุฃุนุถูŽุงุก"; "room_participants_filter_room_members" = "ุชูŽุตูููŠูŽุฉู ุฃุนุถูŽุงุกู ุงู„ุบูุฑููŽุฉ"; "room_participants_invite_prompt_msg" = "ู‡ูŽู„ ุฃู†ุชูŽ ู…ูุชูŽุฃูƒู‘ูุฏูŒ ุฃู†ู‘ูŽูƒูŽ ุชูŒุฑูŠุฏู ุฏูŽุนูˆุฉูŽ %@ ุฅู„ูŽู‰ ู‡ูŽุฐูู‡ู ุงู„ู…ูุญุงุฏูŽุซูŽุฉุŸ"; +"room_event_action_reaction_show_less" = "ุฅุธู‡ูŽุงุฑู ุฃู‚ูŽู„"; +"room_event_action_reaction_show_all" = "ุฅุธู‡ูŽุงุฑู ุงู„ูƒูู„"; +"room_event_action_edit" = "ุชูŽุญุฑููŠุฑ"; +"room_event_action_reply" = "ุงู„ุฑู‘ูŽุฏู‘"; +"room_event_action_view_encryption" = "ู…ูŽุนู„ููˆู…ูŽุงุชู ุงู„ุชู‘ูŽุนู…ููŠูŽุฉ"; +"room_event_action_cancel_download" = "ุฅู„ุบูŽุงุกู ุงู„ุชูŽู‘ู†ุฒูŠู„"; +"room_event_action_cancel_send" = "ุฅู„ุบูŽุงุกู ุงู„ุฅุฑุณูŽุงู„"; +"room_event_action_delete_confirmation_message" = "ู‡ูŽู„ ุฃู†ุชูŽ ู…ูุชูŽุฃูƒู‘ูุฏูŒ ุฃู†ู‘ูŽูƒูŽ ุชูŒุฑูŠุฏู ุญูŽุฐููŽ ู‡ูŽุฐูู‡ู ุงู„ุฑู‘ูุณูŽุงู„ูŽุฉูŽ ุบูŽูŠุฑู ุงู„ู…ูุฑุณูŽู„ูŽุฉุŸ"; +"room_event_action_delete_confirmation_title" = "ุญูŽุฐูู ุงู„ุฑู‘ูŽุณูŽุงุฆูู„ู ุบูŽูŠุฑู ุงู„ู…ูุฑุณูŽู„ูŽุฉ"; +"room_event_action_delete" = "ุญูŽุฐู"; +"room_event_action_resend" = "ุฅุนุงุฏูŽุฉู ุงู„ุฅุฑุณูŽุงู„"; +"room_event_action_save" = "ุญููุธ"; +"room_event_action_report_prompt_ignore_user" = "ู‡ูŽู„ ุชูุฑูŠุฏู ุฅุฎููŽุงุกูŽ ุฌูŽู…ูŠุนู ุงู„ุฑู‘ูŽุณูŽุงุฆูู„ู ู…ูู† ู‡ูŽุฐูŽุง ุงู„ู…ูุณุชูŽุฎุฏูู…ุŸ"; +"room_event_action_ban_prompt_reason" = "ุณูŽุจูŽุจู ุญูŽุธู’ุฑู ู‡ูŽุฐูŽุง ุงู„ู…ูุณุชูŽุฎุฏูู…"; +"room_event_action_kick_prompt_reason" = "ุณูŽุจูŽุจู ุทูŽุฑุฏู ู‡ูŽุฐูŽุง ุงู„ู…ูุณุชูŽุฎุฏูู…"; +"room_event_action_report_prompt_reason" = "ุณูŽุจูŽุจู ุงู„ุฅุจู„ูŽุงุบู ุนูŽู† ู‡ูŽุฐูŽุง ุงู„ู…ูุญุชูŽูˆู‰"; +"room_event_action_report" = "ุงู„ุชู‘ูŽุจู„ููŠุบู ุนูŽู†ู ุงู„ู…ูุญุชูŽูˆูŽู‰"; +"room_event_action_view_decrypted_source" = "ุงู„ุงูุทู‘ูู„ุงุนู ุนูŽู„ูŽู‰ ุงู„ู…ูŽุตุฏูŽุฑู ู…ูŽููƒููˆูƒู ุงู„ุชู‘ูŽุนู…ูŠูŽุฉ"; +"room_event_action_view_source" = "ุงู„ุงูุทู‘ูู„ุงุนู ุนูŽู„ูŽู‰ ุงู„ู…ูŽุตุฏูŽุฑ"; +"room_event_action_permalink" = "ุฑูŽุงุจูุทูŒ ุฏูŽุงุฆูู…"; +"room_event_action_share" = "ู…ูุดุงุฑูŽูƒูŽุฉ"; +"room_event_action_more" = "ู…ูŽุฒูŠุฏ"; +"room_event_action_redact" = "ุฅุฒุงู„ูŽุฉ"; +"room_event_action_quote" = "ุงูู‚ุชูุจูŽุงุณ"; +"room_event_action_copy" = "ู†ูŽุณุฎ"; +"room_delete_unsent_messages" = "ุญูŽุฐูู ุงู„ุฑู‘ูŽุณุงุฆูู„ู ุบูŽูŠุฑู ุงู„ู…ูุฑุณูŽู„ูŽุฉ"; +"room_resend_unsent_messages" = "ุฅุนุงุฏูŽุฉู ุฅุฑุณูŽุงู„ู ุงู„ุฑู‘ูŽุณุงุฆูู„ู ุบูŽูŠุฑู ุงู„ู…ูุฑุณูŽู„ูŽุฉ"; +"room_prompt_cancel" = "ุฅู„ุบูŽุงุกู ุงู„ูƒูู„"; +"room_prompt_resend" = "ุฅุนุงุฏูŽุฉู ุฅุฑุณูŽุงู„ู ุงู„ูƒูู„"; +"room_conference_call_no_power" = "ุชูŽุญุชูŽุงุฌู ุฅู„ูŽู‰ ุฅุฐู†ู ู„ูุฅุฏูŽุงุฑูŽุฉู ู…ููƒุงู„ูŽู…ูŽุฉู ุงูุฌุชูู…ูŽุงุนู ูููŠ ู‡ูŽุฐูู‡ู ุงู„ุบูุฑููŽุฉ"; +"room_ongoing_conference_call_with_close" = "ู…ููƒุงู„ูŽู…ูŽุฉู ุงูุฌุชูู…ูŽุงุนู ู…ูุณุชูŽู…ูุฑู‘ูŽุฉ. ุงู„ุงูู†ุถูู…ูŽุงู…ู ูƒูŽู€%@ ุฃูŽูˆ %@. %@ู‡ุง."; +"room_ongoing_conference_call_close" = "ุฅูุบู„ูŽุงู‚"; +"room_ongoing_conference_call" = "ู…ููƒุงู„ูŽู…ูŽุฉู ุงูุฌุชูู…ูŽุงุนู ู…ูุณุชูŽู…ูุฑู‘ูŽุฉ. ุงู„ุงูู†ุถูู…ูŽุงู…ู ูƒูŽู€%@ ุฃูŽูˆ %@."; +"room_unsent_messages_cancel_message" = "ู‡ูŽู„ ุฃู†ุชูŽ ู…ูุชูŽุฃูƒู‘ูุฏูŒ ุฃู†ู‘ูŽูƒูŽ ุชูŒุฑูŠุฏู ุญูŽุฐููŽ ุฌูŽู…ูŠุนู ุงู„ุฑู‘ูŽุณุงุฆูู„ู ุบูŽูŠุฑู ุงู„ู…ูุฑุณูŽู„ูŽุฉู ูููŠ ู‡ูŽุฐูู‡ู ุงู„ุบูุฑููŽุฉุŸ"; +"room_unsent_messages_cancel_title" = "ุญูŽุฐูู ุงู„ุฑู‘ูŽุณุงุฆูู„ู ุบูŽูŠุฑู ุงู„ู…ูุฑุณูŽู„ูŽุฉ"; +"room_unsent_messages_unknown_devices_notification" = "ููŽุดูŽู„ูŽ ุฅุฑุณูŽุงู„ู ุงู„ุฑู‘ูŽุณุงุฆูู„ู ุจูุณูŽุจูŽุจู ุฌูŽู„ุณูŽุงุชู ุญูŽุงู„ูŠุฉู ุบูŽูŠุฑู ู…ูŽุนุฑููˆููŽุฉ."; +"room_unsent_messages_notification" = "ููŽุดูŽู„ูŽ ุฅุฑุณูŽุงู„ู ุงู„ุฑู‘ูŽุณุงุฆูู„."; +"room_offline_notification" = "ููู‚ูุฏูŽ ุงู„ุงูุชู‘ูุตูŽุงู„ู ุจูุงู„ุฎูŽุงุฏูู…."; +"room_message_reply_to_short_placeholder" = "ุฅุฑุณูŽุงู„ู ุฑูŽุฏู‘โ€ฆ"; +"room_message_short_placeholder" = "ุฅุฑุณูŽุงู„ู ุฑูุณูŽุงู„ูŽุฉโ€ฆ"; +"room_participants_security_information_room_encrypted_for_dm" = "ุงู„ุฑู‘ูŽุณุงุฆูู„ู ู‡ูู†ุง ู…ูุนู…ูŽุงุฉูŒ ู…ูู†ูŽ ุงู„ู†ูู‘ู‡ุงูŠูŽุฉู ุฅู„ูŽู‰ ุงู„ู†ูู‘ู‡ุงูŠูŽุฉู. \n\nูŠูŽุชูู…ู ุชูŽุฃู…ูŠู†ู ุฑูŽุณุงุฆูู„ูŽูƒูŽ ุจูุฃู‚ููŽุงู„ุŒ ููŽู‚ูŽุท ุฃู†ุชูŽ ูˆูŽุงู„ู…ูุณุชูŽู„ูู…ู ู…ูŽู† ู„ูŽุฏูŠู‡ูู… ู…ููุชูŽุงุญูŽูŠู†ู ููŽุฑูŠุฏูŽูŠู†ู ู„ููุชุญูู‡ูŽุง."; +"room_participants_security_information_room_encrypted" = "ุงู„ุฑู‘ูŽุณุงุฆูู„ู ูููŠ ู‡ูŽุฐูู‡ู ุงู„ุบูุฑููŽุฉ ู…ูุนู…ูŽุงุฉูŒ ู…ูู†ูŽ ุงู„ู†ูู‘ู‡ุงูŠูŽุฉู ุฅู„ูŽู‰ ุงู„ู†ูู‘ู‡ุงูŠูŽุฉู. \n\nูŠูŽุชูู…ู ุชูŽุฃู…ูŠู†ู ุฑูŽุณุงุฆูู„ูŽูƒูŽ ุจูุฃู‚ููŽุงู„ุŒ ููŽู‚ูŽุท ุฃู†ุชูŽ ูˆูŽุงู„ู…ูุณุชูŽู„ูู…ู ู…ูŽู† ู„ูŽุฏูŠู‡ูู… ู…ููุชูŽุงุญูŽูŠู†ู ููŽุฑูŠุฏูŽูŠู†ู ู„ููุชุญูู‡ูŽุง."; +"room_participants_security_information_room_not_encrypted_for_dm" = "ุงู„ุฑู‘ูŽุณุงุฆูู„ู ู‡ูู†ุง ู„ูŽูŠุณูŽุช ู…ูุนู…ูŽุงุฉู‹ ู…ูู†ูŽ ุงู„ู†ูู‘ู‡ุงูŠูŽุฉู ุฅู„ูŽู‰ ุงู„ู†ูู‘ู‡ุงูŠูŽุฉู."; +"room_participants_security_information_room_not_encrypted" = "ุงู„ุฑู‘ูŽุณุงุฆูู„ู ูููŠ ู‡ูŽุฐูู‡ู ุงู„ุบูุฑููŽุฉ ู„ูŽูŠุณูŽุช ู…ูุนู…ูŽุงุฉู‹ ู…ูู†ูŽ ุงู„ู†ูู‘ู‡ุงูŠูŽุฉู ุฅู„ูŽู‰ ุงู„ู†ูู‘ู‡ุงูŠูŽุฉู."; +"encrypted_room_message_reply_to_placeholder" = "ุฅุฑุณูŽุงู„ู ุฑูŽุฏู‘ู ู…ูุนู…ูŽู‰โ€ฆ"; +"encrypted_room_message_placeholder" = "ุฅุฑุณูŽุงู„ู ุฑูุณูŽุงู„ูŽุฉู ู…ูุนู…ูŽุงุฉโ€ฆ"; +"room_do_not_have_permission_to_post" = "ู„ูŽูŠุณูŽ ู„ูŽุฏูŠูƒูŽ ุฅุฐู†ูŒ ุจูุงู„ู†ู‘ูŽุดุฑู ูููŠ ู‡ูŽุฐูู‡ู ุงู„ุบูุฑููŽุฉ"; +"room_message_editing" = "ุงู„ุชู‘ูŽุญุฑููŠุฑ"; +"room_message_replying_to" = "ุงู„ุฑู‘ูŽุฏู‘ู ุนูŽู„ูŽู‰ %@"; +"room_message_unable_open_link_error_message" = "ูŠูŽุชูŽุนูŽุฐู‘ูŽุฑู ููŽุชุญู ุงู„ุฑู‘ูŽุงุจูุท."; +"room_message_reply_to_placeholder" = "ุฅุฑุณูŽุงู„ู ุฑูŽุฏู‘ (ุบูŽูŠุฑู ู…ูุนูŽู…ูŽู‰)โ€ฆ"; +"room_message_placeholder" = "ุฅุฑุณูŽุงู„ู ุฑูุณูŽุงู„ูŽุฉ (ุบูŽูŠุฑู ู…ูุนูŽู…ูŽุงุฉ)โ€ฆ"; +"room_many_users_are_typing" = "ุฅู†ู‘ูŽ %@ุŒ %@ ูˆูŽุขุฎูŽุฑููˆู†ูŽ ูŠูŽูƒุชูุจููˆู†โ€ฆ"; +"room_two_users_are_typing" = "ุฅู†ู‘ูŽ %@ ูˆูŽ %@ ูŠูŽูƒุชูุจูŽุงู†โ€ฆ"; +"room_one_user_is_typing" = "ุฅู†ู‘ูŽ %@ ูŠูŽูƒุชูุจโ€ฆ"; +"room_new_messages_notification" = "ุนูŽุฏูŽุฏู %d ุฑูŽุณูŽุงุฆูู„ูŽ ุฌูŽุฏููŠุฏูŽุฉ"; +"room_new_message_notification" = "ุนูŽุฏูŽุฏู %d ุฑูุณูŽุงู„ูŽุฉู‹ ุฌูŽุฏููŠุฏูŽุฉ"; +"room_accessiblity_scroll_to_bottom" = "ุงู„ุชู‘ูŽู…ุฑููŠุฑู ุฅู„ูŽู‰ ุงู„ุฃูŽุณููŽู„"; +"room_jump_to_first_unread" = "ุงู„ู‚ูŽูุฒู ุฅู„ูŽู‰ ุบูŽูŠุฑู ุงู„ู…ูŽู‚ุฑููˆุก"; + +// Chat +"room_slide_to_end_group_call" = "ู…ูŽุฑู‘ูุฑู ู„ูุฅู†ู‡ูŽุงุกู ุงู„ู…ููƒุงู„ูŽู…ูŽุฉู ู„ูู„ุฌู‘ูŽู…ูŠุน"; +"room_member_power_level_short_custom" = "ู…ูุชูŽุฎูŽุตู‘ูุต"; +"room_member_power_level_short_moderator" = "ู…ูุดุฑูู"; +"room_member_power_level_short_admin" = "ู…ูุฏูŠุฑ"; +"room_member_power_level_custom_in" = "ู…ูุชูŽุฎูŽุตู‘ูุตูŒ (%@) ููŠ %@"; +"room_member_power_level_moderator_in" = "ู…ูุดุฑูููŒ ูููŠ %@"; +"room_member_power_level_admin_in" = "ู…ูุฏูŠุฑูŒ ูููŠ %@"; +"room_participants_security_loading" = "ุงู„ุชู‘ูŽุญู…ููŠู„ู ุฌูŽุงุฑูโ€ฆ"; +"room_participants_action_security_status_loading" = "ุงู„ุชู‘ูŽุญู…ููŠู„ู ุฌูŽุงุฑูโ€ฆ"; +"room_participants_action_security_status_warning" = "ุชูŽุญุฐููŠุฑ"; +"room_participants_action_security_status_complete_security" = "ุงู„ุฃู…ูŽุงู†ู ุงู„ูƒูŽุงู…ูู„"; +"room_participants_action_security_status_verify" = "ุชูŽุฃูƒููŠุฏู ุงู„ุชู‘ูŽุญูŽู‚ู‘ูู‚"; +"room_participants_action_security_status_verified" = "ู…ูุชูŽุญูŽู‚ู‘ูŽู‚ูŒ ู…ูู†ู‡"; +"room_participants_action_mention" = "ุฐููƒู’ุฑ"; +"room_participants_action_start_video_call" = "ุจูŽุฏู’ุกู ู…ููƒุงู„ูŽู…ูŽุฉู ู…ูŽุฑุฆูŠู‘ูŽุฉ"; +"room_participants_action_start_voice_call" = "ุจูŽุฏู’ุกู ู…ููƒุงู„ูŽู…ูŽุฉู ุตูŽูˆุชูŠู‘ูŽุฉ"; +"room_participants_action_start_new_chat" = "ุจูŽุฏู’ุกู ู…ูุญุงุฏูŽุซูŽุฉู ุฌูŽุฏููŠุฏูŽุฉ"; +"room_participants_action_set_admin" = "ุฌูŽุนู’ู„ูŽู‡ู ู…ูุฏููŠุฑ"; +"room_participants_action_set_moderator" = "ุฌูŽุนู’ู„ูŽู‡ู ู…ูุดุฑูู"; +"room_participants_action_set_default_power_level" = "ุฅุนูŽุงุฏูŽุฉู ุงู„ุถู‘ูŽุจุทู ุฅู„ูŽู‰ ู…ูุณุชูŽุฎุฏูู…ู ุนูŽุงุฏููŠู‘"; +"room_participants_action_unignore" = "ุฅูุธู’ู‡ูŽุงุฑู ุฌูŽู…ููŠุนู ุงู„ุฑู‘ูŽุณุงุฆูู„ู ู…ูู† ู‡ูŽุฐูŽุง ุงู„ู…ูุณุชูŽุฎุฏูู…"; +"room_participants_action_ignore" = "ุฅุฎููŽุงุกู ุฌูŽู…ููŠุนู ุงู„ุฑู‘ูŽุณุงุฆูู„ู ู…ูู† ู‡ูŽุฐูŽุง ุงู„ู…ูุณุชูŽุฎุฏูู…"; +"room_recents_unknown_room_error_message" = "ูŠูŽุชูŽุนูŽุฐู‘ูŽุฑ ุงู„ุนูุซููˆุฑ ุนูŽู„ูŽู‰ ู‡ูŽุฐูู‡ู ุงู„ุบูุฑููŽุฉ. ุชุฃูƒู‘ูŽุฏ ู…ูู† ูˆุฌูˆุฏูู‡ูŽุง"; +"room_creation_dm_error" = "ูŠูŽุชูŽุนูŽุฐู‘ูŽุฑู ุนูŽู„ูŠู†ูŽุง ุฅู†ุดูŽุงุก ุงู„ู…ูุญุงุฏูŽุซูŽุฉ ุงู„ู…ูุจุงุดูุฑูŽุฉ ุงู„ุฎู‘ูŽุงุตูŽุฉู ุจููƒ. ูŠูุฑุฌูŽู‰ ุงู„ุชู‘ูŽุญูŽู‚ูู‚ู ู…ูู†ูŽ ุงู„ู…ูุณุชูŽุฎุฏูู…ููŠูŽู†ูŽ ุงู„ู„ู‘ูŽุฐููŠู†ูŽ ุชูุฑูŠุฏู ุฏูŽุนูˆูŽุชูŽู‡ูู… ุซูู…ู‘ูŽ ุงู„ู…ูุญุงูˆูŽู„ูŽุฉู ู…ูŽุฑุฉู‹ ุฃูุฎุฑูŽู‰."; diff --git a/Riot/Assets/bg.lproj/Vector.strings b/Riot/Assets/bg.lproj/Vector.strings index d9fa91f5a..04a669524 100644 --- a/Riot/Assets/bg.lproj/Vector.strings +++ b/Riot/Assets/bg.lproj/Vector.strings @@ -106,7 +106,6 @@ // Room recents "room_recents_directory_section" = "ะ”ะ˜ะ ะ•ะšะขะžะ ะ˜ะฏ ะกะชะก ะกะขะะ˜"; "room_creation_invite_another_user" = "ะขัŠั€ัะธ ะฟะพั‚ั€ะตะฑะธั‚ะตะป ะฟะพ ID, ะธะผะต, ะธะผะตะนะป"; -"room_recents_directory_section_network" = "ะœั€ะตะถะฐ"; "room_recents_favourites_section" = "ะ›ะฎะ‘ะ˜ะœะ˜"; "room_recents_people_section" = "ะฅะžะ ะ"; "room_recents_conversations_section" = "ะกะขะะ˜"; diff --git a/Riot/Assets/ca.lproj/Vector.strings b/Riot/Assets/ca.lproj/Vector.strings index f11645924..75c7c7fe2 100644 --- a/Riot/Assets/ca.lproj/Vector.strings +++ b/Riot/Assets/ca.lproj/Vector.strings @@ -106,7 +106,6 @@ "room_creation_invite_another_user" = "Cerca / convida per l'identificador d'usuari, nom o correu electrรฒnic"; // Room recents "room_recents_directory_section" = "Directori de Sales"; -"room_recents_directory_section_network" = "Xarxa"; "room_recents_favourites_section" = "Favorits"; "room_recents_people_section" = "Contactes"; "room_recents_conversations_section" = "Sales"; diff --git a/Riot/Assets/cy.lproj/Vector.strings b/Riot/Assets/cy.lproj/Vector.strings index e1524bbc8..868341391 100644 --- a/Riot/Assets/cy.lproj/Vector.strings +++ b/Riot/Assets/cy.lproj/Vector.strings @@ -130,7 +130,6 @@ "room_creation_error_invite_user_by_email_without_identity_server" = "Nid oes unrhyw weinydd adnabod wedi'i osod felly ni allwch ychwanegu cyfranogwr gydag e-bost."; // Room recents "room_recents_directory_section" = "CYFEIRIADUR YSTAFELLOEDD"; -"room_recents_directory_section_network" = "Rhwydwaith"; "room_recents_favourites_section" = "FFEFRYNAU"; "room_recents_people_section" = "POBL"; "room_recents_conversations_section" = "YSTAFELLOEDD"; diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 52afbc691..df37ce7c2 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -79,7 +79,6 @@ "room_creation_wait_for_creation" = "Es wird gerade schon ein Raum erstellt. Bitte warten."; // Room recents "room_recents_directory_section" = "RAUM VERZEICHNIS"; -"room_recents_directory_section_network" = "Netzwerk"; "room_recents_favourites_section" = "FAVORITEN"; "room_recents_people_section" = "PERSONEN"; "room_recents_conversations_section" = "Rร„UME"; @@ -506,7 +505,7 @@ "group_rooms_filter_rooms" = "Filtere Community-Rรคume"; "e2e_room_key_request_message_new_device" = "Du hast die neue Sitzung '%@' hinzugefรผgt, welche Verschlรผsselungs-Schlรผssel anfordert."; "room_do_not_have_permission_to_post" = "Du hast keine Berechtigung Nachrichten in diesem Raum zu senden"; -"room_event_action_kick_prompt_reason" = "Grund fรผr den Rauswurf des Benutzers"; +"room_event_action_kick_prompt_reason" = "Grund fรผr das Entfernen des Benutzers"; "room_event_action_ban_prompt_reason" = "Grund fรผr die Verbannung des Benutzers"; "room_action_send_photo_or_video" = "Foto oder Video senden"; "room_action_send_sticker" = "Aufkleber senden"; @@ -1389,3 +1388,16 @@ "room_recents_unknown_room_error_message" = "Raum kann nicht gefunden werden. รœberprรผfe bitte, dass er existiert"; "room_creation_dm_error" = "Fehler beim Erstellen der Direktnachricht. Bitte รผberprรผfe die eingeladenen Leute und versuche es erneut."; "settings_ui_theme_picker_message_invert_colours" = "\"Auto\" verwendet die Farbinvertierungseinstellung deines Gerรคts"; +"key_verification_verify_qr_code_scan_code_other_device_action" = "Mit diesem Gerรคt scannen"; +"room_notifs_settings_encrypted_room_notice" = "Am Handy sind Benachrichtigungen bei Erwรคhnungen und Schlรผsselwรถrtern in verschlรผsselten Rรคumen nicht verfรผgbar."; +"room_notifs_settings_account_settings" = "Kontoeinstellungen"; +"room_notifs_settings_manage_notifications" = "Benachrichtigungen kannst du in %@ verwalten"; +"room_notifs_settings_cancel_action" = "Abbrechen"; +"room_notifs_settings_done_action" = "Fertig"; +"room_notifs_settings_none" = "Nichts"; +"room_notifs_settings_mentions_and_keywords" = "Nur Erwรคhnungen und Schlรผsselwรถrtern"; +"room_notifs_settings_all_messages" = "Allen Nachrichten"; + +// Room Notification Settings +"room_notifs_settings_notify_me_for" = "Benachrichtige mich bei"; +"room_details_notifs" = "Benachrichtigungen"; diff --git a/Riot/Assets/en.lproj/InfoPlist.strings b/Riot/Assets/en.lproj/InfoPlist.strings index 6387f123d..468d42cce 100644 --- a/Riot/Assets/en.lproj/InfoPlist.strings +++ b/Riot/Assets/en.lproj/InfoPlist.strings @@ -17,7 +17,7 @@ // Permissions usage explanations "NSCameraUsageDescription" = "The camera is used to take photos and videos, make video calls."; "NSPhotoLibraryUsageDescription" = "The photo library is used to send photos and videos."; -"NSMicrophoneUsageDescription" = "The microphone is used to take videos, make calls."; +"NSMicrophoneUsageDescription" = "Element needs to access your microphone to make and receive calls, take videos, and record voice messages."; "NSContactsUsageDescription" = "To discover contacts already using Matrix, Element can send email addresses and phone numbers in your address book to your chosen Matrix identity server. Where supported, personal data is hashed before sending - please check your identity server's privacy policy for more details."; "NSCalendarsUsageDescription" = "See your scheduled meetings in the app."; "NSFaceIDUsageDescription" = "Face ID is used to access your app."; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 234022cf2..80c55d989 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -182,7 +182,6 @@ // Room recents "room_recents_directory_section" = "ROOM DIRECTORY"; -"room_recents_directory_section_network" = "Network"; "room_recents_favourites_section" = "FAVOURITES"; "room_recents_people_section" = "PEOPLE"; "room_recents_conversations_section" = "ROOMS"; @@ -534,6 +533,7 @@ Tap the + to start adding people."; "settings_labs_create_conference_with_jitsi" = "Create conference calls with jitsi"; "settings_labs_message_reaction" = "React to messages with emoji"; "settings_labs_enable_ringing_for_group_calls" = "Ring for group calls"; +"settings_labs_voice_messages" = "Voice messages"; "settings_version" = "Version %@"; "settings_olm_version" = "Olm Version %@"; @@ -710,6 +710,7 @@ Tap the + to start adding people."; "room_details_topic" = "Topic"; "room_details_favourite_tag" = "Favourite"; "room_details_low_priority_tag" = "Low priority"; +"room_details_notifs" = "Notifications"; "room_details_mute_notifs" = "Mute notifications"; "room_details_direct_chat" = "Direct Chat"; "room_details_access_section"="Who can access this room?"; @@ -772,6 +773,17 @@ Tap the + to start adding people."; "room_details_copy_room_address" = "Copy Room Address"; "room_details_copy_room_url" = "Copy Room URL"; +// Room Notification Settings +"room_notifs_settings_notify_me_for" = "Notify me for"; +"room_notifs_settings_all_messages" = "All Messages"; +"room_notifs_settings_mentions_and_keywords" = "Mentions and Keywords only"; +"room_notifs_settings_none" = "None"; +"room_notifs_settings_done_action" = "Done"; +"room_notifs_settings_cancel_action" = "Cancel"; +"room_notifs_settings_manage_notifications" = "You can manage notifications in %@"; +"room_notifs_settings_account_settings" = "Account settings"; +"room_notifs_settings_encrypted_room_notice" = "Please note that mentions & keyword notifications are not available in encrypted rooms on mobile."; + // Group Details "group_details_title" = "Community Details"; "group_details_home" = "Home"; @@ -1375,6 +1387,7 @@ Tap the + to start adding people."; "key_verification_verify_qr_code_information_other_device" = "Scan the code below to verify:"; "key_verification_verify_qr_code_emoji_information" = "Verify by comparing unique emoji."; "key_verification_verify_qr_code_scan_code_action" = "Scan their code"; +"key_verification_verify_qr_code_scan_code_other_device_action" = "Scan with this device"; "key_verification_verify_qr_code_cannot_scan_action" = "Can't scan?"; "key_verification_verify_qr_code_start_emoji_action" = "Verify by emoji"; @@ -1668,3 +1681,9 @@ Tap the + to start adding people."; "side_menu_action_help" = "Help"; "side_menu_action_feedback" = "Feedback"; "side_menu_app_version" = "Version %@"; + +// Mark: - Voice Messages + +"voice_message_release_to_send" = "Hold to record, release to send"; +"voice_message_remaining_recording_time" = "%@s left"; +"voice_message_stop_locked_mode_recording" = "Tap on the wavelength to stop and playback"; diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 8a4575e64..bf7636ecb 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -244,7 +244,6 @@ "room_recents_server_notice_section" = "SISTEMAJ AVERTOJ"; "room_recents_low_priority_section" = "MALALTA PRIORITATO"; "room_recents_favourites_section" = "ELSTARIGITAJ"; -"room_recents_directory_section_network" = "Reto"; // Room recents "room_recents_directory_section" = "KATALOGO DE ฤˆAMBROJ"; diff --git a/Riot/Assets/es.lproj/Vector.strings b/Riot/Assets/es.lproj/Vector.strings index c14cdda6b..216722a92 100644 --- a/Riot/Assets/es.lproj/Vector.strings +++ b/Riot/Assets/es.lproj/Vector.strings @@ -144,7 +144,6 @@ "room_creation_invite_another_user" = "Buscar / invitar por ID de Usuario, Nombre o correo electrรณnico"; // Room recents "room_recents_directory_section" = "DIRECTORIO DE SALAS"; -"room_recents_directory_section_network" = "Red"; "room_recents_favourites_section" = "FAVORITOS"; "room_recents_people_section" = "PERSONAS"; "room_recents_conversations_section" = "SALAS"; diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 32ede1f7f..4844bde61 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -218,7 +218,6 @@ "auth_login_single_sign_on" = "Logi sisse"; // Room recents "room_recents_directory_section" = "JUTUTUBADE LOEND"; -"room_recents_directory_section_network" = "Vรตrk"; "room_recents_favourites_section" = "LEMMIKUD"; "room_recents_people_section" = "INIMESED"; "room_recents_conversations_section" = "JUTUTOAD"; @@ -1349,3 +1348,16 @@ "settings_ui_theme_picker_message_invert_colours" = "Automaatne valik kasutab sinu seadme pรถรถratud vรคrvide seadistust"; "room_recents_unknown_room_error_message" = "Ei leia sellist jututuba. Palun kontrolli, et ta ikka olemas on"; "room_creation_dm_error" = "Otsevestluse loomine ei รตnnestunud. Palun kontrolli, et kasutajanimed oleks รตiged ja proovi uuesti."; +"key_verification_verify_qr_code_scan_code_other_device_action" = "Skaneeri selle seadmega"; +"room_notifs_settings_encrypted_room_notice" = "Teavitused mainimiste ja vรตtmesรตnade esinemise puhul pole mobiilirakenduses krรผptitud jututoas saadaval."; +"room_notifs_settings_account_settings" = "Kasutajakonto seadistused"; +"room_notifs_settings_manage_notifications" = "Sa vรตid hallata teavitusi %@ jututoas"; +"room_notifs_settings_cancel_action" = "Katkesta"; +"room_notifs_settings_done_action" = "Valmis"; +"room_notifs_settings_none" = "mitte รผhelgi juhul"; +"room_notifs_settings_mentions_and_keywords" = "mainimiste ja vรตtmesรตnade leidumise puhul"; +"room_notifs_settings_all_messages" = "kรตikide sรตnumite puhul"; + +// Room Notification Settings +"room_notifs_settings_notify_me_for" = "Teavita mind"; +"room_details_notifs" = "Teavitused"; diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index 38a6e4d34..9278d4a1d 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -238,7 +238,6 @@ "room_creation_invite_another_user" = "Bilatu / gonbidatu erabiltzaile ID-a, izena edo e-maila erabiliz"; // Room recents "room_recents_directory_section" = "GELEN DIREKTORIOA"; -"room_recents_directory_section_network" = "Sarea"; "room_recents_favourites_section" = "GOGOKOAK"; "room_recents_invites_section" = "GONBIDAPENAK"; // People tab diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index f78010dfe..2f632e10c 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -97,7 +97,6 @@ "room_creation_invite_another_user" = "Rechercher/inviter par identifiant, nom ou e-mail"; // Room recents "room_recents_directory_section" = "Rร‰PERTOIRE DES SALONS"; -"room_recents_directory_section_network" = "Rรฉseau"; "room_recents_favourites_section" = "FAVORIS"; "room_recents_people_section" = "PERSONNES"; "room_recents_conversations_section" = "SALONS"; @@ -1426,3 +1425,16 @@ "settings_ui_theme_picker_message_invert_colours" = "ยซย Autoย ยป utilise le paramรจtre ยซย Inverser les couleursย ยป de lโ€™appreil"; "room_recents_unknown_room_error_message" = "Aucun rรฉsultat dans ce salon. Assurez vous de son existence"; "room_creation_dm_error" = "Nous nโ€™avons pas pu crรฉer votre message direct. Merci de vรฉrifier les utilisateurs que vous voulez inviter et rรฉessayer."; +"key_verification_verify_qr_code_scan_code_other_device_action" = "Scanner avec cet appareil"; +"room_notifs_settings_encrypted_room_notice" = "Veuillez noter que les mentions et mots-clรฉs ne sont pas disponibles dans les salons chiffrรฉs sur mobile."; +"room_notifs_settings_account_settings" = "Paramรจtres du compte"; +"room_notifs_settings_manage_notifications" = "Vous pouvez gรฉrer les notifications dans %@"; +"room_notifs_settings_cancel_action" = "Annuler"; +"room_notifs_settings_done_action" = "Terminรฉ"; +"room_notifs_settings_none" = "Aucun"; +"room_notifs_settings_mentions_and_keywords" = "Seulement les mentions et les mots-clรฉs"; +"room_notifs_settings_all_messages" = "Tous les messages"; + +// Room Notification Settings +"room_notifs_settings_notify_me_for" = "Me notifier pour"; +"room_details_notifs" = "Notifications"; diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 718f7de9b..0d516eb93 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -108,7 +108,6 @@ "room_creation_invite_another_user" = "Keresรฉs / meghรญvรกs felhasznรกlรณi azonosรญtรกs, nรฉv vagy e-mail cรญm alapjรกn"; // Room recents "room_recents_directory_section" = "SZOBA Kร–NYVTรR"; -"room_recents_directory_section_network" = "Hรกlรณzat"; "room_recents_favourites_section" = "KEDVENCEK"; "room_recents_people_section" = "EMBEREK"; "room_recents_conversations_section" = "SZOBรK"; diff --git a/Riot/Assets/is.lproj/Vector.strings b/Riot/Assets/is.lproj/Vector.strings index f6fcd78c3..bd4577fda 100644 --- a/Riot/Assets/is.lproj/Vector.strings +++ b/Riot/Assets/is.lproj/Vector.strings @@ -63,7 +63,6 @@ "room_creation_appearance_name" = "Heiti"; "room_creation_privacy" = "Meรฐferรฐ persรณnuupplรฝsinga"; "room_creation_make_private" = "Gera einka"; -"room_recents_directory_section_network" = "Netkerfi"; "room_recents_favourites_section" = "Eftirlรฆti"; "room_recents_people_section" = "Fร“LK"; "room_recents_conversations_section" = "SPJALLRรSIR"; diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index b6a2f0829..0a7d64e0e 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -112,7 +112,6 @@ "room_creation_invite_another_user" = "Cerca / invita per ID utente, nome o email"; // Room recents "room_recents_directory_section" = "ELENCO STANZE"; -"room_recents_directory_section_network" = "Rete"; "room_recents_favourites_section" = "PREFERITI"; "room_recents_people_section" = "CHAT DIRETTE"; "room_recents_conversations_section" = "STANZE"; @@ -1383,3 +1382,16 @@ "settings_ui_theme_picker_message_invert_colours" = "\"Automatico\" usa l'impostazione \"Inverti Colori\" del tuo dispositivo"; "room_recents_unknown_room_error_message" = "Impossibile trovare questa stanza. Assicurati che esista"; "room_creation_dm_error" = "Impossibile creare il messaggio diretto. Ricontrolla gli utenti che vuoi invitare e riprova."; +"key_verification_verify_qr_code_scan_code_other_device_action" = "Scansiona con questo dispositivo"; +"room_notifs_settings_encrypted_room_notice" = "Nota che le notifiche per menzioni e parole chiave non sono disponibili nelle stanze cifrate su mobile."; +"room_notifs_settings_account_settings" = "impostazioni dell'account"; +"room_notifs_settings_manage_notifications" = "Puoi gestire le notifiche nelle %@"; +"room_notifs_settings_cancel_action" = "Annulla"; +"room_notifs_settings_done_action" = "Fatto"; +"room_notifs_settings_none" = "Niente"; +"room_notifs_settings_mentions_and_keywords" = "Solo menzioni e parole chiave"; +"room_notifs_settings_all_messages" = "Tutti i messaggi"; + +// Room Notification Settings +"room_notifs_settings_notify_me_for" = "Inviami notifiche per"; +"room_details_notifs" = "Notifiche"; diff --git a/Riot/Assets/ja.lproj/Localizable.strings b/Riot/Assets/ja.lproj/Localizable.strings index 51a5c3d85..a2cbb08e6 100644 --- a/Riot/Assets/ja.lproj/Localizable.strings +++ b/Riot/Assets/ja.lproj/Localizable.strings @@ -50,3 +50,27 @@ "SINGLE_UNREAD_IN_ROOM" = "%@ใซใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ๅ—ใ‘ๅ–ใ‚Šใพใ—ใŸ"; /* A single unread message */ "SINGLE_UNREAD" = "ใ‚ใชใŸใฏใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ๅ—ใ‘ๅ–ใ‚Šใพใ—ใŸ"; + +/** Key verification **/ + +"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ใฏๆคœ่จผใ—ใŸใ„"; + +/* New message indicator on a room */ +"MESSAGE_IN_X" = "%@ ๅ†…ใฎใƒกใƒƒใ‚ปใƒผใ‚ธ"; + +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ ใ•ใ‚“ใ‹ใ‚‰ใฎใ‚นใ‚ฟใƒณใƒ—"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@๏ผˆ%@ ใ‹ใ‚‰๏ผ‰"; + +/* Group call from user, CallKit caller name */ +"GROUP_CALL_FROM_USER" = "%@ (ใ‚ฐใƒซใƒผใƒ—้€š่ฉฑ)"; +"MESSAGE_PROTECTED" = "ๆ–ฐใ—ใ„ใƒกใƒƒใ‚ปใƒผใ‚ธ"; + +/* New message indicator from a DM */ +"MESSAGE_FROM_X" = "%@ ใ‹ใ‚‰ใฎใƒกใƒƒใ‚ปใƒผใ‚ธ"; + +/** Notification messages **/ + +/* New message indicator on unknown room */ +"MESSAGE" = "ใƒกใƒƒใ‚ปใƒผใ‚ธ"; diff --git a/Riot/Assets/ja.lproj/Vector.strings b/Riot/Assets/ja.lproj/Vector.strings index a3d3a68e7..6ccb42bc3 100644 --- a/Riot/Assets/ja.lproj/Vector.strings +++ b/Riot/Assets/ja.lproj/Vector.strings @@ -2,7 +2,7 @@ "title_home" = "ใƒ›ใƒผใƒ "; "title_favourites" = "ใŠๆฐ—ใซๅ…ฅใ‚Š"; "title_people" = "ๅฏพ่ฉฑ"; -"title_rooms" = "้ƒจๅฑ‹"; +"title_rooms" = "ใƒซใƒผใƒ "; "warning" = "่ญฆๅ‘Š"; // Actions "view" = "่กจ็คบ"; @@ -92,39 +92,38 @@ "room_creation_account" = "ใ‚ขใ‚ซใ‚ฆใƒณใƒˆ"; "room_creation_appearance" = "ๅค–่ฆณ"; "room_creation_appearance_name" = "ๅๅ‰"; -"room_creation_appearance_picture" = "้ƒจๅฑ‹ใฎใ‚ขใ‚คใ‚ณใƒณ็”ปๅƒ (ไปปๆ„ใง)"; +"room_creation_appearance_picture" = "ใƒใƒฃใƒƒใƒˆ็”ปๅƒ (ไปปๆ„)"; "room_creation_privacy" = "ๅ€‹ไบบๆƒ…ๅ ฑไฟ่ญท"; "room_creation_private_room" = "ใ“ใฎไผš่ฉฑใฏ้žๅ…ฌ้–‹ใงใ™"; "room_creation_public_room" = "ใ“ใฎไผš่ฉฑใฏๅ…ฌ้–‹ใ•ใ‚Œใพใ™"; -"room_creation_make_public" = "ๅ…ฌ้–‹้ƒจๅฑ‹ไฝœๆˆ"; -"room_creation_make_public_prompt_title" = "ใ“ใฎ้ƒจๅฑ‹ใ‚’ๅ…ฌ้–‹ใ—ใพใ™ใ‹๏ผŸ"; -"room_creation_make_public_prompt_msg" = "ใ“ใฎ้ƒจๅฑ‹ใ‚’ไธ€่ˆฌๅ…ฌ้–‹ใ—ใฆใ‚‚ใ‚ˆใ‚ใ—ใ„ใงใ™ใ‹๏ผŸ่ชฐใงใ‚‚ใ‚ใชใŸใฎ็™บ่จ€ใ‚’่ชญใฟใ€้ƒจๅฑ‹ใซๅ‚ๅŠ ใงใใพใ™ใ€‚"; -"room_creation_keep_private" = "้žๅ…ฌ้–‹้ƒจๅฑ‹"; -"room_creation_make_private" = "้žๅ…ฌ้–‹้ƒจๅฑ‹ใ‚’ไฝœๆˆ"; -"room_creation_wait_for_creation" = "้ƒจๅฑ‹ใฏใ™ใงใซไฝœๆˆใ•ใ‚Œใฆใ„ใพใ™ใ€‚ใŠๅพ…ใกใใ ใ•ใ„ใ€‚"; +"room_creation_make_public" = "ใƒ‘ใƒ–ใƒชใƒƒใ‚ฏใซใ™ใ‚‹"; +"room_creation_make_public_prompt_title" = "ใ“ใฎใƒใƒฃใƒƒใƒˆใ‚’ใƒ‘ใƒ–ใƒชใƒƒใ‚ฏใ—ใพใ™ใ‹๏ผŸ"; +"room_creation_make_public_prompt_msg" = "ใ“ใฎใƒใƒฃใƒƒใƒˆใ‚’ใƒ‘ใƒ–ใƒชใƒƒใ‚ฏใ—ใฆใ‚‚ใ‚ˆใ‚ใ—ใ„ใงใ™ใ‹๏ผŸ ่ชฐใงใ‚‚ใ‚ใชใŸใฎใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’่ชญใ‚“ใงใƒใƒฃใƒƒใƒˆใซๅ‚ๅŠ ใงใใพใ™ใ€‚"; +"room_creation_keep_private" = "ใƒ—ใƒฉใ‚คใƒ™ใƒผใƒˆใซไฟใค"; +"room_creation_make_private" = "ใƒ—ใƒฉใ‚คใƒ™ใƒผใƒˆใซใ™ใ‚‹"; +"room_creation_wait_for_creation" = "ใƒซใƒผใƒ ใฏใ™ใงใซไฝœๆˆใ•ใ‚Œใฆใ„ใพใ™ใ€‚ ใŠๅพ…ใกใใ ใ•ใ„ใ€‚"; "room_creation_invite_another_user" = "ใƒฆใƒผใ‚ถID, ่กจ็คบๅ, ้›ปๅญใƒกใƒผใƒซใงๆคœ็ดขใจๆ‹›ๅพ…"; // Room recents -"room_recents_directory_section" = "้ƒจๅฑ‹ไธ€่ฆง"; -"room_recents_directory_section_network" = "้€šไฟกๅ›ž็ทš"; +"room_recents_directory_section" = "ใƒซใƒผใƒ ไธ€่ฆง"; "room_recents_favourites_section" = "ใŠๆฐ—ใซๅ…ฅใ‚Š"; "room_recents_people_section" = "ๅฏพ่ฉฑ"; -"room_recents_conversations_section" = "้ƒจๅฑ‹"; -"room_recents_no_conversation" = "้ƒจๅฑ‹ใŒใ‚ใ‚Šใพใ›ใ‚“"; +"room_recents_conversations_section" = "ใƒซใƒผใƒ "; +"room_recents_no_conversation" = "ใƒซใƒผใƒ ใŒใ‚ใ‚Šใพใ›ใ‚“"; "room_recents_low_priority_section" = "ไฝŽๅ„ชๅ…ˆๅบฆ"; "room_recents_invites_section" = "ๆ‹›ๅพ…ไธญ"; "room_recents_start_chat_with" = "ๅฏพ่ฉฑใ‚’้–‹ๅง‹"; -"room_recents_create_empty_room" = "้ƒจๅฑ‹ใ‚’ไฝœๆˆ"; -"room_recents_join_room" = "้ƒจๅฑ‹ใธๅ‚ๅŠ "; -"room_recents_join_room_title" = "้ƒจๅฑ‹ใธๅ‚ๅŠ "; -"room_recents_join_room_prompt" = "้ƒจๅฑ‹ใฎๅ›บๆœ‰IDใพใŸใฏไฝๆ‰€่กจ่จ˜ใ‚’ๅ…ฅๅŠ›"; +"room_recents_create_empty_room" = "ใƒซใƒผใƒ ใ‚’ไฝœๆˆ"; +"room_recents_join_room" = "ใƒซใƒผใƒ ใธๅ‚ๅŠ "; +"room_recents_join_room_title" = "ใƒซใƒผใƒ ใธๅ‚ๅŠ "; +"room_recents_join_room_prompt" = "ใƒซใƒผใƒ IDใพใŸใฏใƒซใƒผใƒ ใฎใ‚จใ‚คใƒชใ‚ขใ‚นใ‚’ๅ…ฅๅŠ›ใ—ใพใ™"; // People tab "people_invites_section" = "ๆ‹›ๅพ…ไธญ"; "people_conversation_section" = "ไผš่ฉฑ"; "people_no_conversation" = "ไผš่ฉฑใชใ—"; // Rooms tab -"room_directory_no_public_room" = "ๅ…ฌ้–‹ใ•ใ‚ŒใŸ้ƒจๅฑ‹ใŒใ‚ใ‚Šใพใ›ใ‚“"; +"room_directory_no_public_room" = "ๅˆฉ็”จๅฏ่ƒฝใชใƒ‘ใƒ–ใƒชใƒƒใ‚ฏใฎใƒซใƒผใƒ ใฏใ‚ใ‚Šใพใ›ใ‚“"; // Search -"search_rooms" = "้ƒจๅฑ‹"; +"search_rooms" = "ใƒซใƒผใƒ "; "search_messages" = "็™บ่จ€"; "search_people" = "ๅฏพ่ฉฑ"; "search_files" = "ๆทปไป˜ใƒ•ใ‚กใ‚คใƒซ"; @@ -133,12 +132,12 @@ "search_no_result" = "็ตๆžœใชใ—"; "search_in_progress" = "ๆคœ็ดขไธญโ€ฆ"; // Directory -"directory_cell_title" = "้ƒจๅฑ‹ไธ€่ฆงใ‚’่ฆ‹ใ‚‹"; -"directory_cell_description" = "%tu ้ƒจๅฑ‹"; -"directory_search_results_title" = "้ƒจๅฑ‹ไธ€่ฆงๆคœ็ดข็ตๆžœ"; +"directory_cell_title" = "ใƒซใƒผใƒ ไธ€่ฆงใ‚’่ฆ‹ใ‚‹"; +"directory_cell_description" = "%tu ใƒซใƒผใƒ "; +"directory_search_results_title" = "ใƒซใƒผใƒ ไธ€่ฆงๆคœ็ดข็ตๆžœ"; "directory_search_results" = "%tu ไปถใฎๆคœ็ดข็ตๆžœ for %@"; "directory_search_results_more_than" = ">%tu ไปถใฎๆคœ็ดข็ตๆžœ for %@"; -"directory_searching_title" = "้ƒจๅฑ‹ไธ€่ฆงใ‚’ๆคœ็ดขไธญโ€ฆ"; +"directory_searching_title" = "ใƒซใƒผใƒ ไธ€่ฆงใ‚’ๆคœ็ดขไธญโ€ฆ"; "directory_search_fail" = "ไธ€่ฆงใ‚’ๅ–ๅพ—ใงใใพใ›ใ‚“ใงใ—ใŸ"; // Contacts "contacts_address_book_section" = "็ซฏๆœซใฎ้›ป่ฉฑๅธณ"; @@ -153,13 +152,13 @@ "room_participants_add_participant" = "ๅ‚ๅŠ ่€…ใ‚’่ฟฝๅŠ "; "room_participants_one_participant" = "ๅ‚ๅŠ ่€… 1ๅ"; "room_participants_multi_participants" = "ๅ‚ๅŠ ่€… %dๅ"; -"room_participants_leave_prompt_title" = "้ƒจๅฑ‹ใ‚’้€€ๅฎค"; -"room_participants_leave_prompt_msg" = "้ƒจๅฑ‹ใ‚’้€€ๅฎคใ—ใฆๆœฌๅฝ“ใซใ‚ˆใ‚ใ—ใ„ใงใ™ใ‹๏ผŸ"; +"room_participants_leave_prompt_title" = "ใƒซใƒผใƒ ใ‚’ๅ‡บใ‚‹"; +"room_participants_leave_prompt_msg" = "ใƒซใƒผใƒ ใ‚’้€€ๅฎคใ—ใฆๆœฌๅฝ“ใซใ‚ˆใ‚ใ—ใ„ใงใ™ใ‹๏ผŸ"; "room_participants_remove_prompt_title" = "็ขบ่ช"; -"room_participants_remove_prompt_msg" = "ๆœฌๅฝ“ใซ %@ ใ•ใ‚“ใ‚’้ƒจๅฑ‹ใ‹ใ‚‰้€€ๅŽปใ•ใ›ใพใ™ใ‹๏ผŸ"; +"room_participants_remove_prompt_msg" = "ๆœฌๅฝ“ใซ %@ ใ•ใ‚“ใ‚’ใƒใƒฃใƒƒใƒˆใ‹ใ‚‰้€€ๅŽปใ•ใ›ใพใ™ใ‹๏ผŸ"; "room_participants_remove_third_party_invite_msg" = "ใ‚ตใƒผใƒ‰ใƒ‘ใƒผใƒ†ใ‚ฃใฎๆ‹›ๅพ…ใ‚’ๅ‰Š้™คใ™ใ‚‹ใ“ใจใฏใ€APIใŒๅญ˜ๅœจใ™ใ‚‹ใพใงใ‚ตใƒใƒผใƒˆใ•ใ‚Œใฆใ„ใพใ›ใ‚“"; "room_participants_invite_prompt_title" = "็ขบ่ช"; -"room_participants_invite_prompt_msg" = "ๆœฌๅฝ“ใซ %@ ใ•ใ‚“ใ‚’้ƒจๅฑ‹ใธๆ‹›ๅพ…ใ—ใพใ™ใ‹๏ผŸ"; +"room_participants_invite_prompt_msg" = "ๆœฌๅฝ“ใซ %@ ใ•ใ‚“ใ‚’ใƒใƒฃใƒƒใƒˆใธๆ‹›ๅพ…ใ—ใพใ™ใ‹๏ผŸ"; "room_participants_filter_room_members" = "ๅ‚ๅŠ ่€…ใ‚’ๆคœ็ดข"; "room_participants_invite_another_user" = "ใƒฆใƒผใ‚ถID, ่กจ็คบๅ, ้›ปๅญใƒกใƒผใƒซใงๆคœ็ดขใจๆ‹›ๅพ…"; "room_participants_invite_malformed_id_title" = "ๆ‹›ๅพ…ใ‚จใƒฉใƒผ"; @@ -176,9 +175,9 @@ "room_participants_action_section_devices" = "ใ‚ปใƒƒใ‚ทใƒงใƒณ"; "room_participants_action_section_other" = "ใ‚ชใƒ—ใ‚ทใƒงใƒณ"; "room_participants_action_invite" = "ๆ‹›ๅพ…"; -"room_participants_action_leave" = "้ƒจๅฑ‹ใ‚’้€€ๅฎค"; -"room_participants_action_remove" = "้ƒจๅฑ‹ใ‹ใ‚‰้€€ๅฎคใ•ใ›ใ‚‹"; -"room_participants_action_ban" = "ใ“ใฎ้ƒจๅฑ‹ใ‹ใ‚‰ใƒ–ใƒญใƒƒใ‚ฏใ™ใ‚‹"; +"room_participants_action_leave" = "ใ“ใฎใƒซใƒผใƒ ใ‚’ๅ‡บใ‚‹"; +"room_participants_action_remove" = "ใ“ใฎใƒซใƒผใƒ ใ‹ใ‚‰้€€ๅฎคใ•ใ›ใ‚‹"; +"room_participants_action_ban" = "ใ“ใฎใƒซใƒผใƒ ใ‹ใ‚‰ใƒ–ใƒญใƒƒใ‚ฏใ™ใ‚‹"; "room_participants_action_unban" = "ใƒ–ใƒญใƒƒใ‚ฏ่งฃ้™ค"; "room_participants_action_ignore" = "ใ“ใฎๅ‚ๅŠ ่€…ใฎ็™บ่จ€ใ‚’ๅ…จใฆ้ž่กจ็คบใซใ™ใ‚‹"; "room_participants_action_unignore" = "ใ“ใฎๅ‚ๅŠ ่€…ใฎ็™บ่จ€ใ‚’ๅ…จใฆ่กจ็คบใ™ใ‚‹"; @@ -205,7 +204,7 @@ "room_ongoing_conference_call" = "ไผš่ญฐ้€š่ฉฑๅฎŸๆ–ฝไธญใ€‚ %@ ใพใŸใฏ %@ใงๅ‚ๅŠ ใ—ใฆใใ ใ•ใ„ใ€‚"; "room_ongoing_conference_call_with_close" = "ไผš่ญฐ้€š่ฉฑๅฎŸๆ–ฝไธญใ€‚%@ใพใŸใฏ%@ใงๅ‚ๅŠ ใ—ใฆใใ ใ•ใ„ใ€‚ %@ใ€‚"; "room_ongoing_conference_call_close" = "้–‰ใ˜ใ‚‹"; -"room_conference_call_no_power" = "ใ“ใฎ้ƒจๅฑ‹ใงไผš่ญฐ้€š่ฉฑใ‚’็ฎก็†ใ™ใ‚‹ๆจฉ้™ใŒๅฟ…่ฆใงใ™"; +"room_conference_call_no_power" = "ใ“ใฎใƒซใƒผใƒ ใงไผš่ญฐ้€š่ฉฑใ‚’็ฎก็†ใ™ใ‚‹ๆจฉ้™ใŒๅฟ…่ฆใงใ™"; "room_prompt_resend" = "ๅ…จใฆๅ†้€ไฟก"; "room_prompt_cancel" = "ๅ…จใฆไธญๆญข"; "room_resend_unsent_messages" = "ๆœช้€ไฟกใฎๆ–‡ใ‚’ๅ†้€ไฟก"; @@ -307,7 +306,7 @@ "settings_labs_matrix_apps" = "Matrixใ‚ขใƒ—ใƒช"; "settings_labs_create_conference_with_jitsi" = "jitsiใฎไผš่ญฐ้€š่ฉฑใ‚’ไฝœๆˆใ™ใ‚‹"; "settings_version" = "Version %@"; -"settings_olm_version" = "Olm Version %@"; +"settings_olm_version" = "Olm ใƒใƒผใ‚ธใƒงใƒณ %@"; "settings_copyright" = "่‘—ไฝœๆจฉ"; "settings_term_conditions" = "ๅˆฉ็”จ่ฆ็ด„"; "settings_privacy_policy" = "ๅ€‹ไบบๆƒ…ๅ ฑไฟ่ญทๆ–น้‡"; @@ -323,7 +322,7 @@ "settings_password_updated" = "ใ‚ใชใŸใฎใƒ‘ใ‚นใƒฏใƒผใƒ‰ใฏๆ›ดๆ–ฐใ•ใ‚Œใพใ—ใŸ"; "settings_crypto_device_name" = "ใ‚ปใƒƒใ‚ทใƒงใƒณๅ: "; "settings_crypto_device_id" = "\nใ‚ปใƒƒใ‚ทใƒงใƒณID: "; -"settings_crypto_device_key" = "\n็ซฏๆœซ้ต: "; +"settings_crypto_device_key" = "\nใ‚ปใƒƒใ‚ทใƒงใƒณใ‚ญใƒผ:\n"; "settings_crypto_export" = "ๆš—ๅท้ตใ‚’ๅค–้ƒจใธไฟๅญ˜"; "settings_crypto_blacklist_unverified_devices" = "ๆคœ่จผใ•ใ‚ŒใŸใ‚ปใƒƒใ‚ทใƒงใƒณใฎใฟใงๆš—ๅทๅŒ–"; // Room Details @@ -354,7 +353,7 @@ "room_details_addresses_section" = "ไฝๆ‰€่กจ่จ˜"; "room_details_no_local_addresses" = "ใ“ใฎ้ƒจๅฑ‹ใฏใ‚ตใƒผใƒๅ†…ไฝๆ‰€่กจ่จ˜ใŒใ‚ใ‚Šใพใ›ใ‚“"; "room_details_new_address" = "ๆ–ฐใ—ใ„ไฝๆ‰€่กจ่จ˜ใ‚’่ฟฝๅŠ "; -"room_details_new_address_placeholder" = "ๆ–ฐใ—ใ„ไฝๆ‰€่กจ่จ˜ใ‚’่ฟฝๅŠ  (ไพ‹ #foo%@)"; +"room_details_new_address_placeholder" = "ๆ–ฐใ—ใ„ใ‚ขใƒ‰ใƒฌใ‚นใ‚’่ฟฝๅŠ ๏ผˆไพ‹ #foo%@๏ผ‰"; "room_details_addresses_invalid_address_prompt_title" = "ไฝๆ‰€่กจ่จ˜ใŒๆญฃใ—ใใ‚ใ‚Šใพใ›ใ‚“"; "room_details_addresses_invalid_address_prompt_msg" = "%@ ใฏๆญฃใ—ใใชใ„ๅฝขๅผใฎไฝๆ‰€่กจ่จ˜ใงใ™"; "room_details_addresses_disable_main_address_prompt_title" = "ไปฃ่กจไฝๆ‰€่กจ่จ˜ใฎ่ญฆๅ‘Š"; @@ -473,14 +472,14 @@ "group_invite_section" = "ๆ‹›ๅพ…"; "group_section" = "ใ‚ณใƒŸใƒฅใƒ‹ใƒ†ใ‚ฃ"; "room_message_reply_to_placeholder" = "่ฟ”ไฟกใ‚’้€ใ‚‹ (ๆš—ๅทๅŒ–ใ•ใ‚Œใฆใ„ใชใ„)โ€ฆ"; -"room_do_not_have_permission_to_post" = "ใ“ใฎ้ƒจๅฑ‹ใซๆŠ•็จฟใ™ใ‚‹ๆจฉ้™ใŒใ‚ใ‚Šใพใ›ใ‚“"; +"room_do_not_have_permission_to_post" = "ใ“ใฎใƒซใƒผใƒ ใซๆŠ•็จฟใ™ใ‚‹ๆจฉ้™ใŒใ‚ใ‚Šใพใ›ใ‚“"; "encrypted_room_message_reply_to_placeholder" = "ๆš—ๅทๅŒ–ใ•ใ‚ŒใŸ่ฟ”ไฟกใ‚’้€ใ‚‹โ€ฆ"; "room_message_reply_to_short_placeholder" = "่ฟ”ไฟกใ‚’้€ใ‚‹โ€ฆ"; "room_event_action_view_decrypted_source" = "ๅพฉๅทๅŒ–ใ•ใ‚ŒใŸใ‚ฝใƒผใ‚นใ‚’่ฆ‹ใ‚‹"; "room_event_action_kick_prompt_reason" = "ใ“ใฎใƒฆใƒผใ‚ถใƒผใ‚’่ฟฝๆ”พใ™ใ‚‹็†็”ฑ"; "room_action_send_photo_or_video" = "ๅ†™็œŸใ‹ๅ‹•็”ปใ‚’้€ใ‚‹"; "room_action_send_sticker" = "ใ‚นใ‚ฟใƒณใƒ—้€ไฟก"; -"room_replacement_information" = "ใ“ใฎ้ƒจๅฑ‹ใฏไบคๆ›ใ•ใ‚ŒใฆใŠใ‚Šใ€ใ‚‚ใ†ใ‚ขใ‚ฏใƒ†ใ‚ฃใƒ–ใงใฏใ‚ใ‚Šใพใ›ใ‚“ใ€‚"; +"room_replacement_information" = "ใ“ใฎใƒซใƒผใƒ ใฏไบคๆ›ใ•ใ‚ŒใฆใŠใ‚Šใ€ใ‚‚ใ†ใ‚ขใ‚ฏใƒ†ใ‚ฃใƒ–ใงใฏใ‚ใ‚Šใพใ›ใ‚“ใ€‚"; "room_replacement_link" = "ไผš่ฉฑใฏใ“ใ“ใง็ถšใ‘ใ‚‰ใ‚Œใพใ™ใ€‚"; "room_predecessor_information" = "ใ“ใฎ้ƒจๅฑ‹ใฏๅˆฅใฎไผš่ฉฑใฎ็ถšใใงใ™ใ€‚"; "room_predecessor_link" = "ใ‚ˆใ‚Šๅคใ„ใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’่ฆ‹ใ‚‹ใซใฏใ“ใ“ใ‚’ใ‚ฟใƒƒใƒ—ใ—ใฆใใ ใ•ใ„ใ€‚"; @@ -534,7 +533,7 @@ "e2e_room_key_request_title" = "ๆš—ๅทๅŒ–ใ‚ญใƒผ่ฆๆฑ‚"; "e2e_room_key_request_message_new_device" = "ๆš—ๅทๅŒ–ใ‚ญใƒผใ‚’่ฆๆฑ‚ใ—ใฆใ„ใ‚‹ๆ–ฐใ—ใ„็ซฏๆœซ '%@'ใ‚’่ฟฝๅŠ ใ—ใพใ—ใŸใ€‚"; "e2e_room_key_request_message" = "ๆคœ่จผใ•ใ‚Œใฆใ„ใชใ„็ซฏๆœซ '%@'ใŒๆš—ๅทๅŒ–ใ‚ญใƒผใ‚’่ฆๆฑ‚ใ—ใฆใ„ใพใ™ใ€‚"; -"e2e_room_key_request_start_verification" = "ๆคœ่จผ้–‹ๅง‹..."; +"e2e_room_key_request_start_verification" = "ๆคœ่จผใ‚’ๅง‹ใ‚ใพใ™โ€ฆ"; "e2e_room_key_request_share_without_verifying" = "ๆคœ่จผใ›ใšใซๅ…ฑๆœ‰"; "e2e_room_key_request_ignore_request" = "่ฆๆฑ‚ใ‚’็„ก่ฆ–"; // GDPR @@ -556,7 +555,7 @@ "rerequest_keys_alert_title" = "ใƒชใ‚ฏใ‚จใ‚นใƒˆ้€ไฟก"; "rerequest_keys_alert_message" = "ใ“ใฎ็ซฏๆœซใซใ‚ญใƒผใ‚’้€ไฟกใงใใ‚‹ใ‚ˆใ†ใซใ€ใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ๅพฉๅทๅŒ–ใงใใ‚‹ๅˆฅใฎ็ซฏๆœซใงElementใ‚’่ตทๅ‹•ใ—ใฆใใ ใ•ใ„ใ€‚"; "room_event_action_ban_prompt_reason" = "ใ“ใฎใƒฆใƒผใ‚ถใƒผใ‚’ใƒ–ใƒญใƒƒใ‚ฏใ™ใ‚‹็†็”ฑ"; -"room_resource_limit_exceeded_message_contact_1" = " Please "; +"room_resource_limit_exceeded_message_contact_1" = " ใŠ้ก˜ใ„ "; "settings_ui_theme_black" = "Black"; "settings_flair" = "็‰น่‰ฒใ‚’่กจ็คบใ™ใ‚‹"; // String for App Store @@ -599,8 +598,8 @@ // Mark: - Room creation introduction cell "room_intro_cell_add_participants_action" = "ๅ‚ๅŠ ่€…ใ‚’่ฟฝๅŠ "; -"room_participants_security_information_room_encrypted" = "ใ“ใฎ้ƒจๅฑ‹ใง้€ๅ—ไฟกใ•ใ‚Œใ‚‹ใƒกใƒƒใ‚ปใƒผใ‚ธใฏใ‚จใƒณใƒ‰ใƒ„ใƒผใ‚จใƒณใƒ‰ๆš—ๅทๅŒ–ใ•ใ‚Œใพใ™ใ€‚\n\nใƒกใƒƒใ‚ปใƒผใ‚ธใฏๅฎ‰ๅ…จใซไฟ่ญทใ•ใ‚ŒใฆใŠใ‚Šใ€ใ“ใฎ้ƒจๅฑ‹ใฎๅ‚ๅŠ ่€…ใฎใฟใŒใƒกใƒƒใ‚ปใƒผใ‚ธใฎ้–ฒ่ฆงใซๅฟ…่ฆใช้ตใ‚’ๆ‰€ๆŒใ—ใพใ™ใ€‚"; -"room_participants_security_information_room_not_encrypted" = "ใ“ใฎ้ƒจๅฑ‹ใง้€ๅ—ไฟกใ•ใ‚Œใ‚‹ใƒกใƒƒใ‚ปใƒผใ‚ธใฏใ‚จใƒณใƒ‰ใƒ„ใƒผใ‚จใƒณใƒ‰ๆš—ๅทๅŒ–ใ•ใ‚Œใพใ›ใ‚“ใ€‚"; +"room_participants_security_information_room_encrypted" = "ใ“ใฎใƒซใƒผใƒ ใง้€ๅ—ไฟกใ•ใ‚Œใ‚‹ใƒกใƒƒใ‚ปใƒผใ‚ธใฏใ‚จใƒณใƒ‰ใƒ„ใƒผใ‚จใƒณใƒ‰ๆš—ๅทๅŒ–ใ•ใ‚Œใพใ™ใ€‚\n\nใƒกใƒƒใ‚ปใƒผใ‚ธใฏๅฎ‰ๅ…จใซไฟ่ญทใ•ใ‚ŒใฆใŠใ‚Šใ€ใ“ใฎใƒซใƒผใƒ ใฎๅ‚ๅŠ ่€…ใฎใฟใŒใƒกใƒƒใ‚ปใƒผใ‚ธใฎ้–ฒ่ฆงใซๅฟ…่ฆใช้ตใ‚’ๆ‰€ๆŒใ—ใพใ™ใ€‚"; +"room_participants_security_information_room_not_encrypted" = "ใ“ใฎใƒซใƒผใƒ ใฎใƒกใƒƒใ‚ปใƒผใ‚ธใฏใ‚จใƒณใƒ‰ใƒ„ใƒผใ‚จใƒณใƒ‰ๆš—ๅทๅŒ–ใ•ใ‚Œใฆใ„ใพใ›ใ‚“ใ€‚"; "room_intro_cell_information_dm_sentence1_part3" = ". "; "callbar_active_and_single_paused" = "ใฒใจใคใฎใ‚ขใ‚ฏใƒ†ใ‚ฃใƒ–ใช้€š่ฉฑ (%@) ยท ใฒใจใคใฎไธ€ๆ™‚ๅœๆญขใ•ใ‚ŒใŸ้€š่ฉฑ"; @@ -729,7 +728,7 @@ "room_multiple_typing_notification" = "%@ใจใใฎไป–ใฎใƒฆใƒผใ‚ถใƒผใŒๅ…ฅๅŠ›ไธญใงใ™"; "external_link_confirmation_message" = "ใƒชใƒณใ‚ฏ%@ใฏๅˆฅใฎใ‚ตใ‚คใƒˆใซ็งปๅ‹•ใ—ใพใ™: %@\n\nๆœฌๅฝ“ใซ็ถšใ‘ใพใ™ใ‹๏ผŸ"; "room_event_action_delete_confirmation_title" = "ๆœช้€ไฟกใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ๅ‰Š้™ค"; -"room_unsent_messages_cancel_message" = "ใ“ใฎ้ƒจๅฑ‹ใซใ‚ใ‚‹ๆœช้€ไฟกใฎใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ใ™ในใฆๅ‰Š้™คใ—ใฆใ‚‚ใ‚ˆใ‚ใ—ใ„ใงใ™ใ‹๏ผŸ"; +"room_unsent_messages_cancel_message" = "ใ“ใฎใƒซใƒผใƒ ใซใ‚ใ‚‹ๆœช้€ไฟกใฎใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ใ™ในใฆๅ‰Š้™คใ—ใฆใ‚‚ใ‚ˆใ‚ใ—ใ„ใงใ™ใ‹๏ผŸ"; "room_unsent_messages_cancel_title" = "ๆœช้€ไฟกใƒกใƒƒใ‚ปใƒผใ‚ธใ‚’ๅ‰Š้™ค"; "room_message_replying_to" = "%@ใซ่ฟ”ไฟกไธญ"; "room_message_editing" = "็ทจ้›†ไธญ"; @@ -753,7 +752,7 @@ "room_participants_leave_prompt_msg_for_dm" = "้€€ๅ‡บใ—ใฆใ‚ˆใ‚ใ—ใ„ใงใ™ใ‹๏ผŸ"; "room_participants_leave_prompt_title_for_dm" = "้€€ๅ‡บใ™ใ‚‹"; "contacts_address_book_no_identity_server" = "IDใ‚ตใƒผใƒใƒผใŒ่จญๅฎšใ•ใ‚Œใฆใ„ใพใ›ใ‚“"; -"rooms_empty_view_information" = "ใƒซใƒผใƒ ใฏใƒ—ใƒฉใ‚คใƒ™ใƒผใƒˆใงใ‚‚ใƒ‘ใƒ–ใƒชใƒƒใ‚ฏใงใ‚‚ใ€ใ‚ใ‚‰ใ‚†ใ‚‹ใ‚ฐใƒซใƒผใƒ—ใƒใƒฃใƒƒใƒˆใซๆœ€้ฉใงใ™ใ€‚+ใ‚’ใ‚ฟใƒƒใƒ—ใ™ใ‚‹ใจๆ—ขใซใ‚ใ‚‹้ƒจๅฑ‹ใ‚’ๆŽขใ—ใŸใ‚Šใ€ๆ–ฐใ—ใ„้ƒจๅฑ‹ใ‚’ไฝœใ‚‹ใ“ใจใŒใงใใพใ™ใ€‚"; +"rooms_empty_view_information" = "ใƒซใƒผใƒ ใฏใƒ—ใƒฉใ‚คใƒ™ใƒผใƒˆใงใ‚‚ใƒ‘ใƒ–ใƒชใƒƒใ‚ฏใงใ‚‚ใ€ใ‚ใ‚‰ใ‚†ใ‚‹ใ‚ฐใƒซใƒผใƒ—ใƒใƒฃใƒƒใƒˆใซๆœ€้ฉใงใ™ใ€‚+ใ‚’ใ‚ฟใƒƒใƒ—ใ™ใ‚‹ใจใ€ๆ—ขใซใ‚ใ‚‹ใƒซใƒผใƒ ใ‚’่ฆ‹ใคใ‘ใŸใ‚Šใ€ๆ–ฐใ—ใ„ใƒซใƒผใƒ ใ‚’ไฝœใ‚‹ใ“ใจใŒใงใใพใ™ใ€‚"; "rooms_empty_view_title" = "ใƒซใƒผใƒ "; "people_empty_view_information" = "่ชฐใจใงใ‚‚ๅฎ‰ๅ…จใซใƒใƒฃใƒƒใƒˆใงใใพใ™ใ€‚๏ผ‹ใ‚’ใ‚ฟใƒƒใƒ—ใ™ใ‚‹ใจไผš่ฉฑ็›ธๆ‰‹ใ‚’่ฟฝๅŠ ใงใใพใ™ใ€‚"; "people_empty_view_title" = "ไบบใ€…"; diff --git a/Riot/Assets/kab.lproj/Vector.strings b/Riot/Assets/kab.lproj/Vector.strings index 812f4def9..1fa265a06 100644 --- a/Riot/Assets/kab.lproj/Vector.strings +++ b/Riot/Assets/kab.lproj/Vector.strings @@ -383,7 +383,6 @@ "room_recents_no_conversation" = "Ulac tixxamin"; "room_recents_conversations_section" = "TIXXAMIN"; "room_recents_people_section" = "IMDANEN"; -"room_recents_directory_section_network" = "Aแบ“eแนญแนญa"; "room_creation_make_private" = "Err-it d uslig"; "room_creation_privacy" = "Tabaแธnit"; "room_creation_appearance_name" = "Isem"; diff --git a/Riot/Assets/nb-NO.lproj/Localizable.strings b/Riot/Assets/nb-NO.lproj/Localizable.strings index 26102b751..5f6a77573 100644 --- a/Riot/Assets/nb-NO.lproj/Localizable.strings +++ b/Riot/Assets/nb-NO.lproj/Localizable.strings @@ -111,3 +111,9 @@ "MSG_FROM_USER" = "%@ sendte en melding"; /* Message title for a specific person in a named room */ "MSG_FROM_USER_IN_ROOM_TITLE" = "%@ i %@"; + +/* Group call from user, CallKit caller name */ +"GROUP_CALL_FROM_USER" = "%@ (Gruppeanrop)"; + +/* A user added a Jitsi call to a room */ +"GROUP_CALL_STARTED" = "Gruppeanrop startet"; diff --git a/Riot/Assets/nb-NO.lproj/Vector.strings b/Riot/Assets/nb-NO.lproj/Vector.strings index c47bf5f51..744f54033 100644 --- a/Riot/Assets/nb-NO.lproj/Vector.strings +++ b/Riot/Assets/nb-NO.lproj/Vector.strings @@ -64,7 +64,6 @@ "room_creation_appearance" = "Utseende"; "room_creation_appearance_name" = "Navn"; "room_creation_privacy" = "Personvern"; -"room_recents_directory_section_network" = "Nettverk"; "room_recents_create_empty_room" = "Opprett et rom"; "room_recents_join_room" = "Bli med i rommet"; "room_recents_join_room_title" = "Bli med i et rom"; @@ -479,7 +478,7 @@ "settings_discovery_error_message" = "Det oppstod en feil. Vennligst prรธv igjen."; "security_settings_crypto_sessions" = "MINE ร˜KTER"; "security_settings_secure_backup_setup" = "Sett opp"; -"security_settings_secure_backup_delete" = "Slett"; +"security_settings_secure_backup_delete" = "Slett sikkerhetskopi"; "security_settings_crosssigning_complete_security" = "Komplett sikkerhet"; "security_settings_cryptography" = "KRYPTOGRAFI"; "security_settings_complete_security_alert_title" = "Komplett sikkerhet"; @@ -513,7 +512,7 @@ "key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Bekreft passordfrasen"; "key_backup_setup_passphrase_confirm_passphrase_invalid" = "Passordfrasen samsvarer ikke"; "key_backup_setup_passphrase_set_passphrase_action" = "Velg passordfrase"; -"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Gjenopprettingsnรธkkel"; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Sikkerhetsnรธkkel"; "key_backup_setup_success_from_recovery_key_make_copy_action" = "Lag en kpi"; "key_backup_setup_success_from_recovery_key_made_copy_action" = "Jeg har laget en kopi"; "key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Skriv inn gjenopprettingsnรธkkelen"; @@ -834,7 +833,7 @@ "settings_key_backup_info_not_valid" = "Denne รธkten sikkerhetskopierer ikke dine nรธkler, men du har en eksisterende sikkerhetskopi du kan gjenopprette fra og legge til, for รฅ gรฅ videre."; "settings_key_backup_info_valid" = "Denne รธkten sikkerhetskopierer dine nรธkler."; "settings_key_backup_info_version" = "Sikkerhetskopi av nรธkler versjon : %@"; -"settings_key_backup_info_signout_warning" = "Fรธr du logger ut, koble denne sesjonen til sikkerhetskopi av nรธkler for รฅ unngรฅ tap av nรธkler som kanskje bare er lagret pรฅ denne enheten."; +"settings_key_backup_info_signout_warning" = "Sikkerhetskopier nรธklene dine fรธr du logger av for รฅ unngรฅ รฅ miste dem."; "settings_key_backup_info_none" = "Nรธklene dine for denne sesjonen blir ikke sikkerhetskopiert."; "settings_third_party_notices" = "Tredjepartsmerknader"; "settings_labs_e2e_encryption_prompt_message" = "Vennligst logg inn igjen for รฅ ferdigstille oppsett av kryptering."; @@ -864,7 +863,7 @@ "pin_protection_not_allowed_pin" = "Av sikkerhetsรฅrsaker er denne PIN-koden ikke tilgjengelig. Prรธv en annen PIN-kode"; "secrets_setup_recovery_passphrase_information" = "Skriv inn en sikkerhetsfrase bare du kjenner, brukes til รฅ sikre hemmeligheter pรฅ serveren."; "secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "bruke gjenopprettingsnรธkkelen din"; -"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "bruke gjenopprettingsnรธkkelen"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "bruk sikkerhetsnรธkkelen"; "room_details_access_section_for_dm" = "Hvem har tilgang til dette?"; "identity_server_settings_place_holder" = "Legg inn en identitetsserver"; "identity_server_settings_description" = "Du bruker for รธyeblikket %@ for รฅ finne og bli funnet av dine eksisterende kontakter."; @@ -920,13 +919,13 @@ "security_settings_crosssigning" = "KRYSS-SIGNERING"; "security_settings_backup" = "SIKKERHETSKOPI-MELDINGER"; "security_settings_secure_backup_synchronise" = "Synkroniser"; -"security_settings_secure_backup_description" = "Sikre deg mot รฅ miste tilgang til krypterte meldinger og data ved รฅ lagre sikkerhetskopi av krypteringsnรธkler pรฅ din server."; +"security_settings_secure_backup_description" = "Sikkerhetskopier krypteringsnรธklene med kontodataene dine hvis du mister tilgangen til รธktene dine. Nรธklene dine blir sikret med en unik sikkerhetsnรธkkel."; "security_settings_secure_backup" = "SIKKERHETSKOPI"; "security_settings_crosssigning_info_trusted" = "Kryss-signering er aktivert. Du kan stole pรฅ andre brukere og dine andre รธkter basert pรฅ kryss-signering, men du kan ikke kryss-signere fra denne รธkten fordi den ikke har private nรธkler for kryss-signering. Fullfรธr sikkerheten for denne รธkten."; "security_settings_export_keys_manually" = "Eksporter nรธkler manuelt"; -"security_settings_crosssigning_reset" = "Tilbakestill kryss-signering"; -"security_settings_crosssigning_bootstrap" = "Bootstrap kryss-signering"; -"security_settings_crosssigning_info_ok" = "Kryss-signering er aktivert."; +"security_settings_crosssigning_reset" = "Nullstill"; +"security_settings_crosssigning_bootstrap" = "Sett opp"; +"security_settings_crosssigning_info_ok" = "Kryss-signering er klar til bruk."; "security_settings_blacklist_unverified_devices" = "Aldri send meldinger til ikke-klarerte รธkter"; // AuthenticatedSessionViewControllerFactory @@ -1052,7 +1051,7 @@ "bug_report_prompt" = "Applikasjonen krasjet sist gang. Vil du sende inn en krasj-rapport?"; "public_room_section_title" = "Offentlige rom (pรฅ %@):"; "call_no_stun_server_error_message_2" = "Alternativt kan du prรธve รฅ bruke den offentlige serveren ved %@, men denne vil vรฆre mindre pรฅlitelig, og vil dele din IP-adresse med serveren. Du kan ogsรฅ administrere dette i innstillinger"; -"e2e_key_backup_wrong_version" = "Det har blitt oppdaget en ny sikkerhetskopi av meldingsnรธkler .\n\nHvis dette ikke ble intitiert av deg bรธr du endre passordfrase i innstillinger."; +"e2e_key_backup_wrong_version" = "Det har blitt oppdaget en ny sikkerhetskopi av meldingsnรธkler .\n\nHvis dette ikke var deg, angir du en ny sikkerhetsfrase i Innstillinger."; // Key backup wrong version "e2e_key_backup_wrong_version_title" = "Ny sikkerhetskopi av nรธkler"; @@ -1136,7 +1135,7 @@ "secure_key_backup_setup_intro_title" = "Sikkert lagringsomrรฅde"; "secure_key_backup_setup_intro_use_security_key_info" = "Generer en sikkerhetsnรธkkel og lagre den pรฅ et trygt sted som i en passordadministrator eller en safe."; "secure_key_backup_setup_intro_use_security_key_title" = "Bruk en sikkerhetsnรธkkel"; -"secure_key_backup_setup_intro_use_security_passphrase_title" = "Bruk en passordfrase"; +"secure_key_backup_setup_intro_use_security_passphrase_title" = "Bruk en sikkerhetsfrase"; "key_backup_setup_intro_manual_export_action" = "Eksporter nรธkler manuelt"; "key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Koble denne enheten til sikkerhetskopi av meldingsnรธkler"; "key_backup_setup_skip_alert_message" = "Du kan miste dine krypterte meldinger dersom du logger ut eller mister enheten."; @@ -1154,12 +1153,12 @@ "secure_key_backup_setup_existing_backup_error_info" = "Lรฅs den opp for รฅ gjenbruke den pรฅ sikkert lagringsomrรฅde, eller slett den for รฅ opprette en ny sikkerhetskopi av meldinger pรฅ sikkert lagringsomrรฅde."; "secure_key_backup_setup_existing_backup_error_title" = "Det finnes allerede en sikkerhetskopi for meldinger"; "secure_key_backup_setup_intro_use_security_passphrase_info" = "Skriv inn en hemmelig frase bare du vet, og generer en nรธkkel for sikkerhetskopiering."; -"key_backup_setup_passphrase_setup_recovery_key_info" = "Eller sikre sikkerhetskopien din med en gjenopprettingsnรธkkel, og lagre den pรฅ et trygt sted."; -"key_backup_setup_passphrase_info" = "Vi lagrer en kryptert kopi av nรธklene dine pรฅ serveren vรฅr. Beskytt sikkerhetskopien din med en passordfrase for รฅ holde den sikker.\n\nFor maksimal sikkerhet bรธr dette vรฆre forskjellig fra kontopassordet ditt."; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Eller, sikre sikkerhetskopien med en sikkerhetsnรธkkel, og lagre den et trygt sted."; +"key_backup_setup_passphrase_info" = "Vi lagrer en kryptert kopi av nรธklene dine pรฅ serveren vรฅr. Beskytt sikkerhetskopien med en setning for รฅ holde den sikker.\n\nFor maksimal sikkerhet bรธr dette vรฆre forskjellig fra kontopassordet ditt."; // Passphrase -"key_backup_setup_passphrase_title" = "Gjรธr sikkerhetskopien din sikker med en passordfrase"; +"key_backup_setup_passphrase_title" = "Sikre sikkerhetskopien din med en sikkerhetsfrase"; "key_backup_setup_intro_info" = "Meldinger i krypterte rom er sikret med ende-til-ende-kryptering. Bare du og mottakeren(e) har nรธklene til รฅ lese disse meldingene.\n\nLagre nรธklene dine pรฅ et trygt sted for รฅ unngรฅ รฅ miste dem."; // MARK: Key backup recover @@ -1168,29 +1167,29 @@ // Success from recovery key "key_backup_setup_success_from_recovery_key_info" = "Nรธklene dine blir sikkerhetskopiert.\n\nKopier denne gjenopprettingsnรธkkelen og lagre den pรฅ et trygt sted."; -"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Lagre gjenopprettingsnรธkkel"; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Lagre sikkerhetsnรธkkel"; // Success from passphrase "key_backup_setup_success_from_passphrase_info" = "Nรธklene dine blir sikkerhetskopiert.\n\nGjenopprettingsnรธkkelen din er et sikkerhetsnett - du kan bruke den til รฅ gjenopprette tilgangen til de krypterte meldingene dine hvis du glemmer passordfrasen.\n\nLagre gjenopprettingsnรธkkelen din pรฅ en trygg mรฅte, f.eks. ved hjelp av en passordadministrator (eller i en safe)."; -"key_backup_setup_passphrase_setup_recovery_key_action" = "(Avansert) Sett opp med gjenopprettingsnรธkkel"; -"key_backup_recover_invalid_passphrase" = "Sikkerhetskopi kunne ikke dekrypteres med denne passordfrasen: Vennligst sjekk at du har angitt riktig passordfrase."; -"key_backup_recover_invalid_passphrase_title" = "Feil gjenopprettingsfrase"; -"key_backup_recover_invalid_recovery_key" = "Sikkerhetskopi kunne ikke dekrypteres med denne nรธkkelen: bekreft at du skrev inn riktig gjenopprettingsnรธkkel."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Avansert) Sett opp med sikkerhetsnรธkkel"; +"key_backup_recover_invalid_passphrase" = "Sikkerhetskopiering kunne ikke dekrypteres med denne setningen: bekreft at du har skrevet riktig sikkerhetsfrase."; +"key_backup_recover_invalid_passphrase_title" = "Feil sikkerhetsfrase"; +"key_backup_recover_invalid_recovery_key" = "Sikkerhetskopiering kunne ikke dekrypteres med denne nรธkkelen: bekreft at du har angitt riktig sikkerhetsnรธkkel."; "key_backup_recover_invalid_recovery_key_title" = "Feil i gjenopprettingsnรธkkel"; // Recover from passphrase -"key_backup_recover_from_passphrase_info" = "Bruk gjenopprettingspassordet for รฅ lรฅse opp historikken for dine sikrede meldinger"; +"key_backup_recover_from_passphrase_info" = "Bruk sikkerhetsfrasen for รฅ lรฅse opp den sikre meldingsloggen"; // Recover from private key "key_backup_recover_from_private_key_info" = "Gjenoppretter sikkerhetskopi โ€ฆ"; -"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Kjenner du ikke gjenopprettingspassordet ditt? Du kan "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Kjenner du ikke sikkerhetsfrasen din? Du kan "; "key_backup_recover_from_passphrase_recover_action" = "Lรฅs opp historikk"; "key_backup_recover_from_passphrase_passphrase_placeholder" = "Skriv inn passordfrase"; // Recover from recovery key -"key_backup_recover_from_recovery_key_info" = "Bruk gjenopprettingsnรธkkel for รฅ lรฅse opp historikken for sikrede meldinger"; +"key_backup_recover_from_recovery_key_info" = "Bruk sikkerhetsnรธkkelen til รฅ lรฅse opp den sikre meldingsloggen"; "key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; "sign_out_non_existing_key_backup_alert_setup_secure_backup_action" = "Begynn รฅ bruke Sikkert lagringsomrรฅde"; "sign_out_non_existing_key_backup_alert_title" = "Du mister tilgangen til de krypterte meldingene dine hvis du logger ut nรฅ"; @@ -1418,3 +1417,50 @@ "room_intro_cell_information_multiple_dm_sentence2" = "Bare dere er i denne samtalen, med mindre noen av dere inviterer andre til รฅ bli med."; "room_intro_cell_information_dm_sentence2" = "Bare dere to er i denne samtalen, ingen andre kan bli med."; "room_intro_cell_information_dm_sentence1_part3" = ". "; +"side_menu_app_version" = "Versjon %@"; +"side_menu_action_feedback" = "Tilbakemelding"; +"side_menu_action_help" = "Hjelp"; +"side_menu_action_settings" = "Innstillinger"; +"side_menu_action_invite_friends" = "Inviter venner"; + +// Mark: - Side menu + +"side_menu_reveal_action_accessibility_label" = "Venstre panel"; +"user_avatar_view_accessibility_hint" = "Endre bruker avatar"; + +// Mark: - User avatar view + +"user_avatar_view_accessibility_label" = "avatar"; +"space_beta_announce_information" = "Plasser er en ny mรฅte รฅ gruppere rom og mennesker pรฅ. De er ikke pรฅ iOS ennรฅ, men du kan bruke dem nรฅ pรฅ nettet og pรฅ skrivebordet."; +"space_beta_announce_subtitle" = "Den nye versjonen av lokalsamfunn"; +"space_beta_announce_title" = "Plasser kommer snart"; +"space_beta_announce_badge" = "BETA"; +"space_feature_unavailable_information" = "Plasser er en ny mรฅte รฅ gruppere rom og mennesker pรฅ.\n\nDe kommer snart. For nรฅ, hvis du blir med pรฅ en annen plattform, vil du kunne fรฅ tilgang til alle rom du blir med her."; +"space_feature_unavailable_subtitle" = "Plasser er ikke pรฅ iOS ennรฅ, men du kan bruke dem nรฅ pรฅ nettet og pรฅ skrivebordet"; + +// Mark: - Spaces + +"space_feature_unavailable_title" = "Plasser er ikke her ennรฅ"; +"secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Skriv inn sikkerhetsnรธkkelen din for รฅ fortsette."; +"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Skriv inn sikkerhetsfrasen for รฅ fortsette."; + +// Success from secure backup +"key_backup_setup_success_from_secure_backup_info" = "Nรธklene dine blir sikkerhetskopiert."; +"event_formatter_group_call_incoming" = "%@ i %@"; +"event_formatter_group_call_leave" = "Forlat"; +"event_formatter_group_call_join" = "Bli med"; +"event_formatter_group_call" = "Gruppeanrop"; +"event_formatter_call_end_call" = "Avslutt samtale"; +"event_formatter_call_retry" = "Prรธv pรฅ nytt"; +"event_formatter_call_answer" = "Svar"; +"security_settings_secure_backup_restore" = "Gjenopprett fra sikkerhetskopi"; +"security_settings_secure_backup_reset" = "Nullstill"; +"security_settings_secure_backup_info_valid" = "Denne รธkten tar sikkerhetskopi av nรธklene dine."; +"security_settings_secure_backup_info_checking" = "Sjekkerโ€ฆ"; +"settings_ui_theme_picker_message_match_system_theme" = "\"Auto\" samsvarer med enhetens systemtema"; +"settings_ui_theme_picker_message_invert_colours" = "\"Auto\" bruker enhetens \"Inverter farger\" innstillinger"; + +// Chat +"room_slide_to_end_group_call" = "Skyv for รฅ avslutte samtalen for alle"; +"room_recents_unknown_room_error_message" = "Finner ikke dette rommet. Forsikre deg om at den eksisterer"; +"room_creation_dm_error" = "Vi kunne ikke opprette DM. Kontroller brukerne du vil invitere, og prรธv pรฅ nytt."; diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 4b144ba88..5c7a00cae 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -112,7 +112,6 @@ "room_creation_invite_another_user" = "Zoeken/uitnodigen met gebruikers-ID, naam of e-mailadres"; // Room recents "room_recents_directory_section" = "GESPREKSCATALOGUS"; -"room_recents_directory_section_network" = "Netwerk"; "room_recents_favourites_section" = "FAVORIETEN"; "room_recents_people_section" = "PERSONEN"; "room_recents_conversations_section" = "GESPREKKEN"; @@ -611,7 +610,7 @@ "room_does_not_exist" = "%@ bestaat niet"; // Key backup wrong version "e2e_key_backup_wrong_version_title" = "Nieuwe sleutelback-up"; -"e2e_key_backup_wrong_version" = "Er is een nieuwe sleutelback-up voor versleutelde berichten gedetecteerd.\n\nIndien deze niet van u komt, stel dan een nieuw wachtwoord in in de instellingen."; +"e2e_key_backup_wrong_version" = "Er is een nieuwe sleutelback-up voor versleutelde berichten gedetecteerd.\n\nIndien deze niet van u komt, stel dan een nieuw veiligheidswachtwoord in in de instellingen."; "e2e_key_backup_wrong_version_button_settings" = "Instellingen"; "e2e_key_backup_wrong_version_button_wasme" = "Ik was het"; "key_backup_setup_title" = "Sleutelback-up"; @@ -623,10 +622,10 @@ "key_backup_setup_intro_setup_action_without_existing_backup" = "Begin sleutelback-up te gebruiken"; "key_backup_setup_intro_manual_export_info" = "(Geavanceerd)"; "key_backup_setup_intro_manual_export_action" = "Sleutels handmatig exporteren"; -"key_backup_setup_passphrase_title" = "Beveilig uw back-up met een wachtwoord"; -"key_backup_setup_passphrase_info" = "We bewaren een versleutelde kopie van uw sleutels op onze server. Bescherm uw back-up met een wachtwoord om deze veilig te houden.\n\nVoor maximale beveiliging zou dit moeten verschillen van uw accountwachtwoord."; +"key_backup_setup_passphrase_title" = "Beveilig uw back-up met een veiligheidswachtwoord"; +"key_backup_setup_passphrase_info" = "We bewaren een versleutelde kopie van uw sleutels op onze server. Bescherm uw back-up met een veiligheidswachtwoord om deze veilig te houden.\n\nVoor maximale beveiliging zou dit moeten verschillen van uw accountwachtwoord."; "key_backup_setup_passphrase_passphrase_title" = "Invoeren"; -"key_backup_setup_passphrase_passphrase_placeholder" = "Voer wachtwoord in"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Wachtwoord invoeren"; "key_backup_setup_passphrase_passphrase_valid" = "Top!"; "key_backup_setup_passphrase_passphrase_invalid" = "Probeer nog een woord toe te voegen"; "key_backup_setup_passphrase_confirm_passphrase_title" = "Bevestigen"; @@ -634,35 +633,35 @@ "key_backup_setup_passphrase_confirm_passphrase_valid" = "Top!"; "key_backup_setup_passphrase_confirm_passphrase_invalid" = "Wachtwoorden komen niet overeen"; "key_backup_setup_passphrase_set_passphrase_action" = "Wachtwoord instellen"; -"key_backup_setup_passphrase_setup_recovery_key_info" = "Of beveilig uw back-up met een herstelsleutel, en bewaar deze op een veilige plaats."; -"key_backup_setup_passphrase_setup_recovery_key_action" = "(Geavanceerd) Instellen met herstelsleutel"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Of beveilig uw back-up met een veiligheidssleutel en bewaar deze op een veilige plaats."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Geavanceerd) Instellen met veiligheidssleutel"; "key_backup_setup_success_title" = "Klaar!"; // Success from passphrase -"key_backup_setup_success_from_passphrase_info" = "Er wordt een back-up van uw sleutels gemaakt.\n\nUw herstelsleutel is een veiligheidsnet - u kunt deze gebruiken om de toegang tot uw versleutelde berichten te herstellen als u uw wachtwoord zou vergeten.\n\nBewaar uw herstelsleutel op een heel veilig plaats, zoals een wachtwoordbeheerder (of een kluis)."; -"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Herstelsleutel opslaan"; +"key_backup_setup_success_from_passphrase_info" = "Er wordt een back-up van uw sleutels gemaakt.\n\nUw veiligheidssleutel is een veiligheidsnet - u kunt deze gebruiken om de toegang tot uw versleutelde berichten te herstellen als u uw wachtwoord zou vergeten.\n\nBewaar uw veiligheidssleutel op een heel veilige plaats, zoals een wachtwoordbeheerder (of een kluis)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Veiligheidssleutel opslaan"; "key_backup_setup_success_from_passphrase_done_action" = "Klaar"; // Success from recovery key -"key_backup_setup_success_from_recovery_key_info" = "Er wordt een back-up van uw sleutels gemaakt.\n\nMaak een kopie van deze herstelsleutel en bewaar deze op een veilige plaats."; -"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Herstelsleutel"; +"key_backup_setup_success_from_recovery_key_info" = "Er wordt een back-up van uw sleutels gemaakt.\n\nMaak een kopie van deze veiligheidssleutel en bewaar deze op een veilige plaats."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Veiligheidssleutel"; "key_backup_setup_success_from_recovery_key_make_copy_action" = "Maak een kopie"; "key_backup_setup_success_from_recovery_key_made_copy_action" = "Ik heb een kopie gemaakt"; "key_backup_recover_title" = "Versleutelde berichten"; -"key_backup_recover_invalid_passphrase_title" = "Onjuist herstelwachtwoord"; -"key_backup_recover_invalid_passphrase" = "De back-up kon niet ontsleuteld worden met dit wachtwoord: controleer of u het herstelwachtwoord juist hebt ingevoerd."; -"key_backup_recover_invalid_recovery_key_title" = "Herstelsleutel komt niet overeen"; -"key_backup_recover_invalid_recovery_key" = "De back-up kon niet ontsleuteld worden met deze sleutel: controleer of u de juiste herstelsleutel hebt ingevoerd."; -"key_backup_recover_from_passphrase_info" = "Gebruik uw herstelwachtwoord om uw versleutelde berichtgeschiedenis te ontgrendelen"; +"key_backup_recover_invalid_passphrase_title" = "Onjuist veiligheidswachtwoord"; +"key_backup_recover_invalid_passphrase" = "De back-up kon niet ontsleuteld worden met dit wachtwoord: controleer of u het veiligheidswachtwoord juist hebt ingevoerd."; +"key_backup_recover_invalid_recovery_key_title" = "Veiligheidssleutel komt niet overeen"; +"key_backup_recover_invalid_recovery_key" = "De back-up kon niet ontsleuteld worden met deze sleutel: controleer of u de juiste veiligheidssleutel hebt ingevoerd."; +"key_backup_recover_from_passphrase_info" = "Gebruik uw veiligheidswachtwoord om uw versleutelde berichtengeschiedenis te ontgrendelen"; "key_backup_recover_from_passphrase_passphrase_title" = "Invoeren"; -"key_backup_recover_from_passphrase_passphrase_placeholder" = "Voer wachtwoord in"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Wachtwoord invoeren"; "key_backup_recover_from_passphrase_recover_action" = "Geschiedenis ontgrendelen"; -"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Herstelwachtwoord vergeten? Dan kunt u "; -"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "uw herstelsleutel gebruiken"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Veiligheidswachtwoord vergeten? Dan kunt u "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "uw veiligheidssleutel gebruiken"; "key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; -"key_backup_recover_from_recovery_key_info" = "Gebruik uw herstelsleutel om uw versleutelde berichtgeschiedenis te ontgrendelen"; +"key_backup_recover_from_recovery_key_info" = "Gebruik uw veiligheidssleutel om uw versleutelde berichtengeschiedenis te ontgrendelen"; "key_backup_recover_from_recovery_key_recovery_key_title" = "Invoeren"; -"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Voer herstelsleutel in"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Veiligheidssleutel invoeren"; "key_backup_recover_from_recovery_key_recover_action" = "Geschiedenis ontgrendelen"; -"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Herstelsleutel verloren? U kunt er een nieuwe aanmaken in de instellingen."; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Veiligheidssleutel verloren? U kunt er een nieuwe aanmaken in de instellingen."; "key_backup_recover_success_info" = "Back-up hersteld!"; "key_backup_recover_done_action" = "Klaar"; "key_backup_setup_banner_title" = "Verlies nooit uw versleutelde berichten"; @@ -913,7 +912,7 @@ // MARK: - Favourites "favourites_empty_view_title" = "Favoriete gesprekken en personen"; -"home_empty_view_information" = "De alles-in-รฉรฉn veilige chat app voor teams, vrienden en organisaties. Klik op de onderstaande + knop om gesprekken te starten met personen en groepen."; +"home_empty_view_information" = "De alles-in-รฉรฉn veilige chat-app voor teams, vrienden en organisaties. Druk op de + knop hieronder om personen en gesprekken toe te voegen."; // MARK: - Home @@ -1032,32 +1031,32 @@ "secrets_setup_recovery_key_done_action" = "Klaar"; "secrets_setup_recovery_key_export_action" = "Opslaan"; "secrets_setup_recovery_key_loading" = "Ladenโ€ฆ"; -"secrets_setup_recovery_key_information" = "Bewaar uw Herstelsleutel op een veilige plek. Het kan gebruikt worden voor het ontgrendelen van uw versleutelde berichten en data."; -"secrets_recovery_with_key_invalid_recovery_key_message" = "Verifieer dat u de juiste herstelsleutel heeft ingevoerd."; +"secrets_setup_recovery_key_information" = "Bewaar uw veiligheidssleutel op een veilige plek. Deze kan gebruikt worden om uw versleutelde berichten en data te ontsleutelen."; +"secrets_recovery_with_key_invalid_recovery_key_message" = "Verifieer dat u de juiste veiligheidssleutel heeft ingevoerd."; "secrets_recovery_with_key_invalid_recovery_key_title" = "Geen toegang tot geheime opslag"; "secrets_recovery_with_key_recover_action" = "Gebruik sleutel"; -"secrets_recovery_with_key_recovery_key_placeholder" = "Voer de herstelsleutel in"; +"secrets_recovery_with_key_recovery_key_placeholder" = "Veiligheidssleutel invoeren"; "secrets_recovery_with_key_recovery_key_title" = "Invoeren"; -"secrets_recovery_with_key_information_verify_device" = "Gebruik uw herstelsleutel om dit apparaat te verifiรซren."; -"secrets_recovery_with_key_information_default" = "Ontvang toegang tot uw versleutelde berichtengeschiedenis en uw kruislings ondertekenen ID voor het verifiรซren van andere sessie door het invoeren van uw Herstelsleutel."; +"secrets_recovery_with_key_information_verify_device" = "Gebruik uw veiligheidssleutel om dit apparaat te verifiรซren."; +"secrets_recovery_with_key_information_default" = "Ontvang toegang tot uw versleutelde berichtengeschiedenis en kruislings ondertekenen voor het verifiรซren van andere sessie door het invoeren van uw veiligheidssleutel."; // Recover with key -"secrets_recovery_with_key_title" = "Herstelsleutel"; -"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Verifieer dat u het juiste Herstelwachtwoord heeft ingevoerd."; +"secrets_recovery_with_key_title" = "Veiligheidssleutel"; +"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Verifieer dat u het juiste veiligheidswachtwoord heeft ingevoerd."; "secrets_recovery_with_passphrase_invalid_passphrase_title" = "Geen toegang tot geheime opslag"; "secrets_recovery_with_passphrase_lost_passphrase_action_part3" = "."; -"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "uw Herstelsleutel gebruiken"; -"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Herstelwachtwoord vergeten? Dan kunt u "; -"secrets_recovery_with_passphrase_recover_action" = "Gebruik Wachtwoord"; -"secrets_recovery_with_passphrase_passphrase_placeholder" = "Voer uw Herstelwachtwoord in"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "uw veiligheidssleutel gebruiken"; +"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Veiligheidswachtwoord vergeten? Dan kunt u "; +"secrets_recovery_with_passphrase_recover_action" = "Gebruik wachtwoord"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Voer uw veiligheidswachtwoord in"; "secrets_recovery_with_passphrase_passphrase_title" = "Invoeren"; -"secrets_recovery_with_passphrase_information_verify_device" = "Gebruik uw Herstelwachtwoord om dit apparaat te verifiรซren."; -"secrets_recovery_with_passphrase_information_default" = "Ontvang toegang tot uw versleutelde berichtengeschiedenis en uw kruislings ondertekenen ID voor het verifiรซren van andere sessies door het invoeren van uw Herstelwachtwoord."; +"secrets_recovery_with_passphrase_information_verify_device" = "Gebruik uw veiligheidswachtwoord om dit apparaat te verifiรซren."; +"secrets_recovery_with_passphrase_information_default" = "Ontvang toegang tot uw versleutelde berichtengeschiedenis en kruislings ondertekenen voor het verifiรซren van andere sessies door het invoeren van uw veiligheidswachtwoord."; // Recover with passphrase -"secrets_recovery_with_passphrase_title" = "Herstelwachtwoord"; +"secrets_recovery_with_passphrase_title" = "Veiligheidswachtwoord"; "secrets_recovery_reset_action_part_2" = "Alles opnieuw instellen"; // MARK: - Secrets recovery @@ -1065,9 +1064,9 @@ "secrets_recovery_reset_action_part_1" = "Alle herstelopties vergeten of verloren? "; "user_verification_session_details_verify_action_other_user" = "Handmatig verifiรซren"; "user_verification_session_details_verify_action_current_user_manually" = "Handmatig middels een tekst"; -"user_verification_session_details_verify_action_current_user" = "Interactief Verifiรซren"; +"user_verification_session_details_verify_action_current_user" = "Interactief verifiรซren"; "user_verification_session_details_additional_information_untrusted_current_user" = "Als u zich niet heeft aangemeld bij deze sessie, is uw account wellicht geschonden."; -"user_verification_session_details_additional_information_untrusted_other_user" = "Totdat deze persoon de sessie vertrouwd zijn berichten gelabeld met een waarschuwing. Een alternatief is handmatig verifiรซren."; +"user_verification_session_details_additional_information_untrusted_other_user" = "Totdat deze persoon deze sessie vertrouwd, zijn berichten gelabeld met waarschuwingen. Een andere mogelijkheid is om de persoon handmatig te verifiรซren."; "user_verification_session_details_information_untrusted_other_user" = " heeft zich in een nieuwe sessie aangemeld:"; "user_verification_session_details_information_untrusted_current_user" = "Verifieer deze sessie om het als vertrouwd te markeren en het toegang te geven tot versleutelde berichten:"; "user_verification_session_details_information_trusted_other_user_part2" = " verifieer het:"; @@ -1180,8 +1179,8 @@ "key_verification_verify_sas_title_emoji" = "Vergelijk de emoji's"; "device_verification_self_verify_wait_recover_secrets_checking_availability" = "Controleren op andere verificatie mogelijkheden..."; "device_verification_self_verify_wait_recover_secrets_additional_information" = "Wanneer u geen toegang meer heeft tot een bestaande sessie"; -"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Uw Herstelwachtwoord of -sleutel gebruiken"; -"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Herstelsleutel gebruiken"; +"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Uw veiligheidswachtwoord of -sleutel gebruiken"; +"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Veiligheidssleutel gebruiken"; "device_verification_self_verify_wait_additional_information" = "Dit werkt met Element en andere Matrix-apps die kruislings ondertekenen ondersteunen."; "device_verification_self_verify_wait_information" = "Verifieer deze sessie vanaf een van uw andere sessies, om toegang te krijgen tot de versleutelde berichten.\n\nGebruik de laatste versie van Element op uw andere apparaten:"; "device_verification_self_verify_wait_new_sign_in_title" = "Verifieer deze login"; @@ -1266,7 +1265,7 @@ // Room widget permissions "room_widget_permission_title" = "Widget laden"; "widget_picker_manage_integrations" = "Beheer integratiesโ€ฆ"; -"widget_integration_manager_disabled" = "U moet een integratebeheerder inschakelen in uw instellingen"; +"widget_integration_manager_disabled" = "U moet integratiebeheer inschakelen in de instellingen"; "widget_menu_remove" = "Verwijderen voor iedereen"; "widget_menu_revoke_permission" = "Toegang intrekken voor mij"; "widget_menu_open_outside" = "Openen in browser"; @@ -1292,11 +1291,11 @@ "room_details_advanced_e2e_encryption_disabled_for_dm" = "Versleuteling is hier niet ingeschakeld."; "room_details_advanced_e2e_encryption_enabled_for_dm" = "Versleuteling is hier ingeschakeld"; "room_details_advanced_room_id_for_dm" = "ID:"; -"room_details_no_local_addresses_for_dm" = "Dit heeft geen lokaaladres"; +"room_details_no_local_addresses_for_dm" = "Geen lokaaladres bekend"; "room_details_access_section_directory_toggle_for_dm" = "Weergeven in publieke groepsgesprekkencatalogus"; "room_details_access_section_anyone_for_dm" = "Iedereen die de koppeling kent, inclusief gasten"; "room_details_access_section_anyone_apart_from_guest_for_dm" = "Iedereen die de koppeling kent, behalve gasten"; -"room_details_access_section_for_dm" = "Wie mag toegang hebben?"; +"room_details_access_section_for_dm" = "Wie heeft toegang?"; "room_details_room_name_for_dm" = "Naam"; "room_details_photo_for_dm" = "Foto"; "room_details_title_for_dm" = "Details"; @@ -1343,17 +1342,17 @@ "security_settings_cryptography" = "CRYPTOGRAFIE"; "security_settings_crosssigning_complete_security" = "Beveiliging afronden"; "security_settings_crosssigning_reset" = "Reset"; -"security_settings_crosssigning_bootstrap" = "Stel in"; -"security_settings_crosssigning_info_ok" = "Cross-signing is klaar voor gebruik."; +"security_settings_crosssigning_bootstrap" = "Instellen"; +"security_settings_crosssigning_info_ok" = "Kruiselings ondertekenen is klaar voor gebruik."; "security_settings_crosssigning_info_trusted" = "Kruislings ondertekenen is ingeschakeld. U kunt andere personen en sessies verifiรซren met kruislings ondertekenen, maar u kunt dit nog niet vanaf deze sessie doordat de versleutelingssleutel ontbreekt. Rond de beveiliging van deze sessie af."; "security_settings_crosssigning_info_exists" = "Uw account heeft een kruislings ondertekenen ID, maar is nog niet geverifieerd door deze sessie. Rond de beveiliging van deze sessie af."; "security_settings_crosssigning_info_not_bootstrapped" = "Kruislings ondertekenen is nog niet ingesteld."; "security_settings_crosssigning" = "KRUISLINGS ONDERTEKENEN"; "security_settings_backup" = "BERICHTENBACK-UP"; -"security_settings_secure_backup_delete" = "Verwijder backup"; +"security_settings_secure_backup_delete" = "Back-up verwijderen"; "security_settings_secure_backup_synchronise" = "Synchroniseren"; "security_settings_secure_backup_setup" = "Instellen"; -"security_settings_secure_backup_description" = "Waarborg uw toegang tot uw versleutelde berichten & data door de versleutelingssleutels op te slaan. Uw sleutels zullen worden beveiligd met een unieke beveiligingssleutel."; +"security_settings_secure_backup_description" = "Maak een back-up van uw versleutelingssleutel bij uw account data voor het geval u toegang verliest tot uw sessies. Uw sleutels zullen worden beveiligd met een unieke veiligheidssleutel."; "security_settings_secure_backup" = "VEILIGE BACK-UP"; "security_settings_crypto_sessions_description_2" = "Als u deze inlog niet herkent, verander uw wachtwoord en reset uw Veilige Back-up."; "security_settings_crypto_sessions_loading" = "Sessies ladenโ€ฆ"; @@ -1512,3 +1511,16 @@ "settings_ui_theme_picker_message_invert_colours" = "โ€˜Automatischโ€™ gebruikt de instelling โ€˜Kleurweergave omkerenโ€™ van uw apparaat"; "room_recents_unknown_room_error_message" = "Dit gesprek is niet gevonden. Controleer of het bestaat"; "room_creation_dm_error" = "Uw direct gesprek kon niet aangemaakt worden. Controleer de gebruikers die u wilt uitnodigen en probeer het opnieuw."; +"key_verification_verify_qr_code_scan_code_other_device_action" = "Scan met dit apparaat"; +"room_notifs_settings_encrypted_room_notice" = "Let op dat vermeldingen & trefwoorden-meldingen niet beschikbaar zijn in versleutelde gesprekken op mobiel."; +"room_notifs_settings_account_settings" = "Accountinstellingen"; +"room_notifs_settings_manage_notifications" = "U kunt uw meldingen beheren in %@"; +"room_notifs_settings_cancel_action" = "Annuleer"; +"room_notifs_settings_done_action" = "Klaar"; +"room_notifs_settings_none" = "Geen"; +"room_notifs_settings_mentions_and_keywords" = "Alleen vermeldingen en trefwoorden"; +"room_notifs_settings_all_messages" = "Alle berichten"; + +// Room Notification Settings +"room_notifs_settings_notify_me_for" = "Stuur een melding voor"; +"room_details_notifs" = "Meldingen"; diff --git a/Riot/Assets/pl.lproj/Vector.strings b/Riot/Assets/pl.lproj/Vector.strings index 9e57955c2..9fc3c92a9 100644 --- a/Riot/Assets/pl.lproj/Vector.strings +++ b/Riot/Assets/pl.lproj/Vector.strings @@ -86,7 +86,6 @@ "room_creation_appearance" = "Wyglฤ…d"; "room_creation_appearance_name" = "Nazwa"; "room_creation_privacy" = "Prywatnoล›ฤ‡"; -"room_recents_directory_section_network" = "Sieฤ‡"; "room_recents_favourites_section" = "ULUBIONE"; "room_recents_people_section" = "OSOBY"; "room_recents_conversations_section" = "POKOJE"; diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 9a9f7396b..5af654656 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -101,7 +101,6 @@ "room_creation_make_private" = "Fazer privado"; "room_creation_wait_for_creation" = "Uma sala jรก estรก sendo criada. Por favor espere."; "room_creation_invite_another_user" = "Buscar / convidar por ID de usuรกria(o), Nome ou email"; -"room_recents_directory_section_network" = "Rede"; "room_recents_favourites_section" = "FAVORITOS"; "room_recents_people_section" = "PESSOAS"; "room_recents_conversations_section" = "SALAS"; @@ -1380,3 +1379,16 @@ "settings_ui_theme_picker_message_invert_colours" = "\"Auto\" usa as configuraรงรตes \"Inverter Cores\" de seu dispositivo"; "room_recents_unknown_room_error_message" = "Nรฃo dรก para encontrar esta sala. Assegure que ela existe"; "room_creation_dm_error" = "Nรณs nรฃo conseguimos criar sua DM. Por favor cheque as/os usuรกrias(os) que vocรช quer convidar e tente de novo."; +"key_verification_verify_qr_code_scan_code_other_device_action" = "Scannar com este dispositivo"; +"room_notifs_settings_encrypted_room_notice" = "Por favor note que notificaรงรตes de menรงรตes & palavrachave nรฃo estรฃo disponรญveis em salas encriptadas no celular."; +"room_notifs_settings_account_settings" = "Configuraรงรตes de conta"; +"room_notifs_settings_manage_notifications" = "Vocรช pode gerenciar notificaรงรตes em %@"; +"room_notifs_settings_cancel_action" = "Cancelar"; +"room_notifs_settings_done_action" = "Feito"; +"room_notifs_settings_none" = "Nenhuma"; +"room_notifs_settings_mentions_and_keywords" = "Menรงรตes e Palavrachaves somente"; +"room_notifs_settings_all_messages" = "Todas as Mensagens"; + +// Room Notification Settings +"room_notifs_settings_notify_me_for" = "Notifique-me para"; +"room_details_notifs" = "Notificaรงรตes"; diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index 4b717f3f8..0ad6dbef6 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -98,7 +98,6 @@ "room_creation_invite_another_user" = "ะŸะพะธัะบ / ะฟั€ะธะณะปะฐัˆะตะฝะธะต ะฟะพ ะธะดะตะฝั‚ะธั„ะธะบะฐั‚ะพั€ัƒ ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั, ะธะผะตะฝะธ ะธะปะธ ะฐะดั€ะตััƒ ัะปะตะบั‚ั€ะพะฝะฝะพะน ะฟะพั‡ั‚ั‹"; // Room recents "room_recents_directory_section" = "ะšะะขะะ›ะžะ“ ะšะžะœะะะข"; -"room_recents_directory_section_network" = "ะกะตั‚ัŒ"; "room_recents_favourites_section" = "ะ˜ะ—ะ‘ะ ะะะะซะ•"; "room_recents_people_section" = "ะ›ะฎะ”ะ˜"; "room_recents_conversations_section" = "ะšะžะœะะะขะซ"; diff --git a/Riot/Assets/si.lproj/Localizable.strings b/Riot/Assets/si.lproj/Localizable.strings index 432026982..ac8956825 100644 --- a/Riot/Assets/si.lproj/Localizable.strings +++ b/Riot/Assets/si.lproj/Localizable.strings @@ -17,3 +17,6 @@ /* New message from a specific person, not referencing a room. Content included. */ "MSG_FROM_USER_WITH_CONTENT" = "%@: %@"; + +/* Group call from user, CallKit caller name */ +"GROUP_CALL_FROM_USER" = "%@ (เทƒเถธเท–เท„ เถ‡เถธเถญเท”เถธ)"; diff --git a/Riot/Assets/si.lproj/Vector.strings b/Riot/Assets/si.lproj/Vector.strings index 982e41a3b..59e0d02cf 100644 --- a/Riot/Assets/si.lproj/Vector.strings +++ b/Riot/Assets/si.lproj/Vector.strings @@ -1,3 +1,16 @@ // Titles "title_home" = "เถธเท”เถฝเทŠ เถดเท’เถงเท”เท€"; "warning" = "เถ…เท€เท€เทเถฏเถบเถบเท’"; +"join" = "เถ‘เถšเทŠเท€เถฑเทŠเถฑ"; +"save" = "เทƒเท”เถปเถšเท’เถฑเทŠเถฑ"; +"cancel" = "เถ…เท€เถฝเถ‚เถœเท” เถšเถปเถฑเทŠเถฑ"; +"remove" = "เถ‰เท€เถญเทŠ เถšเถปเถฑเทŠเถฑ"; +"leave" = "เท„เทเถปเถบเถฑเทŠเถฑ"; +"start" = "เถ…เถปเถนเถฑเทŠเถฑ"; +"create" = "เทƒเทเถฏเถฑเทŠเถฑ"; +"continue" = "เถ‰เถฏเท’เถปเท’เถบเถง"; +"back" = "เถ†เถดเทƒเท”"; +"next" = "เถŠเถฝเถŸ"; +"title_rooms" = "เถšเทเถธเถป"; +"title_people" = "เถธเท’เถฑเท’เทƒเท”เถฑเทŠ"; +"title_favourites" = "เถดเทŠโ€เถปเท’เถบเถญเถธเถบเท’เถฑเทŠ"; diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 0794ad097..295c8a932 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -84,7 +84,6 @@ "room_creation_make_private" = "Bรซje private"; // Room recents "room_recents_directory_section" = "DREJTORI DHOMASH"; -"room_recents_directory_section_network" = "Rrjet"; "room_recents_people_section" = "PERSONA"; "room_recents_conversations_section" = "DHOMA"; "room_recents_no_conversation" = "Sโ€™ka dhoma"; @@ -1401,3 +1400,16 @@ "settings_ui_theme_picker_message_invert_colours" = "โ€œAutoโ€ pรซrdor rregullimet โ€œKtheji Sรซ Prapthi Ngjyratโ€"; "room_recents_unknown_room_error_message" = "Sโ€™gjendet dot kjo dhomรซ. Sigurohuni se ekziston"; "room_creation_dm_error" = "Sโ€™mundรซm tรซ krijojmรซ dot MD-nรซ tuaj. Ju lutemi, kontrolloni pรซrdoruesit tรซ cilรซve doni tโ€™u dรซrgohet dhe riprovoni."; +"key_verification_verify_qr_code_scan_code_other_device_action" = "Skanoje me kรซtรซ pajisje"; +"room_notifs_settings_encrypted_room_notice" = "Ju lutemi, kini parasysh se njoftimet pรซr pรซrmendje & fjalรซkyรงe sโ€™mund tรซ kihen nรซ celular pรซr dhoma tรซ fshehtรซzuara."; +"room_notifs_settings_account_settings" = "Rregullime llogarie"; +"room_notifs_settings_manage_notifications" = "Njoftimet mund tโ€™i administroni qรซ nga %@"; +"room_notifs_settings_cancel_action" = "Anuloje"; +"room_notifs_settings_done_action" = "U bรซ"; +"room_notifs_settings_none" = "Asnjรซ"; +"room_notifs_settings_mentions_and_keywords" = "Vetรซm Pรซrmendje dhe Fjalรซkyรงe"; +"room_notifs_settings_all_messages" = "Krejt Mesazhet"; + +// Room Notification Settings +"room_notifs_settings_notify_me_for" = "Njoftomรซni pรซr"; +"room_details_notifs" = "Njoftime"; diff --git a/Riot/Assets/sv.lproj/Vector.strings b/Riot/Assets/sv.lproj/Vector.strings index 14a8abd72..1bbec2ef6 100644 --- a/Riot/Assets/sv.lproj/Vector.strings +++ b/Riot/Assets/sv.lproj/Vector.strings @@ -100,7 +100,6 @@ "room_creation_make_private" = "Gรถr privat"; "room_creation_wait_for_creation" = "Ett rum hรฅller redan pรฅ att skapas. Vรคnligen vรคnta."; "room_creation_invite_another_user" = "Sรถk / bjud in efter anvรคndar-ID, namn eller e-postadress"; -"room_recents_directory_section_network" = "Nรคtverk"; "room_recents_favourites_section" = "FAVORITER"; "room_recents_people_section" = "PERSONER"; "room_recents_conversations_section" = "RUM"; diff --git a/Riot/Assets/third_party_licenses.html b/Riot/Assets/third_party_licenses.html index 4dfc32ac7..6e491fdca 100644 --- a/Riot/Assets/third_party_licenses.html +++ b/Riot/Assets/third_party_licenses.html @@ -1688,7 +1688,203 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

- + +
  • + DSWaveformImage (https://github.com/dmrschmidt/DSWaveformImage) +

    + The MIT License (MIT) +

    + Copyright (c) 2013 Dennis Schmidt +

    + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: +

    + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. +

    + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +

    +
  • +
  • + ffmpeg-kit-ios-audio (https://github.com/tanersener/ffmpeg-kit) +

    +
    +                   GNU LESSER GENERAL PUBLIC LICENSE
    +                       Version 3, 29 June 2007
    +
    + Copyright (C) 2007 Free Software Foundation, Inc. 
    + Everyone is permitted to copy and distribute verbatim copies
    + of this license document, but changing it is not allowed.
    +
    +
    +  This version of the GNU Lesser General Public License incorporates
    +the terms and conditions of version 3 of the GNU General Public
    +License, supplemented by the additional permissions listed below.
    +
    +  0. Additional Definitions.
    +
    +  As used herein, "this License" refers to version 3 of the GNU Lesser
    +General Public License, and the "GNU GPL" refers to version 3 of the GNU
    +General Public License.
    +
    +  "The Library" refers to a covered work governed by this License,
    +other than an Application or a Combined Work as defined below.
    +
    +  An "Application" is any work that makes use of an interface provided
    +by the Library, but which is not otherwise based on the Library.
    +Defining a subclass of a class defined by the Library is deemed a mode
    +of using an interface provided by the Library.
    +
    +  A "Combined Work" is a work produced by combining or linking an
    +Application with the Library.  The particular version of the Library
    +with which the Combined Work was made is also called the "Linked
    +Version".
    +
    +  The "Minimal Corresponding Source" for a Combined Work means the
    +Corresponding Source for the Combined Work, excluding any source code
    +for portions of the Combined Work that, considered in isolation, are
    +based on the Application, and not on the Linked Version.
    +
    +  The "Corresponding Application Code" for a Combined Work means the
    +object code and/or source code for the Application, including any data
    +and utility programs needed for reproducing the Combined Work from the
    +Application, but excluding the System Libraries of the Combined Work.
    +
    +  1. Exception to Section 3 of the GNU GPL.
    +
    +  You may convey a covered work under sections 3 and 4 of this License
    +without being bound by section 3 of the GNU GPL.
    +
    +  2. Conveying Modified Versions.
    +
    +  If you modify a copy of the Library, and, in your modifications, a
    +facility refers to a function or data to be supplied by an Application
    +that uses the facility (other than as an argument passed when the
    +facility is invoked), then you may convey a copy of the modified
    +version:
    +
    +   a) under this License, provided that you make a good faith effort to
    +   ensure that, in the event an Application does not supply the
    +   function or data, the facility still operates, and performs
    +   whatever part of its purpose remains meaningful, or
    +
    +   b) under the GNU GPL, with none of the additional permissions of
    +   this License applicable to that copy.
    +
    +  3. Object Code Incorporating Material from Library Header Files.
    +
    +  The object code form of an Application may incorporate material from
    +a header file that is part of the Library.  You may convey such object
    +code under terms of your choice, provided that, if the incorporated
    +material is not limited to numerical parameters, data structure
    +layouts and accessors, or small macros, inline functions and templates
    +(ten or fewer lines in length), you do both of the following:
    +
    +   a) Give prominent notice with each copy of the object code that the
    +   Library is used in it and that the Library and its use are
    +   covered by this License.
    +
    +   b) Accompany the object code with a copy of the GNU GPL and this license
    +   document.
    +
    +  4. Combined Works.
    +
    +  You may convey a Combined Work under terms of your choice that,
    +taken together, effectively do not restrict modification of the
    +portions of the Library contained in the Combined Work and reverse
    +engineering for debugging such modifications, if you also do each of
    +the following:
    +
    +   a) Give prominent notice with each copy of the Combined Work that
    +   the Library is used in it and that the Library and its use are
    +   covered by this License.
    +
    +   b) Accompany the Combined Work with a copy of the GNU GPL and this license
    +   document.
    +
    +   c) For a Combined Work that displays copyright notices during
    +   execution, include the copyright notice for the Library among
    +   these notices, as well as a reference directing the user to the
    +   copies of the GNU GPL and this license document.
    +
    +   d) Do one of the following:
    +
    +       0) Convey the Minimal Corresponding Source under the terms of this
    +       License, and the Corresponding Application Code in a form
    +       suitable for, and under terms that permit, the user to
    +       recombine or relink the Application with a modified version of
    +       the Linked Version to produce a modified Combined Work, in the
    +       manner specified by section 6 of the GNU GPL for conveying
    +       Corresponding Source.
    +
    +       1) Use a suitable shared library mechanism for linking with the
    +       Library.  A suitable mechanism is one that (a) uses at run time
    +       a copy of the Library already present on the user's computer
    +       system, and (b) will operate properly with a modified version
    +       of the Library that is interface-compatible with the Linked
    +       Version.
    +
    +   e) Provide Installation Information, but only if you would otherwise
    +   be required to provide such information under section 6 of the
    +   GNU GPL, and only to the extent that such information is
    +   necessary to install and execute a modified version of the
    +   Combined Work produced by recombining or relinking the
    +   Application with a modified version of the Linked Version. (If
    +   you use option 4d0, the Installation Information must accompany
    +   the Minimal Corresponding Source and Corresponding Application
    +   Code. If you use option 4d1, you must provide the Installation
    +   Information in the manner specified by section 6 of the GNU GPL
    +   for conveying Corresponding Source.)
    +
    +  5. Combined Libraries.
    +
    +  You may place library facilities that are a work based on the
    +Library side by side in a single library together with other library
    +facilities that are not Applications and are not covered by this
    +License, and convey such a combined library under terms of your
    +choice, if you do both of the following:
    +
    +   a) Accompany the combined library with a copy of the same work based
    +   on the Library, uncombined with any other library facilities,
    +   conveyed under the terms of this License.
    +
    +   b) Give prominent notice with the combined library that part of it
    +   is a work based on the Library, and explaining where to find the
    +   accompanying uncombined form of the same work.
    +
    +  6. Revised Versions of the GNU Lesser General Public License.
    +
    +  The Free Software Foundation may publish revised and/or new versions
    +of the GNU Lesser General Public License from time to time. Such new
    +versions will be similar in spirit to the present version, but may
    +differ in detail to address new problems or concerns.
    +
    +  Each version is given a distinguishing version number. If the
    +Library as you received it specifies that a certain numbered version
    +of the GNU Lesser General Public License "or any later version"
    +applies to it, you have the option of following the terms and
    +conditions either of that published version or of any later version
    +published by the Free Software Foundation. If the Library as you
    +received it does not specify a version number of the GNU Lesser
    +General Public License, you may choose any version of the GNU Lesser
    +General Public License ever published by the Free Software Foundation.
    +
    +  If the Library as you received it specifies that a proxy can decide
    +whether future versions of the GNU Lesser General Public License shall
    +apply, that proxy's public statement of acceptance of any version is
    +permanent authorization for you to choose that version for the
    +Library.
    +        
    +
  • diff --git a/Riot/Assets/vi.lproj/Vector.strings b/Riot/Assets/vi.lproj/Vector.strings index 9b2d328c4..88fc75f85 100644 --- a/Riot/Assets/vi.lproj/Vector.strings +++ b/Riot/Assets/vi.lproj/Vector.strings @@ -105,7 +105,6 @@ "room_creation_invite_another_user" = "Tรฌm / mแปi bแบฑng ID ngฦฐแปi dรนng, tรชn hoแบทc email"; // Room recents "room_recents_directory_section" = "DANH MแปคC PHร’NG"; -"room_recents_directory_section_network" = "Mแบกng"; "room_recents_favourites_section" = "YรŠU THรCH"; "room_recents_people_section" = "DANH Bแบ "; "room_recents_conversations_section" = "PHร’NG"; diff --git a/Riot/Assets/zh_Hans.lproj/Vector.strings b/Riot/Assets/zh_Hans.lproj/Vector.strings index 43a665081..da074b767 100644 --- a/Riot/Assets/zh_Hans.lproj/Vector.strings +++ b/Riot/Assets/zh_Hans.lproj/Vector.strings @@ -96,7 +96,6 @@ "room_creation_invite_another_user" = "้€š่ฟ‡็”จๆˆท IDใ€ๅ็งฐๆˆ–็”ตๅญ้‚ฎไปถ่ฟ›่กŒๆœ็ดข/้‚€่ฏท"; // Room recents "room_recents_directory_section" = "่Šๅคฉๅฎค็›ฎๅฝ•"; -"room_recents_directory_section_network" = "็ฝ‘็ปœ"; "room_recents_favourites_section" = "ๆ”ถ่—ๅคน"; "room_recents_people_section" = "่”็ณปไบบ"; "room_recents_conversations_section" = "่Šๅคฉๅฎค"; diff --git a/Riot/Assets/zh_Hant.lproj/Vector.strings b/Riot/Assets/zh_Hant.lproj/Vector.strings index 685722293..de6fbf1ab 100644 --- a/Riot/Assets/zh_Hant.lproj/Vector.strings +++ b/Riot/Assets/zh_Hant.lproj/Vector.strings @@ -184,7 +184,6 @@ "room_creation_invite_another_user" = "้€้Žไฝฟ็”จ่€…IDใ€ๅ็จฑใ€้›ปๅญ้ƒตไปถๅœฐๅ€ไพ†ๆœๅฐ‹/้‚€่ซ‹"; // Room recents "room_recents_directory_section" = "่Šๅคฉๅฎค็›ฎ้Œ„"; -"room_recents_directory_section_network" = "็ถฒ่ทฏ"; "room_recents_favourites_section" = "ๆ”ถ่—ๅคพ"; "room_recents_people_section" = "่ฏ็ตกไบบ"; "room_recents_conversations_section" = "่Šๅคฉๅฎค"; diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index a85ea69be..11a0fa5a1 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -397,7 +397,7 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = } // Move this view in front - [self.contentView bringSubviewToFront:self.bubbleOverlayContainer]; + [self.bubbleOverlayContainer.superview bringSubviewToFront:self.bubbleOverlayContainer]; } else { diff --git a/Riot/Coordinators/Coordinator.swift b/Riot/Coordinators/Coordinator.swift index 4d7f27b6a..3345c2f94 100755 --- a/Riot/Coordinators/Coordinator.swift +++ b/Riot/Coordinators/Coordinator.swift @@ -19,7 +19,7 @@ import UIKit /// Protocol describing a [Coordinator](http://khanlou.com/2015/10/coordinators-redux/). /// Coordinators are the objects which control the navigation flow of the application. /// It helps to isolate and reuse view controllers and pass dependencies down the navigation hierarchy. -protocol Coordinator: class { +protocol Coordinator: AnyObject { /// Starts job of the coordinator. func start() diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index afc57884a..709ed216d 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -85,6 +85,7 @@ internal enum Asset { internal static let roomActionFavourite = ImageAsset(name: "room_action_favourite") internal static let roomActionLeave = ImageAsset(name: "room_action_leave") internal static let roomActionNotification = ImageAsset(name: "room_action_notification") + internal static let roomActionNotificationMuted = ImageAsset(name: "room_action_notification_muted") internal static let roomActionPriorityHigh = ImageAsset(name: "room_action_priority_high") internal static let roomActionPriorityLow = ImageAsset(name: "room_action_priority_low") internal static let homeEmptyScreenArtwork = ImageAsset(name: "home_empty_screen_artwork") @@ -134,6 +135,15 @@ internal enum Asset { internal static let videoCall = ImageAsset(name: "video_call") internal static let voiceCallHangonIcon = ImageAsset(name: "voice_call_hangon_icon") internal static let voiceCallHangupIcon = ImageAsset(name: "voice_call_hangup_icon") + internal static let voiceMessageCancelGradient = ImageAsset(name: "voice_message_cancel_gradient") + internal static let voiceMessageLockChevron = ImageAsset(name: "voice_message_lock_chevron") + internal static let voiceMessageLockIconLocked = ImageAsset(name: "voice_message_lock_icon_locked") + internal static let voiceMessageLockIconUnlocked = ImageAsset(name: "voice_message_lock_icon_unlocked") + internal static let voiceMessagePauseButton = ImageAsset(name: "voice_message_pause_button") + internal static let voiceMessagePlayButton = ImageAsset(name: "voice_message_play_button") + internal static let voiceMessageRecordButtonDefault = ImageAsset(name: "voice_message_record_button_default") + internal static let voiceMessageRecordButtonRecording = ImageAsset(name: "voice_message_record_button_recording") + internal static let voiceMessageRecordIcon = ImageAsset(name: "voice_message_record_icon") internal static let addMemberFloatingAction = ImageAsset(name: "add_member_floating_action") internal static let addParticipant = ImageAsset(name: "add_participant") internal static let addParticipants = ImageAsset(name: "add_participants") @@ -144,6 +154,7 @@ internal enum Asset { internal static let membersListIcon = ImageAsset(name: "members_list_icon") internal static let modIcon = ImageAsset(name: "mod_icon") internal static let moreReactions = ImageAsset(name: "more_reactions") + internal static let notifications = ImageAsset(name: "notifications") internal static let scrollup = ImageAsset(name: "scrollup") internal static let roomsEmptyScreenArtwork = ImageAsset(name: "rooms_empty_screen_artwork") internal static let roomsEmptyScreenArtworkDark = ImageAsset(name: "rooms_empty_screen_artwork_dark") diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index c66d40f6a..0b77710ce 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -172,6 +172,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: RoomInfoListViewController.self) } + internal enum RoomNotificationSettingsViewController: StoryboardType { + internal static let storyboardName = "RoomNotificationSettingsViewController" + + internal static let initialScene = InitialSceneType(storyboard: RoomNotificationSettingsViewController.self) + } internal enum SecretsRecoveryWithKeyViewController: StoryboardType { internal static let storyboardName = "SecretsRecoveryWithKeyViewController" diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index ba05c0233..3de870902 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2014,6 +2014,10 @@ internal enum VectorL10n { internal static var keyVerificationVerifyQrCodeScanCodeAction: String { return VectorL10n.tr("Vector", "key_verification_verify_qr_code_scan_code_action") } + /// Scan with this device + internal static var keyVerificationVerifyQrCodeScanCodeOtherDeviceAction: String { + return VectorL10n.tr("Vector", "key_verification_verify_qr_code_scan_code_other_device_action") + } /// QR code has been successfully validated. internal static var keyVerificationVerifyQrCodeScanOtherCodeSuccessMessage: String { return VectorL10n.tr("Vector", "key_verification_verify_qr_code_scan_other_code_success_message") @@ -2710,6 +2714,10 @@ internal enum VectorL10n { internal static var roomDetailsNoLocalAddressesForDm: String { return VectorL10n.tr("Vector", "room_details_no_local_addresses_for_dm") } + /// Notifications + internal static var roomDetailsNotifs: String { + return VectorL10n.tr("Vector", "room_details_notifs") + } /// Members internal static var roomDetailsPeople: String { return VectorL10n.tr("Vector", "room_details_people") @@ -3018,6 +3026,42 @@ internal enum VectorL10n { internal static var roomNoPrivilegesToCreateGroupCall: String { return VectorL10n.tr("Vector", "room_no_privileges_to_create_group_call") } + /// Account settings + internal static var roomNotifsSettingsAccountSettings: String { + return VectorL10n.tr("Vector", "room_notifs_settings_account_settings") + } + /// All Messages + internal static var roomNotifsSettingsAllMessages: String { + return VectorL10n.tr("Vector", "room_notifs_settings_all_messages") + } + /// Cancel + internal static var roomNotifsSettingsCancelAction: String { + return VectorL10n.tr("Vector", "room_notifs_settings_cancel_action") + } + /// Done + internal static var roomNotifsSettingsDoneAction: String { + return VectorL10n.tr("Vector", "room_notifs_settings_done_action") + } + /// Please note that mentions & keyword notifications are not available in encrypted rooms on mobile. + internal static var roomNotifsSettingsEncryptedRoomNotice: String { + return VectorL10n.tr("Vector", "room_notifs_settings_encrypted_room_notice") + } + /// You can manage notifications in %@ + internal static func roomNotifsSettingsManageNotifications(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_notifs_settings_manage_notifications", p1) + } + /// Mentions and Keywords only + internal static var roomNotifsSettingsMentionsAndKeywords: String { + return VectorL10n.tr("Vector", "room_notifs_settings_mentions_and_keywords") + } + /// None + internal static var roomNotifsSettingsNone: String { + return VectorL10n.tr("Vector", "room_notifs_settings_none") + } + /// Notify me for + internal static var roomNotifsSettingsNotifyMeFor: String { + return VectorL10n.tr("Vector", "room_notifs_settings_notify_me_for") + } /// Connectivity to the server has been lost. internal static var roomOfflineNotification: String { return VectorL10n.tr("Vector", "room_offline_notification") @@ -3314,10 +3358,6 @@ internal enum VectorL10n { internal static var roomRecentsDirectorySection: String { return VectorL10n.tr("Vector", "room_recents_directory_section") } - /// Network - internal static var roomRecentsDirectorySectionNetwork: String { - return VectorL10n.tr("Vector", "room_recents_directory_section_network") - } /// FAVOURITES internal static var roomRecentsFavouritesSection: String { return VectorL10n.tr("Vector", "room_recents_favourites_section") @@ -4334,6 +4374,10 @@ internal enum VectorL10n { internal static var settingsLabsMessageReaction: String { return VectorL10n.tr("Vector", "settings_labs_message_reaction") } + /// Voice messages + internal static var settingsLabsVoiceMessages: String { + return VectorL10n.tr("Vector", "settings_labs_voice_messages") + } /// Mark all messages as read internal static var settingsMarkAllAsRead: String { return VectorL10n.tr("Vector", "settings_mark_all_as_read") @@ -4838,6 +4882,18 @@ internal enum VectorL10n { internal static var voice: String { return VectorL10n.tr("Vector", "voice") } + /// Hold to record, release to send + internal static var voiceMessageReleaseToSend: String { + return VectorL10n.tr("Vector", "voice_message_release_to_send") + } + /// %@s left + internal static func voiceMessageRemainingRecordingTime(_ p1: String) -> String { + return VectorL10n.tr("Vector", "voice_message_remaining_recording_time", p1) + } + /// Tap on the wavelength to stop and playback + internal static var voiceMessageStopLockedModeRecording: String { + return VectorL10n.tr("Vector", "voice_message_stop_locked_mode_recording") + } /// Warning internal static var warning: String { return VectorL10n.tr("Vector", "warning") diff --git a/Riot/Managers/Call/CallPresenter.swift b/Riot/Managers/Call/CallPresenter.swift index 4f5ad7e13..940ac98f3 100644 --- a/Riot/Managers/Call/CallPresenter.swift +++ b/Riot/Managers/Call/CallPresenter.swift @@ -393,7 +393,9 @@ class CallPresenter: NSObject { if let oldCallVC = self.callVCs.values.first, self.presentedCallVC == nil, !self.uiOperationQueue.containsPresentCallVCOperation, - !self.uiOperationQueue.containsEnterPiPOperation { + !self.uiOperationQueue.containsEnterPiPOperation, + let oldCall = oldCallVC.mxCall, + oldCall.state != .ended { // present the call screen after dismissing this one self.presentCallVC(oldCallVC) } diff --git a/Riot/Managers/Call/CallPresenterDelegate.swift b/Riot/Managers/Call/CallPresenterDelegate.swift index 9c7f44687..a9f6df03e 100644 --- a/Riot/Managers/Call/CallPresenterDelegate.swift +++ b/Riot/Managers/Call/CallPresenterDelegate.swift @@ -17,7 +17,7 @@ import Foundation @objc -protocol CallPresenterDelegate: class { +protocol CallPresenterDelegate: AnyObject { // Call screens func callPresenter(_ presenter: CallPresenter, presentCallViewController viewController: UIViewController, diff --git a/Riot/Managers/Call/PiPView.swift b/Riot/Managers/Call/PiPView.swift index 8f5a0587a..54a199044 100644 --- a/Riot/Managers/Call/PiPView.swift +++ b/Riot/Managers/Call/PiPView.swift @@ -23,7 +23,7 @@ import UIKit case topLeft } -@objc protocol PiPViewDelegate: class { +@objc protocol PiPViewDelegate: AnyObject { @objc optional func pipView(_ view: PiPView, didMoveTo position: PiPViewPosition) @objc optional func pipViewDidTap(_ view: PiPView) } diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 533702c2b..7d41bc4df 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -52,6 +52,7 @@ final class RiotSettings: NSObject { static let roomCreationScreenRoomIsPublic = "roomCreationScreenRoomIsPublic" static let allowInviteExernalUsers = "allowInviteExernalUsers" static let enableRingingForGroupCalls = "enableRingingForGroupCalls" + static let enableVoiceMessages = "enableVoiceMessages" static let roomSettingsScreenShowLowPriorityOption = "roomSettingsScreenShowLowPriorityOption" static let roomSettingsScreenShowDirectChatOption = "roomSettingsScreenShowDirectChatOption" static let roomSettingsScreenAllowChangingAccessSettings = "roomSettingsScreenAllowChangingAccessSettings" @@ -80,6 +81,7 @@ final class RiotSettings: NSObject { static let roomMemberScreenShowIgnore = "roomMemberScreenShowIgnore" static let unifiedSearchScreenShowPublicDirectory = "unifiedSearchScreenShowPublicDirectory" static let hideSpaceBetaAnnounce = "hideSpaceBetaAnnounce" + static let secretsRecoveryAllowReset = "secretsRecoveryAllowReset" } static let shared = RiotSettings() @@ -92,6 +94,11 @@ final class RiotSettings: NSObject { return userDefaults }() + private override init() { + super.init() + defaults.register(defaults: [UserDefaultsKeys.enableVoiceMessages: BuildSettings.voiceMessagesEnabled]) + } + // MARK: Servers var homeserverUrlString: String { @@ -214,6 +221,14 @@ final class RiotSettings: NSObject { } } + var enableVoiceMessages: Bool { + get { + return defaults.bool(forKey: UserDefaultsKeys.enableVoiceMessages) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.enableVoiceMessages) + } + } + // MARK: Calls /// Indicate if `allowStunServerFallback` settings has been set once. @@ -695,7 +710,7 @@ final class RiotSettings: NSObject { } } - // Mark: - Unified Search + // MARK: - Unified Search var unifiedSearchScreenShowPublicDirectory: Bool { get { @@ -708,6 +723,19 @@ final class RiotSettings: NSObject { } } + // MARK: - Secrets Recovery + + var secretsRecoveryAllowReset: Bool { + get { + guard defaults.object(forKey: UserDefaultsKeys.secretsRecoveryAllowReset) != nil else { + return BuildSettings.secretsRecoveryAllowReset + } + return defaults.bool(forKey: UserDefaultsKeys.secretsRecoveryAllowReset) + } set { + defaults.set(newValue, forKey: UserDefaultsKeys.secretsRecoveryAllowReset) + } + } + // MARK: - Beta var hideSpaceBetaAnnounce: Bool { diff --git a/Riot/Managers/Theme/Themable.swift b/Riot/Managers/Theme/Themable.swift index f4f59f1e9..71931bfe8 100644 --- a/Riot/Managers/Theme/Themable.swift +++ b/Riot/Managers/Theme/Themable.swift @@ -16,6 +16,6 @@ import Foundation -@objc protocol Themable: class { +@objc protocol Themable: AnyObject { func update(theme: Theme) } diff --git a/Riot/Managers/Widgets/WidgetManager.h b/Riot/Managers/Widgets/WidgetManager.h index 09c2b2588..f5279a216 100644 --- a/Riot/Managers/Widgets/WidgetManager.h +++ b/Riot/Managers/Widgets/WidgetManager.h @@ -183,7 +183,7 @@ FOUNDATION_EXPORT NSString *const WidgetManagerErrorOpenIdTokenKey; /** Store the integration manager configuration for a user. - @param the integration manager configuration. + @param config the integration manager configuration. @param userId the user id. */ - (void)setConfig:(WidgetManagerConfig*)config forUser:(NSString*)userId; diff --git a/Riot/Model/Room/RoomPreviewData.h b/Riot/Model/Room/RoomPreviewData.h index 71280b099..c10295617 100644 --- a/Riot/Model/Room/RoomPreviewData.h +++ b/Riot/Model/Room/RoomPreviewData.h @@ -76,10 +76,17 @@ Contructors. @param roomId the id of the room. - @param emailInvitationParams, in case of an email invitation link, the query parameters extracted from the link. @param mxSession the session to open the room preview with. */ - (instancetype)initWithRoomId:(NSString*)roomId andSession:(MXSession*)mxSession; + +/** + Contructors. + + @param roomId the id of the room. + @param emailInvitationParams in case of an email invitation link, the query parameters extracted from the link. + @param mxSession the session to open the room preview with. + */ - (instancetype)initWithRoomId:(NSString*)roomId emailInvitationParams:(NSDictionary*)emailInvitationParams andSession:(MXSession*)mxSession; /** diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift index 4469fbbfa..8a1949f75 100755 --- a/Riot/Modules/Application/AppCoordinator.swift +++ b/Riot/Modules/Application/AppCoordinator.swift @@ -21,8 +21,12 @@ import Intents import FLEX #endif -/// The AppCoordinator is responsible of screen navigation and data injection at root application level. It decides if authentication or home screen should be shown and inject data needed for these flows, it changes the navigation stack on deep link, displays global warning. -/// This class should avoid to contain too many data management code not related to screen navigation logic. For example `MXSession` or push notification management should be handled in dedicated classes and report only navigation changes to the AppCoordinator. +/// The AppCoordinator is responsible of screen navigation and data injection at root application level. It decides +/// if authentication or home screen should be shown and inject data needed for these flows, it changes the navigation +/// stack on deep link, displays global warning. +/// This class should avoid to contain too many data management code not related to screen navigation logic. For example +/// `MXSession` or push notification management should be handled in dedicated classes and report only navigation +/// changes to the AppCoordinator. final class AppCoordinator: NSObject, AppCoordinatorType { // MARK: - Constants @@ -83,7 +87,8 @@ final class AppCoordinator: NSObject, AppCoordinatorType { } func open(url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { - // NOTE: As said in the Apple documentation be careful on security issues with Custom Scheme URL (see https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app) + // NOTE: As said in the Apple documentation be careful on security issues with Custom Scheme URL: + // https://developer.apple.com/documentation/xcode/allowing_apps_and_websites_to_link_to_your_content/defining_a_custom_url_scheme_for_your_app do { let deepLinkOption = try self.customSchemeURLParser.parse(url: url, options: options) diff --git a/Riot/Modules/Application/LegacyAppDelegate.h b/Riot/Modules/Application/LegacyAppDelegate.h index 78269f348..1b06d8cbe 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.h +++ b/Riot/Modules/Application/LegacyAppDelegate.h @@ -173,7 +173,7 @@ UINavigationControllerDelegate Log out all the accounts without confirmation. Show the authentication screen on successful logout. - @param sendLogoutRequest Indicate whether send logout request to homeserver. + @param sendLogoutServerRequest Indicate whether send logout request to homeserver. @param completion the block to execute at the end of the operation. */ - (void)logoutSendingRequestServer:(BOOL)sendLogoutServerRequest @@ -183,7 +183,7 @@ UINavigationControllerDelegate Present incoming key verification request to accept. @param incomingKeyVerificationRequest The incoming key verification request. - @param The matrix session. + @param session The matrix session. @return Indicate NO if the key verification screen could not be presented. */ - (BOOL)presentIncomingKeyVerificationRequest:(MXKeyVerificationRequest*)incomingKeyVerificationRequest diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index 049b830e7..ae55cf538 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -351,6 +351,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni NSURL *messageSoundURL = [[NSBundle mainBundle] URLForResource:@"message" withExtension:@"caf"]; AudioServicesCreateSystemSoundID((__bridge CFURLRef)messageSoundURL, &_messageSound); + // Set app info now as Mac (Designed for iPad) accesses it before didFinishLaunching is called + self.appInfo = AppInfo.current; + MXLogDebug(@"[AppDelegate] willFinishLaunchingWithOptions: Done"); return YES; @@ -371,8 +374,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni _configuration = [AppConfiguration new]; - self.appInfo = AppInfo.current; - // Log app information NSString *appDisplayName = self.appInfo.displayName; NSString* appVersion = self.appVersion; diff --git a/Riot/Modules/Authentication/SocialLogin/SocialLoginListView.swift b/Riot/Modules/Authentication/SocialLogin/SocialLoginListView.swift index 6a66da782..e2e1a586f 100644 --- a/Riot/Modules/Authentication/SocialLogin/SocialLoginListView.swift +++ b/Riot/Modules/Authentication/SocialLogin/SocialLoginListView.swift @@ -17,7 +17,7 @@ import UIKit import Reusable -@objc protocol SocialLoginListViewDelegate: class { +@objc protocol SocialLoginListViewDelegate: AnyObject { func socialLoginListView(_ socialLoginListView: SocialLoginListView, didTapSocialButtonWithIdentifier identifier: String) } diff --git a/Riot/Modules/Call/Dialpad/DialpadViewController.swift b/Riot/Modules/Call/Dialpad/DialpadViewController.swift index 23e2c21ce..ce9a9fc66 100644 --- a/Riot/Modules/Call/Dialpad/DialpadViewController.swift +++ b/Riot/Modules/Call/Dialpad/DialpadViewController.swift @@ -19,7 +19,7 @@ import UIKit import libPhoneNumber_iOS -@objc protocol DialpadViewControllerDelegate: class { +@objc protocol DialpadViewControllerDelegate: AnyObject { @objc optional func dialpadViewControllerDidTapCall(_ viewController: DialpadViewController, withPhoneNumber phoneNumber: String) @objc optional func dialpadViewControllerDidTapClose(_ viewController: DialpadViewController) diff --git a/Riot/Modules/CallTransfer/CallTransferMainViewController.swift b/Riot/Modules/CallTransfer/CallTransferMainViewController.swift index af4b093d1..f7e23d0cd 100644 --- a/Riot/Modules/CallTransfer/CallTransferMainViewController.swift +++ b/Riot/Modules/CallTransfer/CallTransferMainViewController.swift @@ -18,7 +18,7 @@ import UIKit -@objc protocol CallTransferMainViewControllerDelegate: class { +@objc protocol CallTransferMainViewControllerDelegate: AnyObject { func callTransferMainViewControllerDidComplete(_ viewController: CallTransferMainViewController, consult: Bool, contact: MXKContact?, diff --git a/Riot/Modules/CallTransfer/SelectContact/CallTransferSelectContactViewController.swift b/Riot/Modules/CallTransfer/SelectContact/CallTransferSelectContactViewController.swift index 786cc4f71..df5c0a5a8 100644 --- a/Riot/Modules/CallTransfer/SelectContact/CallTransferSelectContactViewController.swift +++ b/Riot/Modules/CallTransfer/SelectContact/CallTransferSelectContactViewController.swift @@ -19,7 +19,7 @@ import UIKit import Reusable -protocol CallTransferSelectContactViewControllerDelegate: class { +protocol CallTransferSelectContactViewControllerDelegate: AnyObject { func callTransferSelectContactViewControllerDidSelectContact(_ viewController: CallTransferSelectContactViewController, contact: MXKContact?) } diff --git a/Riot/Modules/Camera/CameraPresenter.swift b/Riot/Modules/Camera/CameraPresenter.swift index 749e9abe5..a39bda7c0 100644 --- a/Riot/Modules/Camera/CameraPresenter.swift +++ b/Riot/Modules/Camera/CameraPresenter.swift @@ -18,7 +18,7 @@ import Foundation import UIKit import AVFoundation -@objc protocol CameraPresenterDelegate: class { +@objc protocol CameraPresenterDelegate: AnyObject { func cameraPresenter(_ presenter: CameraPresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) func cameraPresenter(_ presenter: CameraPresenter, didSelectVideoAt url: URL) func cameraPresenterDidCancel(_ cameraPresenter: CameraPresenter) diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m index 3857f61fc..0e2f14ea3 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m @@ -19,7 +19,6 @@ #import "RecentCellData.h" #import "SectionHeaderView.h" -#import "DirectorySectionHeaderContainerView.h" #import "ThemeService.h" @@ -37,7 +36,6 @@ #define RECENTSDATASOURCE_SECTION_PEOPLE 0x40 #define RECENTSDATASOURCE_DEFAULT_SECTION_HEADER_HEIGHT 30.0 -#define RECENTSDATASOURCE_DIRECTORY_SECTION_HEADER_HEIGHT 65.0 NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSourceTapOnDirectoryServerChange"; @@ -48,10 +46,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou NSInteger shrinkedSectionsBitMask; - DirectorySectionHeaderContainerView *directorySectionContainer; - UILabel *networkLabel; - UILabel *directoryServerLabel; - NSMutableDictionary *roomTagsListenerByUserId; // Timer to not refresh publicRoomsDirectoryDataSource on every keystroke. @@ -483,13 +477,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou conversationSection = sectionsCount++; } - if (_recentsDataSourceMode == RecentsDataSourceModeRooms - && BuildSettings.publicRoomsShowDirectory) - { - // Add the directory section after "ROOMS" - directorySection = sectionsCount++; - } - if (self.lowPriorityCellDataArray.count > 0) { lowPrioritySection = sectionsCount++; @@ -572,12 +559,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou { return 0.0; } - else if (section == directorySection - && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_DIRECTORY) - && BuildSettings.publicRoomsAllowServerChange) - { - return RECENTSDATASOURCE_DIRECTORY_SECTION_HEADER_HEIGHT; - } return RECENTSDATASOURCE_DEFAULT_SECTION_HEADER_HEIGHT; } @@ -823,57 +804,6 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou [sectionHeader addSubview:headerLabel]; sectionHeader.headerLabel = headerLabel; - if (section == directorySection - && _recentsDataSourceMode == RecentsDataSourceModeRooms - && !(shrinkedSectionsBitMask & RECENTSDATASOURCE_SECTION_DIRECTORY) - && BuildSettings.publicRoomsAllowServerChange) - { - if (!directorySectionContainer) - { - directorySectionContainer = [[DirectorySectionHeaderContainerView alloc] initWithFrame:CGRectZero]; - directorySectionContainer.backgroundColor = [UIColor clearColor]; - - // Add the "Network" label at the left - networkLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)]; - networkLabel.font = [UIFont systemFontOfSize:16.0]; - networkLabel.text = NSLocalizedStringFromTable(@"room_recents_directory_section_network", @"Vector", nil); - [directorySectionContainer addSubview:networkLabel]; - directorySectionContainer.networkLabel = networkLabel; - - // Add label for selected directory server - directoryServerLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 0, 30)]; - directoryServerLabel.font = [UIFont systemFontOfSize:16.0]; - directoryServerLabel.textAlignment = NSTextAlignmentRight; - [directorySectionContainer addSubview:directoryServerLabel]; - directorySectionContainer.directoryServerLabel = directoryServerLabel; - - // Chevron - UIImageView *chevronImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 6, 12)]; - chevronImageView.contentMode = UIViewContentModeScaleAspectFit; - chevronImageView.image = [UIImage imageNamed:@"disclosure_icon"]; - chevronImageView.tintColor = ThemeService.shared.theme.textSecondaryColor; - [directorySectionContainer addSubview:chevronImageView]; - directorySectionContainer.disclosureView = chevronImageView; - - // Set a tap listener on all the container - UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onDirectoryServerPickerTap:)]; - [tapGesture setNumberOfTouchesRequired:1]; - [tapGesture setNumberOfTapsRequired:1]; - [directorySectionContainer addGestureRecognizer:tapGesture]; - } - - // Apply the current UI theme. - networkLabel.textColor = ThemeService.shared.theme.textPrimaryColor; - directoryServerLabel.textColor = ThemeService.shared.theme.textSecondaryColor; - - // Set the current directory server name - directoryServerLabel.text = _publicRoomsDirectoryDataSource.directoryServerDisplayname; - - // Add the check box container - [sectionHeader addSubview:directorySectionContainer]; - sectionHeader.bottomView = directorySectionContainer; - } - return sectionHeader; } diff --git a/Riot/Modules/Common/Recents/RecentsViewController.h b/Riot/Modules/Common/Recents/RecentsViewController.h index b2b640d80..d3968cfd6 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.h +++ b/Riot/Modules/Common/Recents/RecentsViewController.h @@ -161,10 +161,15 @@ - (void)makeDirectEditedRoom:(BOOL)isDirect; /** - Enable/disable the notifications for the selected room. - */ +Enable/disable the notifications for the selected room. +*/ - (void)muteEditedRoomNotifications:(BOOL)mute; +/** + Edit notification settings for the selected room. + */ +- (void)changeEditedRoomNotificationSettings; + /** Show room directory. */ diff --git a/Riot/Modules/Common/Recents/RecentsViewController.m b/Riot/Modules/Common/Recents/RecentsViewController.m index 6ac8c618b..e76a83cab 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.m +++ b/Riot/Modules/Common/Recents/RecentsViewController.m @@ -34,7 +34,7 @@ #import "Riot-Swift.h" -@interface RecentsViewController () +@interface RecentsViewController () { // Tell whether a recents refresh is pending (suspended during editing mode). BOOL isRefreshPending; @@ -74,6 +74,8 @@ @property (nonatomic, strong) CustomSizedPresentationController *customSizedPresentationController; +@property (nonatomic, strong) RoomNotificationSettingsCoordinatorBridgePresenter *roomNotificationSettingsCoordinatorBridgePresenter; + @end @implementation RecentsViewController @@ -1031,12 +1033,31 @@ UIContextualAction *muteAction = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:title handler:^(UIContextualAction * _Nonnull action, __kindof UIView * _Nonnull sourceView, void (^ _Nonnull completionHandler)(BOOL)) { - [self muteEditedRoomNotifications:!isMuted]; + + if ([BuildSettings roomSettingsScreenShowNotificationsV2]) + { + [self changeEditedRoomNotificationSettings]; + } + else + { + [self muteEditedRoomNotifications:!isMuted]; + } + + completionHandler(YES); }]; muteAction.backgroundColor = actionBackgroundColor; - UIImage *notificationImage = [UIImage imageNamed:@"room_action_notification"]; + UIImage *notificationImage; + if([BuildSettings roomSettingsScreenShowNotificationsV2]) + { + notificationImage = isMuted ? [UIImage imageNamed:@"room_action_notification_muted"] : [UIImage imageNamed:@"room_action_notification"]; + } + else + { + notificationImage = [UIImage imageNamed:@"room_action_notification"]; + } + notificationImage = [notificationImage vc_tintedImageUsingColor:isMuted ? unselectedColor : selectedColor]; muteAction.image = [notificationImage vc_notRenderedImage]; @@ -1298,6 +1319,23 @@ } } +- (void)changeEditedRoomNotificationSettings +{ + if (editedRoomId) + { + // Check whether the user didn't leave the room + MXRoom *room = [self.mainSession roomWithRoomId:editedRoomId]; + if (room) + { + // navigate + self.roomNotificationSettingsCoordinatorBridgePresenter = [[RoomNotificationSettingsCoordinatorBridgePresenter alloc] initWithRoom:room]; + self.roomNotificationSettingsCoordinatorBridgePresenter.delegate = self; + [self.roomNotificationSettingsCoordinatorBridgePresenter presentFrom:self animated:YES]; + } + [self cancelEditionMode:isRefreshPending]; + } +} + - (void)muteEditedRoomNotifications:(BOOL)mute { if (editedRoomId) @@ -1307,27 +1345,27 @@ if (room) { [self startActivityIndicator]; - + if (mute) { [room mentionsOnly:^{ - + [self stopActivityIndicator]; - + // Leave editing mode - [self cancelEditionMode:isRefreshPending]; - + [self cancelEditionMode:self->isRefreshPending]; + }]; } else { [room allMessages:^{ - + [self stopActivityIndicator]; - + // Leave editing mode - [self cancelEditionMode:isRefreshPending]; - + [self cancelEditionMode:self->isRefreshPending]; + }]; } } @@ -2219,4 +2257,11 @@ } } +#pragma mark - RoomNotificationSettingsCoordinatorBridgePresenterDelegate +-(void)roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete:(RoomNotificationSettingsCoordinatorBridgePresenter *)coordinatorBridgePresenter +{ + [coordinatorBridgePresenter dismissWithAnimated:YES completion:nil]; + self.roomNotificationSettingsCoordinatorBridgePresenter = nil; +} + @end diff --git a/Riot/Modules/Common/SectionHeaders/TitleHeaderView.swift b/Riot/Modules/Common/SectionHeaders/TitleHeaderView.swift new file mode 100644 index 000000000..a26d99820 --- /dev/null +++ b/Riot/Modules/Common/SectionHeaders/TitleHeaderView.swift @@ -0,0 +1,39 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation +import Reusable + +class TitleHeaderView: UITableViewHeaderFooterView { + + @IBOutlet weak var label: UILabel! + + func update(title: String) { + label.text = title.uppercased() + } + +} + + +extension TitleHeaderView: NibReusable {} +extension TitleHeaderView: Themable { + + func update(theme: Theme) { + contentView.backgroundColor = theme.headerBackgroundColor + label.textColor = theme.headerTextSecondaryColor + label.font = theme.fonts.body + } +} diff --git a/Riot/Modules/Common/SectionHeaders/TitleHeaderView.xib b/Riot/Modules/Common/SectionHeaders/TitleHeaderView.xib new file mode 100644 index 000000000..c49ab7fff --- /dev/null +++ b/Riot/Modules/Common/SectionHeaders/TitleHeaderView.xib @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m index a4e4151f7..8480205af 100644 --- a/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m +++ b/Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m @@ -74,7 +74,7 @@ /** init the segmentedViewController with a list of UIViewControllers. @param titles the section tiles - @param viewControllers the list of viewControllers to display. + @param someViewControllers the list of viewControllers to display. @param defaultSelected index of the default selected UIViewController in the list. */ - (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)someViewControllers defaultSelected:(NSUInteger)defaultSelected diff --git a/Riot/Modules/CreateRoom/CreateRoomCoordinatorType.swift b/Riot/Modules/CreateRoom/CreateRoomCoordinatorType.swift index 2bddc933c..585538ddb 100644 --- a/Riot/Modules/CreateRoom/CreateRoomCoordinatorType.swift +++ b/Riot/Modules/CreateRoom/CreateRoomCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol CreateRoomCoordinatorDelegate: class { +protocol CreateRoomCoordinatorDelegate: AnyObject { func createRoomCoordinator(_ coordinator: CreateRoomCoordinatorType, didCreateNewRoom room: MXRoom) func createRoomCoordinatorDidCancel(_ coordinator: CreateRoomCoordinatorType) } diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.swift index 82160a816..bcca816cf 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/Cells/ChooseAvatarTableViewCell.swift @@ -17,7 +17,7 @@ import UIKit import Reusable -protocol ChooseAvatarTableViewCellDelegate: class { +protocol ChooseAvatarTableViewCellDelegate: AnyObject { func chooseAvatarTableViewCellDidTapChooseAvatar(_ cell: ChooseAvatarTableViewCell, sourceView: UIView) } diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsCoordinatorType.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsCoordinatorType.swift index d2798fcb5..2d02d6cda 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsCoordinatorType.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol EnterNewRoomDetailsCoordinatorDelegate: class { +protocol EnterNewRoomDetailsCoordinatorDelegate: AnyObject { func enterNewRoomDetailsCoordinator(_ coordinator: EnterNewRoomDetailsCoordinatorType, didCreateNewRoom room: MXRoom) func enterNewRoomDetailsCoordinatorDidCancel(_ coordinator: EnterNewRoomDetailsCoordinatorType) } diff --git a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModelType.swift b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModelType.swift index ea6b388c2..be053323d 100644 --- a/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModelType.swift +++ b/Riot/Modules/CreateRoom/EnterNewRoomDetails/EnterNewRoomDetailsViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol EnterNewRoomDetailsViewModelViewDelegate: class { +protocol EnterNewRoomDetailsViewModelViewDelegate: AnyObject { func enterNewRoomDetailsViewModel(_ viewModel: EnterNewRoomDetailsViewModelType, didUpdateViewState viewSate: EnterNewRoomDetailsViewState) } -protocol EnterNewRoomDetailsViewModelCoordinatorDelegate: class { +protocol EnterNewRoomDetailsViewModelCoordinatorDelegate: AnyObject { func enterNewRoomDetailsViewModel(_ viewModel: EnterNewRoomDetailsViewModelType, didCreateNewRoom room: MXRoom) func enterNewRoomDetailsViewModel(_ viewModel: EnterNewRoomDetailsViewModelType, didTapChooseAvatar sourceView: UIView) func enterNewRoomDetailsViewModelDidCancel(_ viewModel: EnterNewRoomDetailsViewModelType) diff --git a/Riot/Modules/CrossSigning/Banners/CrossSigningSetupBannerCell.swift b/Riot/Modules/CrossSigning/Banners/CrossSigningSetupBannerCell.swift index 6a7161bea..f0c13b154 100644 --- a/Riot/Modules/CrossSigning/Banners/CrossSigningSetupBannerCell.swift +++ b/Riot/Modules/CrossSigning/Banners/CrossSigningSetupBannerCell.swift @@ -16,7 +16,7 @@ import UIKit -@objc protocol CrossSigningSetupBannerCellDelegate: class { +@objc protocol CrossSigningSetupBannerCellDelegate: AnyObject { func crossSigningSetupBannerCellDidTapCloseAction(_ cell: CrossSigningSetupBannerCell) } diff --git a/Riot/Modules/CrossSigning/Setup/CrossSigningSetupCoordinatorType.swift b/Riot/Modules/CrossSigning/Setup/CrossSigningSetupCoordinatorType.swift index 1dc620d3f..ccd85e17b 100644 --- a/Riot/Modules/CrossSigning/Setup/CrossSigningSetupCoordinatorType.swift +++ b/Riot/Modules/CrossSigning/Setup/CrossSigningSetupCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol CrossSigningSetupCoordinatorDelegate: class { +protocol CrossSigningSetupCoordinatorDelegate: AnyObject { func crossSigningSetupCoordinatorDidComplete(_ coordinator: CrossSigningSetupCoordinatorType) func crossSigningSetupCoordinatorDidCancel(_ coordinator: CrossSigningSetupCoordinatorType) func crossSigningSetupCoordinator(_ coordinator: CrossSigningSetupCoordinatorType, didFailWithError error: Error) diff --git a/Riot/Modules/GDPR/GDPRConsentViewController.swift b/Riot/Modules/GDPR/GDPRConsentViewController.swift index 29ebdc22c..4a5cdee8d 100644 --- a/Riot/Modules/GDPR/GDPRConsentViewController.swift +++ b/Riot/Modules/GDPR/GDPRConsentViewController.swift @@ -16,7 +16,7 @@ import Foundation -@objc protocol GDPRConsentViewControllerDelegate: class { +@objc protocol GDPRConsentViewControllerDelegate: AnyObject { func gdprConsentViewControllerDidConsentToGDPRWithSuccess(_ gdprConsentViewController: GDPRConsentViewController) } diff --git a/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m b/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m index b853631cd..4dfbe686f 100644 --- a/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m +++ b/Riot/Modules/GlobalSearch/Files/Views/FilesSearchTableViewCell.m @@ -112,6 +112,9 @@ case MXKAttachmentTypeAudio: image = [UIImage imageNamed:@"file_music_icon"]; break; + case MXKAttachmentTypeVoiceMessage: + image = [UIImage imageNamed:@"file_music_icon"]; + break; case MXKAttachmentTypeVideo: image = [UIImage imageNamed:@"file_video_icon"]; break; diff --git a/Riot/Modules/GlobalSearch/Views/DirectoryRecentTableViewCell.m b/Riot/Modules/GlobalSearch/Views/DirectoryRecentTableViewCell.m index 63cdbfbce..26c103fb2 100644 --- a/Riot/Modules/GlobalSearch/Views/DirectoryRecentTableViewCell.m +++ b/Riot/Modules/GlobalSearch/Views/DirectoryRecentTableViewCell.m @@ -54,20 +54,20 @@ self.titleLabel.text = NSLocalizedStringFromTable(@"directory_search_results_title", @"Vector", nil); // Do we need to display like ">20 results found" or "18 results found"? - NSString *descriptionLabel = (publicRoomsDirectoryDataSource.moreThanRoomsCount && publicRoomsDirectoryDataSource.roomsCount > 0) ? NSLocalizedStringFromTable(@"directory_search_results_more_than", @"Vector", nil) : NSLocalizedStringFromTable(@"directory_search_results", @"Vector", nil); + NSString *descriptionLabel = (publicRoomsDirectoryDataSource.searchResultsCountIsLimited && publicRoomsDirectoryDataSource.searchResultsCount > 0) ? NSLocalizedStringFromTable(@"directory_search_results_more_than", @"Vector", nil) : NSLocalizedStringFromTable(@"directory_search_results", @"Vector", nil); self.descriptionLabel.text = [NSString stringWithFormat:descriptionLabel, - publicRoomsDirectoryDataSource.roomsCount, + publicRoomsDirectoryDataSource.searchResultsCount, publicRoomsDirectoryDataSource.searchPattern]; } else { self.titleLabel.text = NSLocalizedStringFromTable(@"directory_cell_title", @"Vector", nil); self.descriptionLabel.text = [NSString stringWithFormat:NSLocalizedStringFromTable(@"directory_cell_description", @"Vector", nil), - publicRoomsDirectoryDataSource.roomsCount]; + publicRoomsDirectoryDataSource.searchResultsCount]; } - if (publicRoomsDirectoryDataSource.roomsCount) + if (publicRoomsDirectoryDataSource.searchResultsCount) { self.userInteractionEnabled = YES; self.chevronImageView.hidden = NO; diff --git a/Riot/Modules/Home/HomeViewController.m b/Riot/Modules/Home/HomeViewController.m index d8d5382fc..74fef7d37 100644 --- a/Riot/Modules/Home/HomeViewController.m +++ b/Riot/Modules/Home/HomeViewController.m @@ -347,7 +347,16 @@ tableViewCell.notificationsButton.tag = room.isMute || room.isMentionsOnly; [tableViewCell.notificationsButton addTarget:self action:@selector(onNotificationsButtonPressed:) forControlEvents:UIControlEventTouchUpInside]; - tableViewCell.notificationsImageView.image = [UIImage imageNamed:@"room_action_notification"]; + + if ([BuildSettings roomSettingsScreenShowNotificationsV2]) + { + tableViewCell.notificationsImageView.image = tableViewCell.notificationsButton.tag ? [UIImage imageNamed:@"room_action_notification_muted"] : [UIImage imageNamed:@"room_action_notification"]; + } + else + { + tableViewCell.notificationsImageView.image = [UIImage imageNamed:@"room_action_notification"]; + } + tableViewCell.notificationsImageView.tintColor = tableViewCell.notificationsButton.tag ? unselectedColor : selectedColor; // Get the room tag (use only the first one). @@ -663,8 +672,15 @@ MXRoom *room = [self.mainSession roomWithRoomId:editedRoomId]; if (room) { - UIButton *button = (UIButton*)sender; - [self muteEditedRoomNotifications:!button.tag]; + if ([BuildSettings roomSettingsScreenShowNotificationsV2]) + { + [self changeEditedRoomNotificationSettings]; + } + else + { + UIButton *button = (UIButton*)sender; + [self muteEditedRoomNotifications:!button.tag]; + } } } } diff --git a/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorType.swift b/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorType.swift index 30d9c34d3..52bb891ea 100644 --- a/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorType.swift +++ b/Riot/Modules/KeyBackup/Recover/KeyBackupRecoverCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol KeyBackupRecoverCoordinatorDelegate: class { +protocol KeyBackupRecoverCoordinatorDelegate: AnyObject { func keyBackupRecoverCoordinatorDidRecover(_ keyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType) func keyBackupRecoverCoordinatorDidCancel(_ keyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType) } diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinatorType.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinatorType.swift index c33cdcf06..a3bc9030b 100644 --- a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinatorType.swift +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol KeyBackupRecoverFromPassphraseCoordinatorDelegate: class { +protocol KeyBackupRecoverFromPassphraseCoordinatorDelegate: AnyObject { func keyBackupRecoverFromPassphraseCoordinatorDidRecover(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) func keyBackupRecoverFromPassphraseCoordinatorDoNotKnowPassphrase(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) func keyBackupRecoverFromPassphraseCoordinatorDidCancel(_ keyBackupRecoverFromPassphraseCoordinator: KeyBackupRecoverFromPassphraseCoordinatorType) diff --git a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModelType.swift b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModelType.swift index 8b19bc589..69bc84b01 100644 --- a/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModelType.swift +++ b/Riot/Modules/KeyBackup/Recover/Passphrase/KeyBackupRecoverFromPassphraseViewModelType.swift @@ -16,11 +16,11 @@ import Foundation -protocol KeyBackupRecoverFromPassphraseViewModelViewDelegate: class { +protocol KeyBackupRecoverFromPassphraseViewModelViewDelegate: AnyObject { func keyBackupRecoverFromPassphraseViewModel(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType, didUpdateViewState viewSate: KeyBackupRecoverFromPassphraseViewState) } -protocol KeyBackupRecoverFromPassphraseViewModelCoordinatorDelegate: class { +protocol KeyBackupRecoverFromPassphraseViewModelCoordinatorDelegate: AnyObject { func keyBackupRecoverFromPassphraseViewModelDidRecover(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) func keyBackupRecoverFromPassphraseViewModelDidCancel(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) func keyBackupRecoverFromPassphraseViewModelDoNotKnowPassphrase(_ viewModel: KeyBackupRecoverFromPassphraseViewModelType) diff --git a/Riot/Modules/KeyBackup/Recover/PrivateKey/KeyBackupRecoverFromPrivateKeyCoordinatorType.swift b/Riot/Modules/KeyBackup/Recover/PrivateKey/KeyBackupRecoverFromPrivateKeyCoordinatorType.swift index aab042ba5..7146eeeae 100644 --- a/Riot/Modules/KeyBackup/Recover/PrivateKey/KeyBackupRecoverFromPrivateKeyCoordinatorType.swift +++ b/Riot/Modules/KeyBackup/Recover/PrivateKey/KeyBackupRecoverFromPrivateKeyCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyBackupRecoverFromPrivateKeyCoordinatorDelegate: class { +protocol KeyBackupRecoverFromPrivateKeyCoordinatorDelegate: AnyObject { func keyBackupRecoverFromPrivateKeyCoordinatorDidRecover(_ coordinator: KeyBackupRecoverFromPrivateKeyCoordinatorType) func keyBackupRecoverFromPrivateKeyCoordinatorDidPrivateKeyFail(_ coordinator: KeyBackupRecoverFromPrivateKeyCoordinatorType) func keyBackupRecoverFromPrivateKeyCoordinatorDidCancel(_ coordinator: KeyBackupRecoverFromPrivateKeyCoordinatorType) diff --git a/Riot/Modules/KeyBackup/Recover/PrivateKey/KeyBackupRecoverFromPrivateKeyViewModelType.swift b/Riot/Modules/KeyBackup/Recover/PrivateKey/KeyBackupRecoverFromPrivateKeyViewModelType.swift index 1188e0560..396f401f1 100644 --- a/Riot/Modules/KeyBackup/Recover/PrivateKey/KeyBackupRecoverFromPrivateKeyViewModelType.swift +++ b/Riot/Modules/KeyBackup/Recover/PrivateKey/KeyBackupRecoverFromPrivateKeyViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol KeyBackupRecoverFromPrivateKeyViewModelViewDelegate: class { +protocol KeyBackupRecoverFromPrivateKeyViewModelViewDelegate: AnyObject { func keyBackupRecoverFromPrivateKeyViewModel(_ viewModel: KeyBackupRecoverFromPrivateKeyViewModelType, didUpdateViewState viewSate: KeyBackupRecoverFromPrivateKeyViewState) } -protocol KeyBackupRecoverFromPrivateKeyViewModelCoordinatorDelegate: class { +protocol KeyBackupRecoverFromPrivateKeyViewModelCoordinatorDelegate: AnyObject { func keyBackupRecoverFromPrivateKeyViewModelDidRecover(_ viewModel: KeyBackupRecoverFromPrivateKeyViewModelType) func keyBackupRecoverFromPrivateKeyViewModelDidPrivateKeyFail(_ viewModel: KeyBackupRecoverFromPrivateKeyViewModelType) func keyBackupRecoverFromPrivateKeyViewModelDidCancel(_ viewModel: KeyBackupRecoverFromPrivateKeyViewModelType) diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift index 11d528b4f..c354fd13f 100644 --- a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol KeyBackupRecoverFromRecoveryKeyCoordinatorDelegate: class { +protocol KeyBackupRecoverFromRecoveryKeyCoordinatorDelegate: AnyObject { func keyBackupRecoverFromPassphraseCoordinatorDidRecover(_ keyBackupRecoverFromRecoveryKeyCoordinator: KeyBackupRecoverFromRecoveryKeyCoordinatorType) func keyBackupRecoverFromPassphraseCoordinatorDidCancel(_ keyBackupRecoverFromRecoveryKeyCoordinator: KeyBackupRecoverFromRecoveryKeyCoordinatorType) } diff --git a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModelType.swift b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModelType.swift index e952c70a3..366d547d0 100644 --- a/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModelType.swift +++ b/Riot/Modules/KeyBackup/Recover/RecoveryKey/KeyBackupRecoverFromRecoveryKeyViewModelType.swift @@ -16,11 +16,11 @@ import Foundation -protocol KeyBackupRecoverFromRecoveryKeyViewModelViewDelegate: class { +protocol KeyBackupRecoverFromRecoveryKeyViewModelViewDelegate: AnyObject { func keyBackupRecoverFromPassphraseViewModel(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType, didUpdateViewState viewSate: KeyBackupRecoverFromRecoveryKeyViewState) } -protocol KeyBackupRecoverFromRecoveryKeyViewModelCoordinatorDelegate: class { +protocol KeyBackupRecoverFromRecoveryKeyViewModelCoordinatorDelegate: AnyObject { func keyBackupRecoverFromRecoveryKeyViewModelDidRecover(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType) func keyBackupRecoverFromRecoveryKeyViewModelDidCancel(_ viewModel: KeyBackupRecoverFromRecoveryKeyViewModelType) } diff --git a/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.swift b/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.swift index dd5a834da..8b58b241a 100644 --- a/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.swift +++ b/Riot/Modules/KeyBackup/Recover/Success/KeyBackupRecoverSuccessViewController.swift @@ -16,7 +16,7 @@ import UIKit -protocol KeyBackupRecoverSuccessViewControllerDelegate: class { +protocol KeyBackupRecoverSuccessViewControllerDelegate: AnyObject { func keyBackupRecoverSuccessViewControllerDidTapDone(_ keyBackupRecoverSuccessViewController: KeyBackupRecoverSuccessViewController) } diff --git a/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.swift b/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.swift index c968500ed..2f4284fbd 100644 --- a/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.swift +++ b/Riot/Modules/KeyBackup/Setup/Intro/KeyBackupSetupIntroViewController.swift @@ -16,7 +16,7 @@ import UIKit -protocol KeyBackupSetupIntroViewControllerDelegate: class { +protocol KeyBackupSetupIntroViewControllerDelegate: AnyObject { func keyBackupSetupIntroViewControllerDidTapSetupAction(_ keyBackupSetupIntroViewController: KeyBackupSetupIntroViewController) func keyBackupSetupIntroViewControllerDidCancel(_ keyBackupSetupIntroViewController: KeyBackupSetupIntroViewController) } diff --git a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorType.swift b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorType.swift index f5a5dd0b0..612be7a1d 100644 --- a/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorType.swift +++ b/Riot/Modules/KeyBackup/Setup/KeyBackupSetupCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol KeyBackupSetupCoordinatorDelegate: class { +protocol KeyBackupSetupCoordinatorDelegate: AnyObject { func keyBackupSetupCoordinatorDidCancel(_ keyBackupSetupCoordinator: KeyBackupSetupCoordinatorType) func keyBackupSetupCoordinatorDidSetupRecoveryKey(_ keyBackupSetupCoordinator: KeyBackupSetupCoordinatorType) } diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinatorType.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinatorType.swift index f94722adb..24403a387 100644 --- a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinatorType.swift +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol KeyBackupSetupPassphraseCoordinatorDelegate: class { +protocol KeyBackupSetupPassphraseCoordinatorDelegate: AnyObject { func keyBackupSetupPassphraseCoordinator(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType, didCreateBackupFromPassphraseWithResultingRecoveryKey recoveryKey: String) func keyBackupSetupPassphraseCoordinator(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType, didCreateBackupFromRecoveryKey recoveryKey: String) func keyBackupSetupPassphraseCoordinatorDidCancel(_ keyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordinatorType) diff --git a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModelType.swift b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModelType.swift index 2f4b2dfaa..89da31b4d 100644 --- a/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModelType.swift +++ b/Riot/Modules/KeyBackup/Setup/Passphrase/KeyBackupSetupPassphraseViewModelType.swift @@ -16,12 +16,12 @@ import Foundation -protocol KeyBackupSetupPassphraseViewModelViewDelegate: class { +protocol KeyBackupSetupPassphraseViewModelViewDelegate: AnyObject { func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didUpdateViewState viewSate: KeyBackupSetupPassphraseViewState) func keyBackupSetupPassphraseViewModelShowSkipAlert(_ viewModel: KeyBackupSetupPassphraseViewModelType) } -protocol KeyBackupSetupPassphraseViewModelCoordinatorDelegate: class { +protocol KeyBackupSetupPassphraseViewModelCoordinatorDelegate: AnyObject { func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didCreateBackupFromPassphraseWithResultingRecoveryKey recoveryKey: String) func keyBackupSetupPassphraseViewModel(_ viewModel: KeyBackupSetupPassphraseViewModelType, didCreateBackupFromRecoveryKey recoveryKey: String) func keyBackupSetupPassphraseViewModelDidCancel(_ viewModel: KeyBackupSetupPassphraseViewModelType) diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.swift b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.swift index 57fe04ba9..563d4b0b6 100644 --- a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.swift +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromPassphraseViewController.swift @@ -16,7 +16,7 @@ import UIKit -protocol KeyBackupSetupSuccessFromPassphraseViewControllerDelegate: class { +protocol KeyBackupSetupSuccessFromPassphraseViewControllerDelegate: AnyObject { func keyBackupSetupSuccessFromPassphraseViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromPassphraseViewController) } diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift index 6f474f753..0c33c5915 100644 --- a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromRecoveryKeyViewController.swift @@ -16,7 +16,7 @@ import UIKit -protocol KeyBackupSetupSuccessFromRecoveryKeyViewControllerDelegate: class { +protocol KeyBackupSetupSuccessFromRecoveryKeyViewControllerDelegate: AnyObject { func keyBackupSetupSuccessFromRecoveryKeyViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromRecoveryKeyViewController) } diff --git a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.swift b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.swift index c369cb5e9..dcd23e6d6 100644 --- a/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.swift +++ b/Riot/Modules/KeyBackup/Setup/Success/KeyBackupSetupSuccessFromSecureBackupViewController.swift @@ -16,7 +16,7 @@ import UIKit -protocol KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate: class { +protocol KeyBackupSetupSuccessFromSecureBackupViewControllerDelegate: AnyObject { func keyBackupSetupSuccessFromSecureBackupViewControllerDidTapDoneAction(_ viewController: KeyBackupSetupSuccessFromSecureBackupViewController) } diff --git a/Riot/Modules/KeyVerification/Common/KeyVerificationCoordinatorType.swift b/Riot/Modules/KeyVerification/Common/KeyVerificationCoordinatorType.swift index 901d6b11d..ca1731afd 100644 --- a/Riot/Modules/KeyVerification/Common/KeyVerificationCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Common/KeyVerificationCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyVerificationCoordinatorDelegate: class { +protocol KeyVerificationCoordinatorDelegate: AnyObject { func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) func keyVerificationCoordinatorDidCancel(_ coordinator: KeyVerificationCoordinatorType) } diff --git a/Riot/Modules/KeyVerification/Common/Loading/KeyVerificationDataLoadingCoordinatorType.swift b/Riot/Modules/KeyVerification/Common/Loading/KeyVerificationDataLoadingCoordinatorType.swift index 8a7ba1dae..f0ff8bfa2 100644 --- a/Riot/Modules/KeyVerification/Common/Loading/KeyVerificationDataLoadingCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Common/Loading/KeyVerificationDataLoadingCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyVerificationDataLoadingCoordinatorDelegate: class { +protocol KeyVerificationDataLoadingCoordinatorDelegate: AnyObject { func keyVerificationDataLoadingCoordinator(_ coordinator: KeyVerificationDataLoadingCoordinatorType, didLoadUser user: MXUser, device: MXDeviceInfo) func keyVerificationDataLoadingCoordinator(_ coordinator: KeyVerificationDataLoadingCoordinatorType, didAcceptKeyVerificationRequestWithTransaction transaction: MXKeyVerificationTransaction) func keyVerificationDataLoadingCoordinator(_ coordinator: KeyVerificationDataLoadingCoordinatorType, didAcceptKeyVerificationRequest keyVerificationRequest: MXKeyVerificationRequest) diff --git a/Riot/Modules/KeyVerification/Common/Loading/KeyVerificationDataLoadingViewModelType.swift b/Riot/Modules/KeyVerification/Common/Loading/KeyVerificationDataLoadingViewModelType.swift index d55bcc3bd..223ce6f50 100644 --- a/Riot/Modules/KeyVerification/Common/Loading/KeyVerificationDataLoadingViewModelType.swift +++ b/Riot/Modules/KeyVerification/Common/Loading/KeyVerificationDataLoadingViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol KeyVerificationDataLoadingViewModelViewDelegate: class { +protocol KeyVerificationDataLoadingViewModelViewDelegate: AnyObject { func keyVerificationDataLoadingViewModel(_ viewModel: KeyVerificationDataLoadingViewModelType, didUpdateViewState viewSate: KeyVerificationDataLoadingViewState) } -protocol KeyVerificationDataLoadingViewModelCoordinatorDelegate: class { +protocol KeyVerificationDataLoadingViewModelCoordinatorDelegate: AnyObject { func keyVerificationDataLoadingViewModel(_ viewModel: KeyVerificationDataLoadingViewModelType, didLoadUser user: MXUser, device: MXDeviceInfo) func keyVerificationDataLoadingViewModel(_ viewModel: KeyVerificationDataLoadingViewModelType, didAcceptKeyVerificationWithTransaction transaction: MXKeyVerificationTransaction) func keyVerificationDataLoadingViewModel(_ viewModel: KeyVerificationDataLoadingViewModelType, didAcceptKeyVerificationRequest keyVerificationRequest: MXKeyVerificationRequest) diff --git a/Riot/Modules/KeyVerification/Common/ScanConfirmation/KeyVerificationScanConfirmationCoordinatorType.swift b/Riot/Modules/KeyVerification/Common/ScanConfirmation/KeyVerificationScanConfirmationCoordinatorType.swift index 35a2381c4..8087fadb2 100644 --- a/Riot/Modules/KeyVerification/Common/ScanConfirmation/KeyVerificationScanConfirmationCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Common/ScanConfirmation/KeyVerificationScanConfirmationCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyVerificationScanConfirmationCoordinatorDelegate: class { +protocol KeyVerificationScanConfirmationCoordinatorDelegate: AnyObject { func keyVerificationScanConfirmationCoordinatorDidComplete(_ coordinator: KeyVerificationScanConfirmationCoordinatorType) func keyVerificationScanConfirmationCoordinatorDidCancel(_ coordinator: KeyVerificationScanConfirmationCoordinatorType) } diff --git a/Riot/Modules/KeyVerification/Common/ScanConfirmation/KeyVerificationScanConfirmationViewModelType.swift b/Riot/Modules/KeyVerification/Common/ScanConfirmation/KeyVerificationScanConfirmationViewModelType.swift index 03d843560..fc11ea2bd 100644 --- a/Riot/Modules/KeyVerification/Common/ScanConfirmation/KeyVerificationScanConfirmationViewModelType.swift +++ b/Riot/Modules/KeyVerification/Common/ScanConfirmation/KeyVerificationScanConfirmationViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol KeyVerificationScanConfirmationViewModelViewDelegate: class { +protocol KeyVerificationScanConfirmationViewModelViewDelegate: AnyObject { func keyVerificationScanConfirmationViewModel(_ viewModel: KeyVerificationScanConfirmationViewModelType, didUpdateViewState viewSate: KeyVerificationScanConfirmationViewState) } -protocol KeyVerificationScanConfirmationViewModelCoordinatorDelegate: class { +protocol KeyVerificationScanConfirmationViewModelCoordinatorDelegate: AnyObject { func keyVerificationScanConfirmationViewModelDidComplete(_ viewModel: KeyVerificationScanConfirmationViewModelType) func keyVerificationScanConfirmationViewModelDidCancel(_ viewModel: KeyVerificationScanConfirmationViewModelType) } diff --git a/Riot/Modules/KeyVerification/Common/Verified/KeyVerificationVerifiedViewController.swift b/Riot/Modules/KeyVerification/Common/Verified/KeyVerificationVerifiedViewController.swift index ed869052f..32cd0cf6a 100644 --- a/Riot/Modules/KeyVerification/Common/Verified/KeyVerificationVerifiedViewController.swift +++ b/Riot/Modules/KeyVerification/Common/Verified/KeyVerificationVerifiedViewController.swift @@ -18,7 +18,7 @@ import UIKit -protocol KeyVerificationVerifiedViewControllerDelegate: class { +protocol KeyVerificationVerifiedViewControllerDelegate: AnyObject { func keyVerificationVerifiedViewControllerDidTapSetupAction(_ viewController: KeyVerificationVerifiedViewController) func keyVerificationVerifiedViewControllerDidCancel(_ viewController: KeyVerificationVerifiedViewController) } diff --git a/Riot/Modules/KeyVerification/Common/Verify/SAS/KeyVerificationVerifyBySASCoordinatorType.swift b/Riot/Modules/KeyVerification/Common/Verify/SAS/KeyVerificationVerifyBySASCoordinatorType.swift index 378e2517e..77ac567ff 100644 --- a/Riot/Modules/KeyVerification/Common/Verify/SAS/KeyVerificationVerifyBySASCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Common/Verify/SAS/KeyVerificationVerifyBySASCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyVerificationVerifyBySASCoordinatorDelegate: class { +protocol KeyVerificationVerifyBySASCoordinatorDelegate: AnyObject { func keyVerificationVerifyBySASCoordinatorDidComplete(_ coordinator: KeyVerificationVerifyBySASCoordinatorType) func keyVerificationVerifyBySASCoordinatorDidCancel(_ coordinator: KeyVerificationVerifyBySASCoordinatorType) } diff --git a/Riot/Modules/KeyVerification/Common/Verify/SAS/KeyVerificationVerifyBySASViewModelType.swift b/Riot/Modules/KeyVerification/Common/Verify/SAS/KeyVerificationVerifyBySASViewModelType.swift index a5686bc7a..10c26756a 100644 --- a/Riot/Modules/KeyVerification/Common/Verify/SAS/KeyVerificationVerifyBySASViewModelType.swift +++ b/Riot/Modules/KeyVerification/Common/Verify/SAS/KeyVerificationVerifyBySASViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol KeyVerificationVerifyBySASViewModelViewDelegate: class { +protocol KeyVerificationVerifyBySASViewModelViewDelegate: AnyObject { func keyVerificationVerifyBySASViewModel(_ viewModel: KeyVerificationVerifyBySASViewModelType, didUpdateViewState viewSate: KeyVerificationVerifyViewState) } -protocol KeyVerificationVerifyBySASViewModelCoordinatorDelegate: class { +protocol KeyVerificationVerifyBySASViewModelCoordinatorDelegate: AnyObject { func keyVerificationVerifyViewModelDidComplete(_ viewModel: KeyVerificationVerifyBySASViewModelType) func keyVerificationVerifyViewModelDidCancel(_ viewModel: KeyVerificationVerifyBySASViewModelType) } diff --git a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningCoordinatorType.swift b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningCoordinatorType.swift index 0bf2711f6..5df1ebde6 100644 --- a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyVerificationVerifyByScanningCoordinatorDelegate: class { +protocol KeyVerificationVerifyByScanningCoordinatorDelegate: AnyObject { func keyVerificationVerifyByScanningCoordinatorDidCancel(_ coordinator: KeyVerificationVerifyByScanningCoordinatorType) func keyVerificationVerifyByScanningCoordinator(_ coordinator: KeyVerificationVerifyByScanningCoordinatorType, didScanOtherQRCodeData qrCodeData: MXQRCodeData, withTransaction transaction: MXQRCodeTransaction) func keyVerificationVerifyByScanningCoordinator(_ coordinator: KeyVerificationVerifyByScanningCoordinatorType, qrCodeDidScannedByOtherWithTransaction transaction: MXQRCodeTransaction) diff --git a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift index fc01906da..49f1a80ca 100644 --- a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift +++ b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift @@ -143,7 +143,9 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController { self.titleLabel.text = VectorL10n.keyVerificationVerifyQrCodeTitle self.informationLabel.text = VectorL10n.keyVerificationVerifyQrCodeInformation - self.scanCodeButton.setTitle(VectorL10n.keyVerificationVerifyQrCodeScanCodeAction, for: .normal) + // Hide until we have the type of the verification request + self.scanCodeButton.isHidden = true + self.cannotScanButton.setTitle(VectorL10n.keyVerificationVerifyQrCodeCannotScanAction, for: .normal) } @@ -157,8 +159,8 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController { self.render(error: error) case .scannedCodeValidated(let isValid): self.renderScannedCode(valid: isValid) - case .cancelled(let reason): - self.renderCancelled(reason: reason) + case .cancelled(let reason, let verificationKind): + self.renderCancelled(reason: reason, verificationKind: verificationKind) case .cancelledByMe(let reason): self.renderCancelledByMe(reason: reason) } @@ -195,10 +197,13 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController { switch viewData.verificationKind { case .user: informationText = VectorL10n.keyVerificationVerifyQrCodeInformation + self.scanCodeButton.setTitle(VectorL10n.keyVerificationVerifyQrCodeScanCodeAction, for: .normal) default: informationText = VectorL10n.keyVerificationVerifyQrCodeInformationOtherDevice + self.scanCodeButton.setTitle(VectorL10n.keyVerificationVerifyQrCodeScanCodeOtherDeviceAction, for: .normal) } + self.scanCodeButton.isHidden = false self.informationLabel.text = informationText } } @@ -231,12 +236,21 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController { } } - private func renderCancelled(reason: MXTransactionCancelCode) { + private func renderCancelled(reason: MXTransactionCancelCode, + verificationKind: KeyVerificationKind) { self.activityPresenter.removeCurrentActivityIndicator(animated: true) self.stopQRCodeScanningIfPresented() - self.errorPresenter.presentError(from: self.alertPresentingViewController, title: "", message: VectorL10n.deviceVerificationCancelled, animated: true) { + // if we're verifying with someone else, let the user know they cancelled. + // if we're verifying our own device, assume the user probably knows since it was them who + // cancelled on their other device + if verificationKind == .user { + self.errorPresenter.presentError(from: self.alertPresentingViewController, title: "", message: VectorL10n.deviceVerificationCancelled, animated: true) { + self.dismissQRCodeScanningIfPresented(animated: false) + self.viewModel.process(viewAction: .cancel) + } + } else { self.dismissQRCodeScanningIfPresented(animated: false) self.viewModel.process(viewAction: .cancel) } diff --git a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewModel.swift b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewModel.swift index 706a121d9..de4b9e189 100644 --- a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewModel.swift +++ b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewModel.swift @@ -225,7 +225,7 @@ final class KeyVerificationVerifyByScanningViewModel: KeyVerificationVerifyBySca return } self.unregisterTransactionDidStateChangeNotification() - self.update(viewState: .cancelled(reason)) + self.update(viewState: .cancelled(cancelCode: reason, verificationKind: verificationKind)) case MXSASTransactionStateCancelledByMe: guard let reason = transaction.reasonCancelCode else { return @@ -251,7 +251,7 @@ final class KeyVerificationVerifyByScanningViewModel: KeyVerificationVerifyBySca return } self.unregisterTransactionDidStateChangeNotification() - self.update(viewState: .cancelled(reason)) + self.update(viewState: .cancelled(cancelCode: reason, verificationKind: verificationKind)) case .cancelledByMe: guard let reason = transaction.reasonCancelCode else { return diff --git a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewModelType.swift b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewModelType.swift index 4be0568d5..d7eedfabc 100644 --- a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewModelType.swift +++ b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol KeyVerificationVerifyByScanningViewModelViewDelegate: class { +protocol KeyVerificationVerifyByScanningViewModelViewDelegate: AnyObject { func keyVerificationVerifyByScanningViewModel(_ viewModel: KeyVerificationVerifyByScanningViewModelType, didUpdateViewState viewSate: KeyVerificationVerifyByScanningViewState) } -protocol KeyVerificationVerifyByScanningViewModelCoordinatorDelegate: class { +protocol KeyVerificationVerifyByScanningViewModelCoordinatorDelegate: AnyObject { func keyVerificationVerifyByScanningViewModelDidCancel(_ viewModel: KeyVerificationVerifyByScanningViewModelType) func keyVerificationVerifyByScanningViewModel(_ viewModel: KeyVerificationVerifyByScanningViewModelType, didScanOtherQRCodeData qrCodeData: MXQRCodeData, withTransaction transaction: MXQRCodeTransaction) diff --git a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewState.swift b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewState.swift index b6323eb70..e3ecc8238 100644 --- a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewState.swift +++ b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewState.swift @@ -29,7 +29,7 @@ enum KeyVerificationVerifyByScanningViewState { case loading case loaded(viewData: KeyVerificationVerifyByScanningViewData) case scannedCodeValidated(isValid: Bool) - case cancelled(MXTransactionCancelCode) + case cancelled(cancelCode: MXTransactionCancelCode, verificationKind: KeyVerificationKind) case cancelledByMe(MXTransactionCancelCode) case error(Error) } diff --git a/Riot/Modules/KeyVerification/Device/Incoming/DeviceVerificationIncomingCoordinatorType.swift b/Riot/Modules/KeyVerification/Device/Incoming/DeviceVerificationIncomingCoordinatorType.swift index 46e135212..1d5e600ab 100644 --- a/Riot/Modules/KeyVerification/Device/Incoming/DeviceVerificationIncomingCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Device/Incoming/DeviceVerificationIncomingCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol DeviceVerificationIncomingCoordinatorDelegate: class { +protocol DeviceVerificationIncomingCoordinatorDelegate: AnyObject { func deviceVerificationIncomingCoordinator(_ coordinator: DeviceVerificationIncomingCoordinatorType, didAcceptTransaction message: MXSASTransaction) func deviceVerificationIncomingCoordinatorDidCancel(_ coordinator: DeviceVerificationIncomingCoordinatorType) } diff --git a/Riot/Modules/KeyVerification/Device/Incoming/DeviceVerificationIncomingViewModelType.swift b/Riot/Modules/KeyVerification/Device/Incoming/DeviceVerificationIncomingViewModelType.swift index 465fb4013..48b148aa1 100644 --- a/Riot/Modules/KeyVerification/Device/Incoming/DeviceVerificationIncomingViewModelType.swift +++ b/Riot/Modules/KeyVerification/Device/Incoming/DeviceVerificationIncomingViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol DeviceVerificationIncomingViewModelViewDelegate: class { +protocol DeviceVerificationIncomingViewModelViewDelegate: AnyObject { func deviceVerificationIncomingViewModel(_ viewModel: DeviceVerificationIncomingViewModelType, didUpdateViewState viewSate: DeviceVerificationIncomingViewState) } -protocol DeviceVerificationIncomingViewModelCoordinatorDelegate: class { +protocol DeviceVerificationIncomingViewModelCoordinatorDelegate: AnyObject { func deviceVerificationIncomingViewModel(_ viewModel: DeviceVerificationIncomingViewModelType, didAcceptTransaction transaction: MXSASTransaction) func deviceVerificationIncomingViewModelDidCancel(_ viewModel: DeviceVerificationIncomingViewModelType) } diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinatorType.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinatorType.swift index 7f37b9564..ef098fcd2 100644 --- a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyVerificationManuallyVerifyCoordinatorDelegate: class { +protocol KeyVerificationManuallyVerifyCoordinatorDelegate: AnyObject { func keyVerificationManuallyVerifyCoordinator(_ coordinator: KeyVerificationManuallyVerifyCoordinatorType, didVerifiedDeviceWithId deviceId: String, of userId: String) func keyVerificationManuallyVerifyCoordinatorDidCancel(_ coordinator: KeyVerificationManuallyVerifyCoordinatorType) } diff --git a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModelType.swift b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModelType.swift index 49f1972a5..1f1a73a57 100644 --- a/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModelType.swift +++ b/Riot/Modules/KeyVerification/Device/ManuallyVerify/KeyVerificationManuallyVerifyViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol KeyVerificationManuallyVerifyViewModelViewDelegate: class { +protocol KeyVerificationManuallyVerifyViewModelViewDelegate: AnyObject { func keyVerificationManuallyVerifyViewModel(_ viewModel: KeyVerificationManuallyVerifyViewModelType, didUpdateViewState viewSate: KeyVerificationManuallyVerifyViewState) } -protocol KeyVerificationManuallyVerifyViewModelCoordinatorDelegate: class { +protocol KeyVerificationManuallyVerifyViewModelCoordinatorDelegate: AnyObject { func keyVerificationManuallyVerifyViewModel(_ viewModel: KeyVerificationManuallyVerifyViewModelType, didVerifiedDeviceWithId deviceId: String, of userId: String) func keyVerificationManuallyVerifyViewModelDidCancel(_ viewModel: KeyVerificationManuallyVerifyViewModelType) } diff --git a/Riot/Modules/KeyVerification/Device/SelfVerifyStart/KeyVerificationSelfVerifyStartCoordinatorType.swift b/Riot/Modules/KeyVerification/Device/SelfVerifyStart/KeyVerificationSelfVerifyStartCoordinatorType.swift index faa2a8555..6799602d3 100644 --- a/Riot/Modules/KeyVerification/Device/SelfVerifyStart/KeyVerificationSelfVerifyStartCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Device/SelfVerifyStart/KeyVerificationSelfVerifyStartCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyVerificationSelfVerifyStartCoordinatorDelegate: class { +protocol KeyVerificationSelfVerifyStartCoordinatorDelegate: AnyObject { func keyVerificationSelfVerifyStartCoordinator(_ coordinator: KeyVerificationSelfVerifyStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest) diff --git a/Riot/Modules/KeyVerification/Device/SelfVerifyStart/KeyVerificationSelfVerifyStartViewModelType.swift b/Riot/Modules/KeyVerification/Device/SelfVerifyStart/KeyVerificationSelfVerifyStartViewModelType.swift index 0de1eccf5..2f70b5a31 100644 --- a/Riot/Modules/KeyVerification/Device/SelfVerifyStart/KeyVerificationSelfVerifyStartViewModelType.swift +++ b/Riot/Modules/KeyVerification/Device/SelfVerifyStart/KeyVerificationSelfVerifyStartViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol KeyVerificationSelfVerifyStartViewModelViewDelegate: class { +protocol KeyVerificationSelfVerifyStartViewModelViewDelegate: AnyObject { func keyVerificationSelfVerifyStartViewModel(_ viewModel: KeyVerificationSelfVerifyStartViewModelType, didUpdateViewState viewSate: KeyVerificationSelfVerifyStartViewState) } -protocol KeyVerificationSelfVerifyStartViewModelCoordinatorDelegate: class { +protocol KeyVerificationSelfVerifyStartViewModelCoordinatorDelegate: AnyObject { func keyVerificationSelfVerifyStartViewModel(_ viewModel: KeyVerificationSelfVerifyStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest) func keyVerificationSelfVerifyStartViewModelDidCancel(_ viewModel: KeyVerificationSelfVerifyStartViewModelType) } diff --git a/Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitCoordinatorType.swift b/Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitCoordinatorType.swift index 6c0219c4a..ba1b6c410 100644 --- a/Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol KeyVerificationSelfVerifyWaitCoordinatorDelegate: class { +protocol KeyVerificationSelfVerifyWaitCoordinatorDelegate: AnyObject { func keyVerificationSelfVerifyWaitCoordinator(_ coordinator: KeyVerificationSelfVerifyWaitCoordinatorType, didAcceptKeyVerificationRequest keyVerificationRequest: MXKeyVerificationRequest) func keyVerificationSelfVerifyWaitCoordinator(_ coordinator: KeyVerificationSelfVerifyWaitCoordinatorType, didAcceptIncomingSASTransaction incomingSASTransaction: MXIncomingSASTransaction) func keyVerificationSelfVerifyWaitCoordinatorDidCancel(_ coordinator: KeyVerificationSelfVerifyWaitCoordinatorType) diff --git a/Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitViewModelType.swift b/Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitViewModelType.swift index c6fc85ff0..264a73b83 100644 --- a/Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitViewModelType.swift +++ b/Riot/Modules/KeyVerification/Device/SelfVerifyWait/KeyVerificationSelfVerifyWaitViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol KeyVerificationSelfVerifyWaitViewModelViewDelegate: class { +protocol KeyVerificationSelfVerifyWaitViewModelViewDelegate: AnyObject { func keyVerificationSelfVerifyWaitViewModel(_ viewModel: KeyVerificationSelfVerifyWaitViewModelType, didUpdateViewState viewSate: KeyVerificationSelfVerifyWaitViewState) } -protocol KeyVerificationSelfVerifyWaitViewModelCoordinatorDelegate: class { +protocol KeyVerificationSelfVerifyWaitViewModelCoordinatorDelegate: AnyObject { func keyVerificationSelfVerifyWaitViewModel(_ viewModel: KeyVerificationSelfVerifyWaitViewModelType, didAcceptKeyVerificationRequest keyVerificationRequest: MXKeyVerificationRequest) func keyVerificationSelfVerifyWaitViewModel(_ viewModel: KeyVerificationSelfVerifyWaitViewModelType, didAcceptIncomingSASTransaction incomingSASTransaction: MXIncomingSASTransaction) func keyVerificationSelfVerifyWaitViewModelDidCancel(_ viewModel: KeyVerificationSelfVerifyWaitViewModelType) diff --git a/Riot/Modules/KeyVerification/Device/Start/DeviceVerificationStartCoordinatorType.swift b/Riot/Modules/KeyVerification/Device/Start/DeviceVerificationStartCoordinatorType.swift index 72871ee60..16a79760c 100644 --- a/Riot/Modules/KeyVerification/Device/Start/DeviceVerificationStartCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/Device/Start/DeviceVerificationStartCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol DeviceVerificationStartCoordinatorDelegate: class { +protocol DeviceVerificationStartCoordinatorDelegate: AnyObject { func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) diff --git a/Riot/Modules/KeyVerification/Device/Start/DeviceVerificationStartViewModelType.swift b/Riot/Modules/KeyVerification/Device/Start/DeviceVerificationStartViewModelType.swift index 07da5a197..015e80faf 100644 --- a/Riot/Modules/KeyVerification/Device/Start/DeviceVerificationStartViewModelType.swift +++ b/Riot/Modules/KeyVerification/Device/Start/DeviceVerificationStartViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol DeviceVerificationStartViewModelViewDelegate: class { +protocol DeviceVerificationStartViewModelViewDelegate: AnyObject { func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didUpdateViewState viewSate: DeviceVerificationStartViewState) } -protocol DeviceVerificationStartViewModelCoordinatorDelegate: class { +protocol DeviceVerificationStartViewModelCoordinatorDelegate: AnyObject { func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType) func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) diff --git a/Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusCoordinatorType.swift b/Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusCoordinatorType.swift index 05d2c73b8..107fa42fa 100644 --- a/Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol UserVerificationSessionStatusCoordinatorDelegate: class { +protocol UserVerificationSessionStatusCoordinatorDelegate: AnyObject { func userVerificationSessionStatusCoordinator(_ coordinator: UserVerificationSessionStatusCoordinatorType, wantsToVerifyDeviceWithId deviceId: String, for userId: String) func userVerificationSessionStatusCoordinator(_ coordinator: UserVerificationSessionStatusCoordinatorType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String) func userVerificationSessionStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionStatusCoordinatorType) diff --git a/Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusViewModelType.swift b/Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusViewModelType.swift index 4598b4fa8..b600e235e 100644 --- a/Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusViewModelType.swift +++ b/Riot/Modules/KeyVerification/User/SessionStatus/UserVerificationSessionStatusViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol UserVerificationSessionStatusViewModelViewDelegate: class { +protocol UserVerificationSessionStatusViewModelViewDelegate: AnyObject { func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionStatusViewState) } -protocol UserVerificationSessionStatusViewModelCoordinatorDelegate: class { +protocol UserVerificationSessionStatusViewModelCoordinatorDelegate: AnyObject { func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, wantsToVerifyDeviceWithId deviceId: String, for userId: String) func userVerificationSessionStatusViewModel(_ viewModel: UserVerificationSessionStatusViewModelType, wantsToManuallyVerifyDeviceWithId deviceId: String, for userId: String) func userVerificationSessionStatusViewModelDidClose(_ viewModel: UserVerificationSessionStatusViewModelType) diff --git a/Riot/Modules/KeyVerification/User/SessionsStatus/UserVerificationSessionsStatusCoordinatorType.swift b/Riot/Modules/KeyVerification/User/SessionsStatus/UserVerificationSessionsStatusCoordinatorType.swift index 46eccaaa5..67c39e688 100644 --- a/Riot/Modules/KeyVerification/User/SessionsStatus/UserVerificationSessionsStatusCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/User/SessionsStatus/UserVerificationSessionsStatusCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol UserVerificationSessionsStatusCoordinatorDelegate: class { +protocol UserVerificationSessionsStatusCoordinatorDelegate: AnyObject { func userVerificationSessionsStatusCoordinatorDidClose(_ coordinator: UserVerificationSessionsStatusCoordinatorType) func userVerificationSessionsStatusCoordinator(_ coordinator: UserVerificationSessionsStatusCoordinatorType, didSelectDeviceWithId deviceId: String, for userId: String) } diff --git a/Riot/Modules/KeyVerification/User/SessionsStatus/UserVerificationSessionsStatusViewModelType.swift b/Riot/Modules/KeyVerification/User/SessionsStatus/UserVerificationSessionsStatusViewModelType.swift index 4df41221d..fe704b6ad 100644 --- a/Riot/Modules/KeyVerification/User/SessionsStatus/UserVerificationSessionsStatusViewModelType.swift +++ b/Riot/Modules/KeyVerification/User/SessionsStatus/UserVerificationSessionsStatusViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol UserVerificationSessionsStatusViewModelViewDelegate: class { +protocol UserVerificationSessionsStatusViewModelViewDelegate: AnyObject { func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didUpdateViewState viewSate: UserVerificationSessionsStatusViewState) } -protocol UserVerificationSessionsStatusViewModelCoordinatorDelegate: class { +protocol UserVerificationSessionsStatusViewModelCoordinatorDelegate: AnyObject { func userVerificationSessionsStatusViewModel(_ viewModel: UserVerificationSessionsStatusViewModelType, didSelectDeviceWithId deviceId: String, for userId: String) func userVerificationSessionsStatusViewModelDidClose(_ viewModel: UserVerificationSessionsStatusViewModelType) } diff --git a/Riot/Modules/KeyVerification/User/Start/UserVerificationStartCoordinatorType.swift b/Riot/Modules/KeyVerification/User/Start/UserVerificationStartCoordinatorType.swift index ba5c677d9..7f0130b5a 100644 --- a/Riot/Modules/KeyVerification/User/Start/UserVerificationStartCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/User/Start/UserVerificationStartCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol UserVerificationStartCoordinatorDelegate: class { +protocol UserVerificationStartCoordinatorDelegate: AnyObject { func userVerificationStartCoordinator(_ coordinator: UserVerificationStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest) diff --git a/Riot/Modules/KeyVerification/User/Start/UserVerificationStartViewModelType.swift b/Riot/Modules/KeyVerification/User/Start/UserVerificationStartViewModelType.swift index 40582e0bf..b84043938 100644 --- a/Riot/Modules/KeyVerification/User/Start/UserVerificationStartViewModelType.swift +++ b/Riot/Modules/KeyVerification/User/Start/UserVerificationStartViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol UserVerificationStartViewModelViewDelegate: class { +protocol UserVerificationStartViewModelViewDelegate: AnyObject { func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, didUpdateViewState viewSate: UserVerificationStartViewState) } -protocol UserVerificationStartViewModelCoordinatorDelegate: class { +protocol UserVerificationStartViewModelCoordinatorDelegate: AnyObject { func userVerificationStartViewModel(_ viewModel: UserVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest) diff --git a/Riot/Modules/KeyVerification/User/UserVerificationCoordinatorType.swift b/Riot/Modules/KeyVerification/User/UserVerificationCoordinatorType.swift index feeba8d3b..db665607e 100644 --- a/Riot/Modules/KeyVerification/User/UserVerificationCoordinatorType.swift +++ b/Riot/Modules/KeyVerification/User/UserVerificationCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol UserVerificationCoordinatorDelegate: class { +protocol UserVerificationCoordinatorDelegate: AnyObject { func userVerificationCoordinatorDidComplete(_ coordinator: UserVerificationCoordinatorType) } diff --git a/Riot/Modules/MediaPicker/MediaPickerCoordinator.swift b/Riot/Modules/MediaPicker/MediaPickerCoordinator.swift index d3b9c2c0d..61586be8d 100644 --- a/Riot/Modules/MediaPicker/MediaPickerCoordinator.swift +++ b/Riot/Modules/MediaPicker/MediaPickerCoordinator.swift @@ -83,8 +83,8 @@ extension MediaPickerCoordinator: MediaPickerViewControllerDelegate { self.delegate?.mediaPickerCoordinator(self, didSelectImageData: imageData, withUTI: uti) } - func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelectVideo videoURL: URL!) { - self.delegate?.mediaPickerCoordinator(self, didSelectVideoAt: videoURL) + func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelectVideo videoAsset: AVAsset!) { + self.delegate?.mediaPickerCoordinator(self, didSelectVideo: videoAsset) } func mediaPickerController(_ mediaPickerController: MediaPickerViewController!, didSelect assets: [PHAsset]!) { diff --git a/Riot/Modules/MediaPicker/MediaPickerCoordinatorBridgePresenter.swift b/Riot/Modules/MediaPicker/MediaPickerCoordinatorBridgePresenter.swift index 0ba21d893..782662ea9 100644 --- a/Riot/Modules/MediaPicker/MediaPickerCoordinatorBridgePresenter.swift +++ b/Riot/Modules/MediaPicker/MediaPickerCoordinatorBridgePresenter.swift @@ -20,7 +20,7 @@ import Foundation @objc protocol MediaPickerCoordinatorBridgePresenterDelegate { func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) - func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectVideoAt url: URL) + func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectVideo videoAsset: AVAsset) func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectAssets assets: [PHAsset]) func mediaPickerCoordinatorBridgePresenterDidCancel(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter) } @@ -110,8 +110,8 @@ extension MediaPickerCoordinatorBridgePresenter: MediaPickerCoordinatorDelegate self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectImageData: imageData, withUTI: uti) } - func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectVideoAt url: URL) { - self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectVideoAt: url) + func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectVideo videoAsset: AVAsset) { + self.delegate?.mediaPickerCoordinatorBridgePresenter(self, didSelectVideo: videoAsset) } func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectAssets assets: [PHAsset]) { diff --git a/Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift b/Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift index 53f154536..4f8fc5d8f 100644 --- a/Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift +++ b/Riot/Modules/MediaPicker/MediaPickerCoordinatorType.swift @@ -18,9 +18,9 @@ import Foundation -protocol MediaPickerCoordinatorDelegate: class { +protocol MediaPickerCoordinatorDelegate: AnyObject { func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) - func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectVideoAt url: URL) + func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectVideo videoAsset: AVAsset) func mediaPickerCoordinator(_ coordinator: MediaPickerCoordinatorType, didSelectAssets assets: [PHAsset]) func mediaPickerCoordinatorDidCancel(_ coordinator: MediaPickerCoordinatorType) } diff --git a/Riot/Modules/MediaPicker/MediaPickerViewController.h b/Riot/Modules/MediaPicker/MediaPickerViewController.h index 99e9365fe..71487e1ea 100644 --- a/Riot/Modules/MediaPicker/MediaPickerViewController.h +++ b/Riot/Modules/MediaPicker/MediaPickerViewController.h @@ -39,9 +39,9 @@ Tells the delegate that the user select a video. @param mediaPickerController the `MediaPickerViewController` instance. - @param videoURL the local url of the video to send. + @param videoAsset an `AVAsset` that represents the video to send. */ -- (void)mediaPickerController:(MediaPickerViewController *)mediaPickerController didSelectVideo:(NSURL*)videoURL; +- (void)mediaPickerController:(MediaPickerViewController *)mediaPickerController didSelectVideo:(AVAsset*)videoAsset; /** Tells the delegate that the user wants to cancel media picking. diff --git a/Riot/Modules/MediaPicker/MediaPickerViewController.m b/Riot/Modules/MediaPicker/MediaPickerViewController.m index 6b7725a11..e34617241 100644 --- a/Riot/Modules/MediaPicker/MediaPickerViewController.m +++ b/Riot/Modules/MediaPicker/MediaPickerViewController.m @@ -608,28 +608,19 @@ if (asset) { - if ([asset isKindOfClass:[AVURLAsset class]]) - { - MXLogDebug(@"[MediaPickerVC] didSelectAsset: Got AVAsset for video"); - AVURLAsset *avURLAsset = (AVURLAsset*)asset; + MXLogDebug(@"[MediaPickerVC] didSelectAsset: Got AVAsset for video"); + + // Validate first the selected video + [self validateSelectedVideo:asset responseHandler:^(BOOL isValidated) { + + if (isValidated) + { + [self.delegate mediaPickerController:self didSelectVideo:asset]; + } - // Validate first the selected video - [self validateSelectedVideo:[avURLAsset URL] responseHandler:^(BOOL isValidated) { - - if (isValidated) - { - [self.delegate mediaPickerController:self didSelectVideo:[avURLAsset URL]]; - } - - self->isValidationInProgress = NO; - - }]; - } - else - { - MXLogDebug(@"[MediaPickerVC] Selected video asset is not initialized from an URL!"); self->isValidationInProgress = NO; - } + + }]; } else { @@ -693,7 +684,7 @@ [self setNeedsStatusBarAppearanceUpdate]; } -- (void)validateSelectedVideo:(NSURL*)selectedVideoURL responseHandler:(void (^)(BOOL isValidated))handler +- (void)validateSelectedVideo:(AVAsset*)selectedVideo responseHandler:(void (^)(BOOL isValidated))handler { [self dismissImageValidationView]; @@ -727,15 +718,16 @@ videoPlayer = [[AVPlayerViewController alloc] init]; if (videoPlayer) { + AVPlayerItem *item = [AVPlayerItem playerItemWithAsset:selectedVideo]; videoPlayer.allowsPictureInPicturePlayback = NO; videoPlayer.updatesNowPlayingInfoCenter = NO; - videoPlayer.player = [AVPlayer playerWithURL:selectedVideoURL]; + videoPlayer.player = [AVPlayer playerWithPlayerItem:item]; videoPlayer.videoGravity = AVLayerVideoGravityResizeAspect; videoPlayer.showsPlaybackControls = NO; // create a thumbnail for the first frame - AVAsset *asset = [AVAsset assetWithURL:selectedVideoURL]; - AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset]; + AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:selectedVideo]; + generator.appliesPreferredTrackTransform = YES; CGImageRef thumbnailRef = [generator copyCGImageAtTime:kCMTimeZero actualTime:nil error:nil]; // set thumbnail on validationView diff --git a/Riot/Modules/MediaPicker/SingleImagePickerPresenter.swift b/Riot/Modules/MediaPicker/SingleImagePickerPresenter.swift index f8bc066ee..1a1d20169 100644 --- a/Riot/Modules/MediaPicker/SingleImagePickerPresenter.swift +++ b/Riot/Modules/MediaPicker/SingleImagePickerPresenter.swift @@ -18,7 +18,7 @@ import Foundation import UIKit import AVFoundation -@objc protocol SingleImagePickerPresenterDelegate: class { +@objc protocol SingleImagePickerPresenterDelegate: AnyObject { func singleImagePickerPresenter(_ presenter: SingleImagePickerPresenter, didSelectImageData imageData: Data, withUTI uti: MXKUTI?) func singleImagePickerPresenterDidCancel(_ presenter: SingleImagePickerPresenter) } @@ -135,7 +135,7 @@ extension SingleImagePickerPresenter: MediaPickerCoordinatorBridgePresenterDeleg self.delegate?.singleImagePickerPresenter(self, didSelectImageData: imageData, withUTI: uti) } - func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectVideoAt url: URL) { + func mediaPickerCoordinatorBridgePresenter(_ coordinatorBridgePresenter: MediaPickerCoordinatorBridgePresenter, didSelectVideo videoAsset: AVAsset) { self.delegate?.singleImagePickerPresenterDidCancel(self) } diff --git a/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.h b/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.h index 752cce049..750daac05 100644 --- a/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.h +++ b/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.h @@ -62,11 +62,16 @@ @property (nonatomic, readonly) NSString *directoryServerDisplayname; /** - The number of public rooms matching `searchPattern`. - It is accurate only if 'moreThanRoomsCount' is NO. + The number of public rooms that have been fetched so far. */ @property (nonatomic, readonly) NSUInteger roomsCount; +/** + The total number of public rooms matching `searchPattern`. + It is accurate only if 'searchResultsCountIsLimited' is NO. + */ +@property (nonatomic, readonly) NSUInteger searchResultsCount; + /** In case of search with a lot of matching public rooms, we cannot return an accurate value except by paginating the full list of rooms, which is not expected. @@ -74,7 +79,7 @@ This flag indicates that we know that there is more matching rooms than we got so far. */ -@property (nonatomic, readonly) BOOL moreThanRoomsCount; +@property (nonatomic, readonly) BOOL searchResultsCountIsLimited; /** The maximum number of public rooms to retrieve during a pagination. diff --git a/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m b/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m index e69035aa0..fa8ac0ab0 100644 --- a/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m +++ b/Riot/Modules/PublicRoomList/DataSources/PublicRoomsDirectoryDataSource.m @@ -165,6 +165,11 @@ static NSString *const kNSFWKeyword = @"nsfw"; } } +- (NSUInteger)roomsCount +{ + return rooms.count; +} + - (NSIndexPath*)cellIndexPathWithRoomId:(NSString*)roomId andMatrixSession:(MXSession*)matrixSession { NSIndexPath *indexPath = nil; @@ -217,8 +222,8 @@ static NSString *const kNSFWKeyword = @"nsfw"; // Reset all pagination vars [rooms removeAllObjects]; nextBatch = nil; - _roomsCount = 0; - _moreThanRoomsCount = NO; + _searchResultsCount = 0; + _searchResultsCountIsLimited = NO; _hasReachedPaginationEnd = NO; } @@ -264,14 +269,14 @@ static NSString *const kNSFWKeyword = @"nsfw"; if (!self->_searchPattern) { // When there is no search, we can use totalRoomCountEstimate returned by the server - self->_roomsCount = publicRoomsResponse.totalRoomCountEstimate; - self->_moreThanRoomsCount = NO; + self->_searchResultsCount = publicRoomsResponse.totalRoomCountEstimate; + self->_searchResultsCountIsLimited = NO; } else { // Else we can only display something like ">20 matching rooms" - self->_roomsCount = self->rooms.count; - self->_moreThanRoomsCount = publicRoomsResponse.nextBatch ? YES : NO; + self->_searchResultsCount = self->rooms.count; + self->_searchResultsCountIsLimited = publicRoomsResponse.nextBatch ? YES : NO; } // Detect pagination end diff --git a/Riot/Modules/QRCode/Reader/QRCodeReaderViewController.swift b/Riot/Modules/QRCode/Reader/QRCodeReaderViewController.swift index 766795d63..4d35af705 100644 --- a/Riot/Modules/QRCode/Reader/QRCodeReaderViewController.swift +++ b/Riot/Modules/QRCode/Reader/QRCodeReaderViewController.swift @@ -17,7 +17,7 @@ import UIKit import ZXingObjC -protocol QRCodeReaderViewControllerDelegate: class { +protocol QRCodeReaderViewControllerDelegate: AnyObject { func qrCodeReaderViewController(_ viewController: QRCodeReaderViewController, didFound payloadData: Data) func qrCodeReaderViewControllerDidCancel(_ viewController: QRCodeReaderViewController) } diff --git a/Riot/Modules/Reauthentication/ReauthenticationCoordinatorType.swift b/Riot/Modules/Reauthentication/ReauthenticationCoordinatorType.swift index bdbc31ce2..96c48215d 100644 --- a/Riot/Modules/Reauthentication/ReauthenticationCoordinatorType.swift +++ b/Riot/Modules/Reauthentication/ReauthenticationCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol ReauthenticationCoordinatorDelegate: class { +protocol ReauthenticationCoordinatorDelegate: AnyObject { func reauthenticationCoordinatorDidComplete(_ coordinator: ReauthenticationCoordinatorType, withAuthenticationParameters: [String: Any]?) func reauthenticationCoordinatorDidCancel(_ coordinator: ReauthenticationCoordinatorType) func reauthenticationCoordinator(_ coordinator: ReauthenticationCoordinatorType, didFailWithError: Error) diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift index e67dcfdbd..77cab862e 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift @@ -39,7 +39,7 @@ enum BubbleReactionsViewState { } } -@objc protocol BubbleReactionsViewModelDelegate: class { +@objc protocol BubbleReactionsViewModelDelegate: AnyObject { func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didAddReaction reactionCount: MXReactionCount, forEventId eventId: String) func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didRemoveReaction reactionCount: MXReactionCount, forEventId eventId: String) func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didShowAllTappedForEventId eventId: String) @@ -47,7 +47,7 @@ enum BubbleReactionsViewState { func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didLongPressForEventId eventId: String) } -protocol BubbleReactionsViewModelViewDelegate: class { +protocol BubbleReactionsViewModelViewDelegate: AnyObject { func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didUpdateViewState viewState: BubbleReactionsViewState) } diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 423b84426..2a216c44f 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -986,6 +986,9 @@ static NSAttributedString *timestampVerticalWhitespace = nil; case MXKAttachmentTypeAudio: accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_audio", @"Vector", nil); break; + case MXKAttachmentTypeVoiceMessage: + accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_audio", @"Vector", nil); + break; case MXKAttachmentTypeVideo: accessibilityLabel = NSLocalizedStringFromTable(@"media_type_accessibility_video", @"Vector", nil); break; diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift index fee8b41d9..2171e3ab5 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift @@ -16,11 +16,11 @@ import Foundation -protocol ReactionsMenuViewModelViewDelegate: class { +protocol ReactionsMenuViewModelViewDelegate: AnyObject { func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didUpdateViewState viewState: ReactionsMenuViewState) } -@objc protocol ReactionsMenuViewModelCoordinatorDelegate: class { +@objc protocol ReactionsMenuViewModelCoordinatorDelegate: AnyObject { func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didAddReaction reaction: String, forEventId eventId: String) func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didRemoveReaction reaction: String, forEventId eventId: String) func reactionsMenuViewModelDidTapMoreReactions(_ viewModel: ReactionsMenuViewModel, forEventId eventId: String) diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift index 32052dc06..e236a6ec1 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift @@ -16,7 +16,7 @@ import UIKit -@objc protocol RoomContextualMenuViewControllerDelegate: class { +@objc protocol RoomContextualMenuViewControllerDelegate: AnyObject { func roomContextualMenuViewControllerDidTapBackgroundOverlay(_ viewController: RoomContextualMenuViewController) } diff --git a/Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalCoordinatorType.swift b/Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalCoordinatorType.swift index 02ff0e62e..0629043b0 100644 --- a/Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalCoordinatorType.swift +++ b/Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol RoomCreationEventsModalCoordinatorDelegate: class { +protocol RoomCreationEventsModalCoordinatorDelegate: AnyObject { func roomCreationEventsModalCoordinatorDidTapClose(_ coordinator: RoomCreationEventsModalCoordinatorType) } diff --git a/Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalViewModelType.swift b/Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalViewModelType.swift index 6a670c1f5..5378da659 100644 --- a/Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalViewModelType.swift +++ b/Riot/Modules/Room/CreationModal/RoomCreationEventsModal/RoomCreationEventsModalViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol RoomCreationEventsModalViewModelViewDelegate: class { +protocol RoomCreationEventsModalViewModelViewDelegate: AnyObject { func roomCreationEventsModalViewModel(_ viewModel: RoomCreationEventsModalViewModelType, didUpdateViewState viewSate: RoomCreationEventsModalViewState) } -protocol RoomCreationEventsModalViewModelCoordinatorDelegate: class { +protocol RoomCreationEventsModalViewModelCoordinatorDelegate: AnyObject { func roomCreationEventsModalViewModelDidTapClose(_ viewModel: RoomCreationEventsModalViewModelType) } diff --git a/Riot/Modules/Room/CreationModal/RoomCreationModalCoordinatorType.swift b/Riot/Modules/Room/CreationModal/RoomCreationModalCoordinatorType.swift index 11f3a1c21..f1830a0c1 100644 --- a/Riot/Modules/Room/CreationModal/RoomCreationModalCoordinatorType.swift +++ b/Riot/Modules/Room/CreationModal/RoomCreationModalCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol RoomCreationModalCoordinatorDelegate: class { +protocol RoomCreationModalCoordinatorDelegate: AnyObject { func roomCreationModalCoordinatorDidComplete(_ coordinator: RoomCreationModalCoordinatorType) } diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 831f96634..434aee6ac 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -887,8 +887,10 @@ const CGFloat kTypingCellHeight = 24; success:(void (^)(NSString *eventId))success failure:(void (^)(NSError *error))failure { + AVURLAsset *videoAsset = [AVURLAsset assetWithURL:videoLocalURL]; UIImage *videoThumbnail = [MXKVideoThumbnailGenerator.shared generateThumbnailFrom:videoLocalURL]; - [self sendVideo:videoLocalURL withThumbnail:videoThumbnail success:success failure:failure]; + + [self sendVideoAsset:videoAsset withThumbnail:videoThumbnail success:success failure:failure]; } - (void)acceptVerificationRequestForEventId:(NSString*)eventId success:(void(^)(void))success failure:(void(^)(NSError*))failure @@ -1037,12 +1039,10 @@ const CGFloat kTypingCellHeight = 24; - (void)applyMaskToAttachmentViewOfBubbleCell:(MXKRoomBubbleTableViewCell *)cell { - if (cell.attachmentView && !cell.attachmentView.layer.mask) + if (cell.attachmentView) { - UIBezierPath *myClippingPath = [UIBezierPath bezierPathWithRoundedRect:cell.attachmentView.bounds cornerRadius:6]; - CAShapeLayer *mask = [CAShapeLayer layer]; - mask.path = myClippingPath.CGPath; - cell.attachmentView.layer.mask = mask; + cell.attachmentView.layer.cornerRadius = 6; + cell.attachmentView.layer.masksToBounds = YES; } } diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorType.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorType.swift index 2f02bd705..61788b24b 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorType.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol EditHistoryCoordinatorDelegate: class { +protocol EditHistoryCoordinatorDelegate: AnyObject { func editHistoryCoordinatorDidComplete(_ coordinator: EditHistoryCoordinatorType) } diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift index 4ed9ff707..25d73396d 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol EditHistoryViewModelViewDelegate: class { +protocol EditHistoryViewModelViewDelegate: AnyObject { func editHistoryViewModel(_ viewModel: EditHistoryViewModelType, didUpdateViewState viewSate: EditHistoryViewState) } -protocol EditHistoryViewModelCoordinatorDelegate: class { +protocol EditHistoryViewModelCoordinatorDelegate: AnyObject { func editHistoryViewModelDidClose(_ viewModel: EditHistoryViewModelType) } diff --git a/Riot/Modules/Room/EmojiPicker/EmojiPickerCoordinatorType.swift b/Riot/Modules/Room/EmojiPicker/EmojiPickerCoordinatorType.swift index e414ea29b..5d99c6118 100644 --- a/Riot/Modules/Room/EmojiPicker/EmojiPickerCoordinatorType.swift +++ b/Riot/Modules/Room/EmojiPicker/EmojiPickerCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol EmojiPickerCoordinatorDelegate: class { +protocol EmojiPickerCoordinatorDelegate: AnyObject { func emojiPickerCoordinator(_ coordinator: EmojiPickerCoordinatorType, didAddEmoji emoji: String, forEventId eventId: String) func emojiPickerCoordinator(_ coordinator: EmojiPickerCoordinatorType, didRemoveEmoji emoji: String, forEventId eventId: String) func emojiPickerCoordinatorDidCancel(_ coordinator: EmojiPickerCoordinatorType) diff --git a/Riot/Modules/Room/EmojiPicker/EmojiPickerViewModelType.swift b/Riot/Modules/Room/EmojiPicker/EmojiPickerViewModelType.swift index 3f15484f1..ba9b44222 100644 --- a/Riot/Modules/Room/EmojiPicker/EmojiPickerViewModelType.swift +++ b/Riot/Modules/Room/EmojiPicker/EmojiPickerViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol EmojiPickerViewModelViewDelegate: class { +protocol EmojiPickerViewModelViewDelegate: AnyObject { func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didUpdateViewState viewSate: EmojiPickerViewState) } -protocol EmojiPickerViewModelCoordinatorDelegate: class { +protocol EmojiPickerViewModelCoordinatorDelegate: AnyObject { func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didAddEmoji emoji: String, forEventId eventId: String) func emojiPickerViewModel(_ viewModel: EmojiPickerViewModelType, didRemoveEmoji emoji: String, forEventId eventId: String) func emojiPickerViewModelDidCancel(_ viewModel: EmojiPickerViewModelType) diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsAvatarView.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsAvatarView.swift new file mode 100644 index 000000000..c9bf9af89 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsAvatarView.swift @@ -0,0 +1,51 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation +import Reusable + +class RoomNotificationSettingsAvatarView: UIView { + + @IBOutlet weak var avatarView: MXKImageView! + @IBOutlet weak var nameLabel: UILabel! + + func configure(viewData: AvatarViewDataProtocol) { + let avatarImage = AvatarGenerator.generateAvatar(forMatrixItem: viewData.matrixItemId, withDisplayName: viewData.displayName) + + if let avatarUrl = viewData.avatarUrl { + avatarView.enableInMemoryCache = true + + avatarView.setImageURI(avatarUrl, + withType: nil, + andImageOrientation: .up, + toFitViewSize: avatarView.frame.size, + with: MXThumbnailingMethodCrop, + previewImage: avatarImage, + mediaManager: viewData.mediaManager) + } else { + avatarView.image = avatarImage + } + nameLabel.text = viewData.displayName + } +} + +extension RoomNotificationSettingsAvatarView: NibLoadable { } +extension RoomNotificationSettingsAvatarView: Themable { + func update(theme: Theme) { + nameLabel?.font = theme.fonts.title3SB + nameLabel?.textColor = theme.textPrimaryColor + } +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsAvatarView.xib b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsAvatarView.xib new file mode 100644 index 000000000..b9c21f892 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsAvatarView.xib @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsBridgePresenter.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsBridgePresenter.swift new file mode 100644 index 000000000..0be2f18a1 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsBridgePresenter.swift @@ -0,0 +1,100 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +@objc protocol RoomNotificationSettingsCoordinatorBridgePresenterDelegate { + func roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: RoomNotificationSettingsCoordinatorBridgePresenter) +} + +/// RoomNotificationSettingsCoordinatorBridgePresenter enables to start RoomNotificationSettingsCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +/// It breaks the Coordinator abstraction and it has been introduced for Objective-C compatibility (mainly for integration in legacy view controllers). +/// Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator. +@objcMembers +final class RoomNotificationSettingsCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let room: MXRoom + private var coordinator: RoomNotificationSettingsCoordinator? + + // MARK: Public + + weak var delegate: RoomNotificationSettingsCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(room: MXRoom) { + self.room = room + super.init() + } + + // MARK: - Public + + // NOTE: Default value feature is not compatible with Objective-C. + // func present(from viewController: UIViewController, animated: Bool) { + // self.present(from: viewController, animated: animated) + // } + + func present(from viewController: UIViewController, animated: Bool) { + let roomNotificationSettingsCoordinator = RoomNotificationSettingsCoordinator(room: room) + roomNotificationSettingsCoordinator.delegate = self + let presentable = roomNotificationSettingsCoordinator.toPresentable() + let navigationController = RiotNavigationController(rootViewController: presentable) + navigationController.modalPresentationStyle = .formSheet + presentable.presentationController?.delegate = self + viewController.present(navigationController, animated: animated, completion: nil) + roomNotificationSettingsCoordinator.start() + + self.coordinator = roomNotificationSettingsCoordinator + } + + func dismiss(animated: Bool, completion: (() -> Void)?) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + + if let completion = completion { + completion() + } + } + } +} + +// MARK: - RoomNotificationSettingsCoordinatorDelegate +extension RoomNotificationSettingsCoordinatorBridgePresenter: RoomNotificationSettingsCoordinatorDelegate { + func roomNotificationSettingsCoordinatorDidCancel(_ coordinator: RoomNotificationSettingsCoordinatorType) { + self.delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self) + } + + func roomNotificationSettingsCoordinatorDidComplete(_ coordinator: RoomNotificationSettingsCoordinatorType) { + self.delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self) + } +} + +// MARK: - UIAdaptivePresentationControllerDelegate + +extension RoomNotificationSettingsCoordinatorBridgePresenter: UIAdaptivePresentationControllerDelegate { + + func roomNotificationSettingsCoordinatorDidComplete(_ presentationController: UIPresentationController) { + self.delegate?.roomNotificationSettingsCoordinatorBridgePresenterDelegateDidComplete(self) + } + +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCellViewData.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCellViewData.swift new file mode 100644 index 000000000..2b50835d1 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCellViewData.swift @@ -0,0 +1,22 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +struct RoomNotificationSettingsCellViewData { + let notificicationState: RoomNotificationState + let selected: Bool +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCoordinator.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCoordinator.swift new file mode 100644 index 000000000..2d8e48cee --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCoordinator.swift @@ -0,0 +1,75 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/NotificationSettings RoomNotificationSettings +/* + Copyright 2021 New Vector Ltd + + 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 Foundation +import UIKit + +final class RoomNotificationSettingsCoordinator: RoomNotificationSettingsCoordinatorType { + + // MARK: - Properties + + // MARK: Private + private var roomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType + private let roomNotificationSettingsViewController: RoomNotificationSettingsViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: RoomNotificationSettingsCoordinatorDelegate? + + // MARK: - Setup + + init(room: MXRoom, showAvatar: Bool = true) { + let repository = RoomNotificationSettingsService(room: room) + + let avatarData = showAvatar ? RoomAvatarViewData( + roomId: room.roomId, + displayName: room.summary.displayname, + avatarUrl: room.summary.avatar, + mediaManager: room.mxSession.mediaManager + ) : nil + let roomNotificationSettingsViewModel = RoomNotificationSettingsViewModel(roomNotificationService: repository, roomEncrypted: room.summary.isEncrypted, avatarViewData: avatarData) + let roomNotificationSettingsViewController = RoomNotificationSettingsViewController.instantiate(with: roomNotificationSettingsViewModel) + self.roomNotificationSettingsViewModel = roomNotificationSettingsViewModel + self.roomNotificationSettingsViewController = roomNotificationSettingsViewController + } + + // MARK: - Public methods + + func start() { + self.roomNotificationSettingsViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.roomNotificationSettingsViewController + } +} + +// MARK: - RoomNotificationSettingsViewModelCoordinatorDelegate +extension RoomNotificationSettingsCoordinator: RoomNotificationSettingsViewModelCoordinatorDelegate { + + func roomNotificationSettingsViewModelDidComplete(_ viewModel: RoomNotificationSettingsViewModelType) { + self.delegate?.roomNotificationSettingsCoordinatorDidComplete(self) + } + + func roomNotificationSettingsViewModelDidCancel(_ viewModel: RoomNotificationSettingsViewModelType) { + self.delegate?.roomNotificationSettingsCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCoordinatorType.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCoordinatorType.swift new file mode 100644 index 000000000..ac0c6288b --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/NotificationSettings RoomNotificationSettings +/* + Copyright 2021 New Vector Ltd + + 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 Foundation + +protocol RoomNotificationSettingsCoordinatorDelegate: AnyObject { + func roomNotificationSettingsCoordinatorDidComplete(_ coordinator: RoomNotificationSettingsCoordinatorType) + func roomNotificationSettingsCoordinatorDidCancel(_ coordinator: RoomNotificationSettingsCoordinatorType) +} + +/// `RoomNotificationSettingsCoordinatorType` is a protocol describing a Coordinator that handles changes to the room navigation settings navigation flow. +protocol RoomNotificationSettingsCoordinatorType: Coordinator, Presentable { + var delegate: RoomNotificationSettingsCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsFooter.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsFooter.swift new file mode 100644 index 000000000..db5285192 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsFooter.swift @@ -0,0 +1,60 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 UIKit +import Reusable + +class RoomNotificationSettingsFooter: UITableViewHeaderFooterView { + + struct State { + let showEncryptedNotice: Bool + let showAccountLink: Bool + } + + @IBOutlet weak var label: UILabel! + + func update(footerState: State) { + + // Don't include link until global settings in place +// let paragraphStyle = NSMutableParagraphStyle() +// paragraphStyle.lineHeightMultiple = 1.16 +// let paragraphAttributes: [NSAttributedString.Key: Any] = [ +// NSAttributedString.Key.kern: -0.08, +// NSAttributedString.Key.paragraphStyle: paragraphStyle, +// NSAttributedString.Key.font: UIFont.systemFont(ofSize: 13.0) +// ] +// let linkStr = VectorL10n.roomNotifsSettingsAccountSettings +// let formatStr = VectorL10n.roomNotifsSettingsManageNotifications(linkStr) +// +// let formattedStr = String(format: formatStr, arguments: [linkStr]) +// let footer0 = NSMutableAttributedString(string: formattedStr, attributes: paragraphAttributes) +// let linkRange = (footer0.string as NSString).range(of: linkStr) +// footer0.addAttribute(NSAttributedString.Key.link, value: Constants.linkToAccountSettings, range: linkRange) + + label.text = footerState.showEncryptedNotice ? VectorL10n.roomNotifsSettingsEncryptedRoomNotice : nil + + } +} + + +extension RoomNotificationSettingsFooter: NibReusable {} +extension RoomNotificationSettingsFooter: Themable { + + func update(theme: Theme) { + contentView.backgroundColor = theme.headerBackgroundColor + label.textColor = theme.headerTextSecondaryColor + } +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsFooter.xib b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsFooter.xib new file mode 100644 index 000000000..48895dca9 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsFooter.xib @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsSerivceType.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsSerivceType.swift new file mode 100644 index 000000000..22961ad03 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsSerivceType.swift @@ -0,0 +1,27 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +typealias UpdateRoomNotificationStateCompletion = () -> Void +typealias RoomNotificationStateCallback = (RoomNotificationState) -> Void + +protocol RoomNotificationSettingsServiceType { + + func observeNotificationState(listener: @escaping RoomNotificationStateCallback) + func update(state: RoomNotificationState, completion: @escaping UpdateRoomNotificationStateCompletion) + var notificationState: RoomNotificationState { get } +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewAction.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewAction.swift new file mode 100644 index 000000000..5d42f64fa --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewAction.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/NotificationSettings RoomNotificationSettings +/* + Copyright 2021 New Vector Ltd + + 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 Foundation +/// RoomNotificationSettingsViewController view actions exposed to view model +enum RoomNotificationSettingsViewAction { + case load + case selectNotificationState(RoomNotificationState) + case save + case cancel +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewController.storyboard b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewController.storyboard new file mode 100644 index 000000000..8d49ce878 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewController.storyboard @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewController.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewController.swift new file mode 100644 index 000000000..3e2adf89e --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewController.swift @@ -0,0 +1,233 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/NotificationSettings RoomNotificationSettings +/* + Copyright 2021 New Vector Ltd + + 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 UIKit + +final class RoomNotificationSettingsViewController: UIViewController { + + // MARK: - Properties + + private enum Constants { + static let linkToAccountSettings = "linkToAccountSettings" + } + + // MARK: Outlets + + @IBOutlet private weak var mainTableView: UITableView! + + // MARK: Private + + private var viewModel: RoomNotificationSettingsViewModelType! + private var theme: Theme! + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + private lazy var avatarView: RoomNotificationSettingsAvatarView = { + RoomNotificationSettingsAvatarView.loadFromNib() + }() + + private struct Row { + var cellViewData: RoomNotificationSettingsCellViewData + var action: (() -> Void)? + } + + private struct Section { + var title: String + var rows: [Row] + var footerState: RoomNotificationSettingsFooter.State + } + + private var sections: [Section] = [] { + didSet { + mainTableView.reloadData() + } + } + + private var viewState: RoomNotificationSettingsViewStateType! + + // MARK: - Setup + + class func instantiate(with viewModel: RoomNotificationSettingsViewModelType) -> RoomNotificationSettingsViewController { + let viewController = StoryboardScene.RoomNotificationSettingsViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + setupViews() + activityPresenter = ActivityIndicatorPresenter() + errorPresenter = MXKErrorAlertPresentation() + + registerThemeServiceDidChangeThemeNotification() + update(theme: theme) + + viewModel.viewDelegate = self + viewModel.process(viewAction: .load) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + view.backgroundColor = theme.headerBackgroundColor + mainTableView.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + mainTableView.reloadData() + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + + self.title = VectorL10n.roomDetailsNotifs + let doneBarButtonItem = MXKBarButtonItem(title: VectorL10n.roomNotifsSettingsDoneAction, style: .plain) { [weak self] in + self?.viewModel.process(viewAction: .save) + } + + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.roomNotifsSettingsCancelAction, style: .plain) { [weak self] in + self?.viewModel.process(viewAction: .cancel) + } + + if navigationController?.navigationBar.backItem == nil { + navigationItem.leftBarButtonItem = cancelBarButtonItem + } + navigationItem.rightBarButtonItem = doneBarButtonItem + mainTableView.register(cellType: RoomNotificationSettingsCell.self) + mainTableView.register(headerFooterViewType: RoomNotificationSettingsFooter.self) + mainTableView.register(headerFooterViewType: TitleHeaderView.self) + mainTableView.sectionFooterHeight = UITableView.automaticDimension + mainTableView.sectionHeaderHeight = UITableView.automaticDimension + mainTableView.estimatedSectionFooterHeight = 50 + mainTableView.estimatedSectionHeaderHeight = 30 + } + + private func render(viewState: RoomNotificationSettingsViewStateType) { + + if viewState.saving { + activityPresenter.presentActivityIndicator(on: view, animated: true) + } else { + activityPresenter.removeCurrentActivityIndicator(animated: true) + } + self.viewState = viewState + if let avatarData = viewState.avatarData { + mainTableView.tableHeaderView = avatarView + avatarView.configure(viewData: avatarData) + avatarView.update(theme: theme) + } + updateSections() + } + + private func updateSections() { + let rows = viewState.notificationOptions.map({ (setting) -> Row in + let cellViewData = RoomNotificationSettingsCellViewData(notificicationState: setting, selected: viewState.notificationState == setting) + return Row(cellViewData: cellViewData, + action: { + self.viewModel.process(viewAction: .selectNotificationState(setting)) + }) + }) + let footerState = RoomNotificationSettingsFooter.State(showEncryptedNotice: viewState.roomEncrypted, showAccountLink: false) + let section0 = Section(title: VectorL10n.roomNotifsSettingsNotifyMeFor, rows: rows, footerState: footerState) + sections = [ + section0 + ] + } +} + +// MARK: - UITableViewDataSource +extension RoomNotificationSettingsViewController: UITableViewDataSource { + + func numberOfSections(in tableView: UITableView) -> Int { + return sections.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sections[section].rows.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let row = sections[indexPath.section].rows[indexPath.row] + let cell: RoomNotificationSettingsCell = tableView.dequeueReusableCell(for: indexPath) + cell.update(state: row.cellViewData) + cell.update(theme: theme) + return cell + } + + func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + return UITableView.automaticDimension + } + + func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + return UITableView.automaticDimension + } + +} + +// MARK: - UITableViewDelegate +extension RoomNotificationSettingsViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let headerView: TitleHeaderView = tableView.dequeueReusableHeaderFooterView() else { return nil } + headerView.update(title: sections[section].title) + headerView.update(theme: theme) + return headerView + } + + func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + guard let footerView: RoomNotificationSettingsFooter = tableView.dequeueReusableHeaderFooterView() else { return nil } + let footerState = sections[section].footerState + footerView.update(footerState: footerState) + footerView.update(theme: theme) + return footerView + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + + let row = sections[indexPath.section].rows[indexPath.row] + row.action?() + } + +} + +// MARK: - RoomNotificationSettingsViewModelViewDelegate +extension RoomNotificationSettingsViewController: RoomNotificationSettingsViewModelViewDelegate { + + func roomNotificationSettingsViewModel(_ viewModel: RoomNotificationSettingsViewModelType, didUpdateViewState viewSate: RoomNotificationSettingsViewStateType) { + render(viewState: viewSate) + } +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewModel.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewModel.swift new file mode 100644 index 000000000..e9f5729df --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewModel.swift @@ -0,0 +1,89 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/NotificationSettings RoomNotificationSettings +/* + Copyright 2021 New Vector Ltd + + 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 Foundation + +final class RoomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let roomNotificationService: RoomNotificationSettingsServiceType + private var state: RoomNotificationSettingsViewState { + willSet { + update(viewState: newValue) + } + } + // MARK: Public + + weak var viewDelegate: RoomNotificationSettingsViewModelViewDelegate? + + weak var coordinatorDelegate: RoomNotificationSettingsViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(roomNotificationService: RoomNotificationSettingsServiceType, roomEncrypted: Bool, avatarViewData: AvatarViewDataProtocol?) { + self.roomNotificationService = roomNotificationService + + let notificationState = Self.mapNotificationStateOnRead(encrypted: roomEncrypted, state: roomNotificationService.notificationState) + self.state = RoomNotificationSettingsViewState(roomEncrypted: roomEncrypted, saving: false, notificationState: notificationState, avatarData: avatarViewData) + self.roomNotificationService.observeNotificationState { [weak self] state in + guard let self = self else { return } + + self.state.notificationState = Self.mapNotificationStateOnRead(encrypted: roomEncrypted, state: state) + } + } + + // MARK: - Public + + func process(viewAction: RoomNotificationSettingsViewAction) { + switch viewAction { + case .load: + update(viewState: self.state) + case .selectNotificationState(let state): + self.state.notificationState = state + case .save: + self.state.saving = true + roomNotificationService.update(state: state.notificationState) { [weak self] in + guard let self = self else { return } + self.state.saving = false + self.coordinatorDelegate?.roomNotificationSettingsViewModelDidComplete(self) + } + case .cancel: + coordinatorDelegate?.roomNotificationSettingsViewModelDidCancel(self) + } + } + + // MARK: - Private + + private static func mapNotificationStateOnRead(encrypted: Bool, state: RoomNotificationState) -> RoomNotificationState { + if encrypted, case .mentionsAndKeywordsOnly = state { + // Notifications not supported on encrypted rooms, map mentionsOnly to mute on read + return .mute + } else { + return state + } + } + + private func update(viewState: RoomNotificationSettingsViewStateType) { + self.viewDelegate?.roomNotificationSettingsViewModel(self, didUpdateViewState: viewState) + } + + +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewModelType.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewModelType.swift new file mode 100644 index 000000000..e2fe135d4 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewModelType.swift @@ -0,0 +1,37 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/NotificationSettings RoomNotificationSettings +/* + Copyright 2021 New Vector Ltd + + 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 Foundation + +protocol RoomNotificationSettingsViewModelViewDelegate: AnyObject { + func roomNotificationSettingsViewModel(_ viewModel: RoomNotificationSettingsViewModelType, didUpdateViewState viewState: RoomNotificationSettingsViewStateType) +} + +protocol RoomNotificationSettingsViewModelCoordinatorDelegate: AnyObject { + func roomNotificationSettingsViewModelDidComplete(_ viewModel: RoomNotificationSettingsViewModelType) + func roomNotificationSettingsViewModelDidCancel(_ viewModel: RoomNotificationSettingsViewModelType) +} + +/// Protocol describing the view model used by `RoomNotificationSettingsViewController` +protocol RoomNotificationSettingsViewModelType { + + var viewDelegate: RoomNotificationSettingsViewModelViewDelegate? { get set } + var coordinatorDelegate: RoomNotificationSettingsViewModelCoordinatorDelegate? { get set } + + func process(viewAction: RoomNotificationSettingsViewAction) +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewStateType.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewStateType.swift new file mode 100644 index 000000000..56de53b13 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationSettingsViewStateType.swift @@ -0,0 +1,42 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/NotificationSettings RoomNotificationSettings +/* + Copyright 2021 New Vector Ltd + + 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 Foundation + +/// RoomNotificationSettingsViewController view state +struct RoomNotificationSettingsViewState: RoomNotificationSettingsViewStateType { + let roomEncrypted: Bool + var saving: Bool + var notificationState: RoomNotificationState + var notificationOptions: [RoomNotificationState] { + if roomEncrypted { + return [.all, .mute] + } else { + return RoomNotificationState.allCases + } + } + let avatarData: AvatarViewDataProtocol? +} + +protocol RoomNotificationSettingsViewStateType { + var saving: Bool { get } + var roomEncrypted: Bool { get } + var notificationOptions: [RoomNotificationState] { get } + var notificationState: RoomNotificationState { get } + var avatarData: AvatarViewDataProtocol? { get } +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationState.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationState.swift new file mode 100644 index 000000000..054993117 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationState.swift @@ -0,0 +1,23 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +enum RoomNotificationState: CaseIterable { + case all + case mentionsAndKeywordsOnly + case mute +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationsSettingsCell.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationsSettingsCell.swift new file mode 100644 index 000000000..343c7bba4 --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationsSettingsCell.swift @@ -0,0 +1,56 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 UIKit +import Reusable + +class RoomNotificationSettingsCell: UITableViewCell { + + func update(state: RoomNotificationSettingsCellViewData) { + textLabel?.text = state.notificicationState.title + if state.selected { + accessoryView = UIImageView(image: Asset.Images.checkmark.image) + } else { + accessoryView = nil + } + } +} + +extension RoomNotificationSettingsCell: Reusable {} + +extension RoomNotificationSettingsCell: Themable { + func update(theme: Theme) { + textLabel?.font = theme.fonts.body + textLabel?.textColor = theme.textPrimaryColor + backgroundColor = theme.backgroundColor + contentView.backgroundColor = .clear + tintColor = theme.tintColor + selectedBackgroundView = UIView() + selectedBackgroundView?.backgroundColor = theme.selectedBackgroundColor + } +} + +fileprivate extension RoomNotificationState { + var title: String { + switch self { + case .all: + return VectorL10n.roomNotifsSettingsAllMessages + case .mentionsAndKeywordsOnly: + return VectorL10n.roomNotifsSettingsMentionsAndKeywords + case .mute: + return VectorL10n.roomNotifsSettingsNone + } + } +} diff --git a/Riot/Modules/Room/NotificationSettings/RoomNotificationsSettingsService.swift b/Riot/Modules/Room/NotificationSettings/RoomNotificationsSettingsService.swift new file mode 100644 index 000000000..1bd0e4b6b --- /dev/null +++ b/Riot/Modules/Room/NotificationSettings/RoomNotificationsSettingsService.swift @@ -0,0 +1,325 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +final class RoomNotificationSettingsService: RoomNotificationSettingsServiceType { + + typealias Completion = () -> Void + + // MARK: - Properties + + // MARK: Private + + private let room: MXRoom + + private var notificationCenterDidUpdateObserver: NSObjectProtocol? + private var notificationCenterDidFailObserver: NSObjectProtocol? + + private var observers: [ObjectIdentifier] = [] + + // MARK: Public + + var notificationState: RoomNotificationState { + room.notificationState + } + + // MARK: - Setup + + init(room: MXRoom) { + self.room = room + } + + deinit { + observers.forEach(NotificationCenter.default.removeObserver) + } + + // MARK: - Public + + func observeNotificationState(listener: @escaping RoomNotificationStateCallback) { + + let observer = NotificationCenter.default.addObserver( + forName: NSNotification.Name(rawValue: kMXNotificationCenterDidUpdateRules), + object: nil, + queue: OperationQueue.main) { [weak self] _ in + guard let self = self else { return } + listener(self.room.notificationState) + } + observers += [ObjectIdentifier(observer)] + } + + func update(state: RoomNotificationState, completion: @escaping Completion) { + switch state { + case .all: + allMessages(completion: completion) + case .mentionsAndKeywordsOnly: + mentionsOnly(completion: completion) + case .mute: + mute(completion: completion) + } + } + + // MARK: - Private + + private func mute(completion: @escaping Completion) { + guard !room.isMuted else { + completion() + return + } + + if let rule = room.roomPushRule { + removePushRule(rule: rule) { + self.mute(completion: completion) + } + return + } + + guard let rule = room.overridePushRule else { + self.addPushRuleToMute(completion: completion) + return + } + + guard notificationCenterDidUpdateObserver == nil else { + MXLog.debug("[RoomNotificationSettingsService] Request in progress: ignore push rule update") + completion() + return + } + + // if the user defined one, use it + if rule.actionsContains(actionType: MXPushRuleActionTypeDontNotify) { + enablePushRule(rule: rule, completion: completion) + } else { + removePushRule(rule: rule) { + self.addPushRuleToMute(completion: completion) + } + } + } + + private func mentionsOnly(completion: @escaping Completion) { + guard !room.isMentionsOnly else { + completion() + return + } + + if let rule = room.overridePushRule, room.isMuted { + removePushRule(rule: rule) { + self.mentionsOnly(completion: completion) + } + return + } + + guard let rule = room.roomPushRule else { + addPushRuleToMentionOnly(completion: completion) + return + } + + guard notificationCenterDidUpdateObserver == nil else { + MXLog.debug("[MXRoom+Riot] Request in progress: ignore push rule update") + completion() + return + } + + // if the user defined one, use it + if rule.actionsContains(actionType: MXPushRuleActionTypeDontNotify) { + enablePushRule(rule: rule, completion: completion) + } else { + removePushRule(rule: rule) { + self.addPushRuleToMentionOnly(completion: completion) + } + } + + } + + private func allMessages(completion: @escaping Completion) { + if !room.isMentionsOnly && !room.isMuted { + completion() + return + } + + if let rule = room.overridePushRule, room.isMuted { + removePushRule(rule: rule) { + self.allMessages(completion: completion) + } + return + } + + if let rule = room.roomPushRule, room.isMentionsOnly { + removePushRule(rule: rule, completion: completion) + } + } + + private func addPushRuleToMentionOnly(completion: @escaping Completion) { + handleUpdateCallback(completion) { [weak self] in + guard let self = self else { return true } + return self.room.roomPushRule != nil + } + handleFailureCallback(completion) + + room.mxSession.notificationCenter.addRoomRule( + room.roomId, + notify: false, + sound: false, + highlight: false) + } + + private func addPushRuleToMute(completion: @escaping Completion) { + guard let roomId = room.roomId else { + return + } + handleUpdateCallback(completion) { [weak self] in + guard let self = self else { return true } + return self.room.overridePushRule != nil + } + handleFailureCallback(completion) + + room.mxSession.notificationCenter.addOverrideRule( + withId: roomId, + conditions: [["kind": "event_match", "key": "room_id", "pattern": roomId]], + notify: false, + sound: false, + highlight: false + ) + } + + private func removePushRule(rule: MXPushRule, completion: @escaping Completion) { + handleUpdateCallback(completion) { [weak self] in + guard let self = self else { return true } + return self.room.mxSession.notificationCenter.rule(byId: rule.ruleId) == nil + } + handleFailureCallback(completion) + + room.mxSession.notificationCenter.removeRule(rule) + } + + private func enablePushRule(rule: MXPushRule, completion: @escaping Completion) { + handleUpdateCallback(completion) { + // No way to check whether this notification concerns the push rule. Consider the change is applied. + return true + } + handleFailureCallback(completion) + + room.mxSession.notificationCenter.enableRule(rule, isEnabled: true) + } + + private func handleUpdateCallback(_ completion: @escaping Completion, releaseCheck: @escaping () -> Bool) { + notificationCenterDidUpdateObserver = NotificationCenter.default.addObserver( + forName: NSNotification.Name(rawValue: kMXNotificationCenterDidUpdateRules), + object: nil, + queue: OperationQueue.main) { [weak self] _ in + guard let self = self else { return } + if releaseCheck() { + self.removeObservers() + completion() + } + } + } + + private func handleFailureCallback(_ completion: @escaping Completion) { + notificationCenterDidFailObserver = NotificationCenter.default.addObserver( + forName: NSNotification.Name(rawValue: kMXNotificationCenterDidFailRulesUpdate), + object: nil, + queue: OperationQueue.main) { [weak self] _ in + guard let self = self else { return } + self.removeObservers() + completion() + } + } + + func removeObservers() { + if let observer = self.notificationCenterDidUpdateObserver { + NotificationCenter.default.removeObserver(observer) + self.notificationCenterDidUpdateObserver = nil + } + + if let observer = self.notificationCenterDidFailObserver { + NotificationCenter.default.removeObserver(observer) + self.notificationCenterDidFailObserver = nil + } + } +} + +// We could move these to their own file and make available in global namespace or move to sdk but they are only used here at the moment +fileprivate extension MXRoom { + + typealias Completion = () -> Void + func getRoomRule(from rules: [Any]) -> MXPushRule? { + guard let pushRules = rules as? [MXPushRule] else { + return nil + } + + return pushRules.first(where: { self.roomId == $0.ruleId }) + } + + var overridePushRule: MXPushRule? { + getRoomRule(from: mxSession.notificationCenter.rules.global.override) + } + + var roomPushRule: MXPushRule? { + getRoomRule(from: mxSession.notificationCenter.rules.global.room) + } + + var notificationState: RoomNotificationState { + + if isMuted { + return .mute + } + if isMentionsOnly { + return .mentionsAndKeywordsOnly + } + return .all + } + + var isMuted: Bool { + // Check whether an override rule has been defined with the roomm id as rule id. + // This kind of rule is created to mute the room + guard let rule = self.overridePushRule, + rule.actionsContains(actionType: MXPushRuleActionTypeDontNotify), + rule.conditionIsEnabled(kind: .eventMatch, for: roomId) else { + return false + } + return rule.enabled + } + + var isMentionsOnly: Bool { + // Check push rules at room level + guard let rule = roomPushRule else { return false } + return rule.enabled && rule.actionsContains(actionType: MXPushRuleActionTypeDontNotify) + } + +} + +fileprivate extension MXPushRule { + func actionsContains(actionType: MXPushRuleActionType) -> Bool { + guard let actions = actions as? [MXPushRuleAction] else { + return false + } + return actions.contains(where: { $0.actionType == actionType }) + } + + func conditionIsEnabled(kind: MXPushRuleConditionType, for roomId: String) -> Bool { + guard let conditions = conditions as? [MXPushRuleCondition] else { + return false + } + let ruleContainsCondition = conditions.contains { condition in + guard case kind = MXPushRuleConditionType(identifier: condition.kind), + let key = condition.parameters["key"] as? String, + let pattern = condition.parameters["pattern"] as? String + else { return false } + return key == "room_id" && pattern == roomId + } + return ruleContainsCondition && enabled + } +} diff --git a/Riot/Modules/Room/ReactionHistory/ReactionHistoryCoordinatorType.swift b/Riot/Modules/Room/ReactionHistory/ReactionHistoryCoordinatorType.swift index 420dc1fe2..95a5bfbe8 100644 --- a/Riot/Modules/Room/ReactionHistory/ReactionHistoryCoordinatorType.swift +++ b/Riot/Modules/Room/ReactionHistory/ReactionHistoryCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol ReactionHistoryCoordinatorDelegate: class { +protocol ReactionHistoryCoordinatorDelegate: AnyObject { func reactionHistoryCoordinatorDidClose(_ coordinator: ReactionHistoryCoordinatorType) } diff --git a/Riot/Modules/Room/ReactionHistory/ReactionHistoryViewModelType.swift b/Riot/Modules/Room/ReactionHistory/ReactionHistoryViewModelType.swift index 92c300d46..735ee7b16 100644 --- a/Riot/Modules/Room/ReactionHistory/ReactionHistoryViewModelType.swift +++ b/Riot/Modules/Room/ReactionHistory/ReactionHistoryViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol ReactionHistoryViewModelViewDelegate: class { +protocol ReactionHistoryViewModelViewDelegate: AnyObject { func reactionHistoryViewModel(_ viewModel: ReactionHistoryViewModelType, didUpdateViewState viewSate: ReactionHistoryViewState) } -protocol ReactionHistoryViewModelCoordinatorDelegate: class { +protocol ReactionHistoryViewModelCoordinatorDelegate: AnyObject { func reactionHistoryViewModelDidClose(_ viewModel: ReactionHistoryViewModelType) } diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift index f1efb7577..569803fbf 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinator.swift @@ -137,6 +137,12 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType { return coordinator } + private func createRoomNotificationSettingsCoordinator() -> RoomNotificationSettingsCoordinator { + let coordinator = RoomNotificationSettingsCoordinator(room: room, showAvatar: false) + coordinator.delegate = self + return coordinator + } + private func showRoomDetails(with target: RoomInfoListTarget, animated: Bool) { switch target { case .integrations: @@ -152,8 +158,16 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType { self.navigationRouter.push(search, animated: animated, popCompletion: nil) } }) + case .notifications: + let coordinator = createRoomNotificationSettingsCoordinator() + coordinator.start() + self.add(childCoordinator: coordinator) + self.navigationRouter.push(coordinator, animated: true, popCompletion: nil) default: - segmentedViewController.selectedIndex = target.tabIndex + guard let tabIndex = target.tabIndex else { + fatalError("No settings tab index for this target.") + } + segmentedViewController.selectedIndex = tabIndex if case .settings(let roomSettingsField) = target { roomSettingsViewController?.selectedRoomSettingsField = roomSettingsField @@ -180,6 +194,18 @@ extension RoomInfoCoordinator: RoomInfoListCoordinatorDelegate { extension RoomInfoCoordinator: RoomParticipantsViewControllerDelegate { func roomParticipantsViewController(_ roomParticipantsViewController: RoomParticipantsViewController!, mention member: MXRoomMember!) { + self.navigationRouter.popToRootModule(animated: true) + self.delegate?.roomInfoCoordinator(self, didRequestMentionForMember: member) + } + +} + +extension RoomInfoCoordinator: RoomNotificationSettingsCoordinatorDelegate { + func roomNotificationSettingsCoordinatorDidComplete(_ coordinator: RoomNotificationSettingsCoordinatorType) { + self.navigationRouter.popModule(animated: true) + } + + func roomNotificationSettingsCoordinatorDidCancel(_ coordinator: RoomNotificationSettingsCoordinatorType) { } diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorBridgePresenter.swift b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorBridgePresenter.swift index 44a46cbd0..48dc70d3e 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorBridgePresenter.swift @@ -20,6 +20,7 @@ import Foundation @objc protocol RoomInfoCoordinatorBridgePresenterDelegate { func roomInfoCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: RoomInfoCoordinatorBridgePresenter) + func roomInfoCoordinatorBridgePresenter(_ coordinatorBridgePresenter: RoomInfoCoordinatorBridgePresenter, didRequestMentionForMember member: MXRoomMember) } /// RoomInfoCoordinatorBridgePresenter enables to start RoomInfoCoordinator from a view controller. @@ -115,6 +116,10 @@ extension RoomInfoCoordinatorBridgePresenter: RoomInfoCoordinatorDelegate { self.delegate?.roomInfoCoordinatorBridgePresenterDelegateDidComplete(self) } + func roomInfoCoordinator(_ coordinator: RoomInfoCoordinatorType, didRequestMentionForMember member: MXRoomMember) { + self.delegate?.roomInfoCoordinatorBridgePresenter(self, didRequestMentionForMember: member) + } + } // MARK: - UIAdaptivePresentationControllerDelegate diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorType.swift b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorType.swift index c98c7a751..092a3b874 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorType.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoCoordinatorType.swift @@ -18,8 +18,9 @@ import Foundation -protocol RoomInfoCoordinatorDelegate: class { +protocol RoomInfoCoordinatorDelegate: AnyObject { func roomInfoCoordinatorDidComplete(_ coordinator: RoomInfoCoordinatorType) + func roomInfoCoordinator(_ coordinator: RoomInfoCoordinatorType, didRequestMentionForMember member: MXRoomMember) } /// `RoomInfoCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListCoordinatorType.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListCoordinatorType.swift index db953c40b..7979d1aa8 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListCoordinatorType.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol RoomInfoListCoordinatorDelegate: class { +protocol RoomInfoListCoordinatorDelegate: AnyObject { func roomInfoListCoordinator(_ coordinator: RoomInfoListCoordinatorType, wantsToNavigateTo target: RoomInfoListTarget) func roomInfoListCoordinatorDidCancel(_ coordinator: RoomInfoListCoordinatorType) } diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift index 945d2844a..c6e0c8887 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewAction.swift @@ -24,25 +24,21 @@ enum RoomInfoListTarget: Equatable { case uploads case integrations case search - - var tabIndex: UInt { - let tabIndex: UInt - + case notifications + + var tabIndex: UInt? { switch self { case .members: - tabIndex = 0 + return 0 case .uploads: - tabIndex = 1 + return 1 case .settings: - tabIndex = 2 - case .integrations: - tabIndex = 3 - case .search: - tabIndex = 4 + return 2 + default: + return nil } - - return tabIndex } + } /// RoomInfoListViewController view actions exposed to view model diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift index bfc3396bf..ddc14d605 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewController.swift @@ -150,6 +150,9 @@ final class RoomInfoListViewController: UIViewController { let rowSettings = Row(type: .default, icon: Asset.Images.settingsIcon.image, text: VectorL10n.roomDetailsSettings, accessoryType: .disclosureIndicator) { self.viewModel.process(viewAction: .navigate(target: .settings())) } + let roomNotifications = Row(type: .default, icon: Asset.Images.notifications.image, text: VectorL10n.roomDetailsNotifs, accessoryType: .disclosureIndicator) { + self.viewModel.process(viewAction: .navigate(target: .notifications)) + } let text = viewData.numberOfMembers == 1 ? VectorL10n.roomInfoListOneMember : VectorL10n.roomInfoListSeveralMembers(String(viewData.numberOfMembers)) let rowMembers = Row(type: .default, icon: Asset.Images.userIcon.image, text: text, accessoryType: .disclosureIndicator) { self.viewModel.process(viewAction: .navigate(target: .members)) @@ -165,8 +168,11 @@ final class RoomInfoListViewController: UIViewController { } var rows = [rowSettings] - if (RiotSettings.shared.roomInfoScreenShowIntegrations) - { + + if BuildSettings.roomSettingsScreenShowNotificationsV2 { + rows.append(roomNotifications) + } + if RiotSettings.shared.roomInfoScreenShowIntegrations { rows.append(rowIntegrations) } rows.append(rowMembers) diff --git a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModelType.swift b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModelType.swift index 439624dea..2610d89a2 100644 --- a/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModelType.swift +++ b/Riot/Modules/Room/RoomInfo/RoomInfoList/RoomInfoListViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol RoomInfoListViewModelViewDelegate: class { +protocol RoomInfoListViewModelViewDelegate: AnyObject { func roomInfoListViewModel(_ viewModel: RoomInfoListViewModelType, didUpdateViewState viewSate: RoomInfoListViewState) } -protocol RoomInfoListViewModelCoordinatorDelegate: class { +protocol RoomInfoListViewModelCoordinatorDelegate: AnyObject { func roomInfoListViewModelDidCancel(_ viewModel: RoomInfoListViewModelType) func roomInfoListViewModel(_ viewModel: RoomInfoListViewModelType, wantsToNavigateTo target: RoomInfoListTarget) } diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index fb4b7db8f..8d32e11e8 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -135,7 +135,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; @interface RoomViewController () + RoomDataSourceDelegate, RoomCreationModalCoordinatorBridgePresenterDelegate, RoomInfoCoordinatorBridgePresenterDelegate, DialpadViewControllerDelegate, RemoveJitsiWidgetViewDelegate, VoiceMessageControllerDelegate> { // The preview header @@ -240,6 +240,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; @property (nonatomic, getter=isActivitiesViewExpanded) BOOL activitiesViewExpanded; @property (nonatomic, getter=isScrollToBottomHidden) BOOL scrollToBottomHidden; +@property (nonatomic, strong) VoiceMessageController *voiceMessageController; + @end @implementation RoomViewController @@ -313,6 +315,9 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; // Show / hide actions button in document preview according BuildSettings self.allowActionsInDocumentPreview = BuildSettings.messageDetailsAllowShare; + + _voiceMessageController = [[VoiceMessageController alloc] initWithThemeService:ThemeService.shared mediaServiceProvider:VoiceMessageMediaServiceProvider.sharedProvider]; + self.voiceMessageController.delegate = self; } - (void)viewDidLoad @@ -386,6 +391,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; [self.bubblesTableView registerNib:RoomTypingBubbleCell.nib forCellReuseIdentifier:RoomTypingBubbleCell.defaultReuseIdentifier]; + [self.bubblesTableView registerClass:VoiceMessageBubbleCell.class forCellReuseIdentifier:VoiceMessageBubbleCell.defaultReuseIdentifier]; + [self.bubblesTableView registerClass:VoiceMessageWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:VoiceMessageWithoutSenderInfoBubbleCell.defaultReuseIdentifier]; + [self.bubblesTableView registerClass:VoiceMessageWithPaginationTitleBubbleCell.class forCellReuseIdentifier:VoiceMessageWithPaginationTitleBubbleCell.defaultReuseIdentifier]; + [self vc_removeBackTitle]; [self setupRemoveJitsiWidgetRemoveView]; @@ -607,6 +616,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; self.roomDataSource.showReadMarker = YES; self.updateRoomReadMarker = NO; isAppeared = NO; + + [VoiceMessageMediaServiceProvider.sharedProvider stopAllServices]; } - (void)viewDidAppear:(BOOL)animated @@ -1114,6 +1125,9 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; if (!self.inputToolbarView || ![self.inputToolbarView isMemberOfClass:roomInputToolbarViewClass]) { [super setRoomInputToolbarViewClass:roomInputToolbarViewClass]; + + [(RoomInputToolbarView *)self.inputToolbarView setVoiceMessageToolbarView:self.voiceMessageController.voiceMessageToolbarView]; + [self updateInputToolBarViewHeight]; } } @@ -2359,6 +2373,16 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; { cellViewClass = RoomGroupCallStatusBubbleCell.class; } + else if (bubbleData.attachment.type == MXKAttachmentTypeVoiceMessage) + { + if (bubbleData.isPaginationFirstBubble) { + cellViewClass = VoiceMessageWithPaginationTitleBubbleCell.class; + } else if (bubbleData.shouldHideSenderInformation) { + cellViewClass = VoiceMessageWithoutSenderInfoBubbleCell.class; + } else { + cellViewClass = VoiceMessageBubbleCell.class; + } + } else if (bubbleData.isIncoming) { if (bubbleData.isAttachmentWithThumbnail) @@ -2718,12 +2742,11 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; [actionIdentifier isEqualToString:RoomGroupCallStatusBubbleCell.answerAction]) { MXWeakify(self); - NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; // Check app permissions first [MXKTools checkAccessForCall:YES - manualChangeMessageForAudio:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"microphone_access_not_granted_for_call"], appDisplayName] - manualChangeMessageForVideo:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"camera_access_not_granted_for_call"], appDisplayName] + manualChangeMessageForAudio:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"microphone_access_not_granted_for_call"], AppInfo.current.displayName] + manualChangeMessageForVideo:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"camera_access_not_granted_for_call"], AppInfo.current.displayName] showPopUpInViewController:self completionHandler:^(BOOL granted) { MXStrongifyAndReturnIfNil(self); @@ -3715,12 +3738,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; { __weak __typeof(self) weakSelf = self; - NSString *appDisplayName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; - // Check app permissions first [MXKTools checkAccessForCall:video - manualChangeMessageForAudio:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"microphone_access_not_granted_for_call"], appDisplayName] - manualChangeMessageForVideo:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"camera_access_not_granted_for_call"], appDisplayName] + manualChangeMessageForAudio:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"microphone_access_not_granted_for_call"], AppInfo.current.displayName] + manualChangeMessageForVideo:[NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"camera_access_not_granted_for_call"], AppInfo.current.displayName] showPopUpInViewController:self completionHandler:^(BOOL granted) { if (weakSelf) @@ -3936,13 +3957,6 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; [self cancelEventSelection]; } -#pragma mark - RoomParticipantsViewControllerDelegate - -- (void)roomParticipantsViewController:(RoomParticipantsViewController *)roomParticipantsViewController mention:(MXRoomMember*)member -{ - [self mention:member]; -} - #pragma mark - MXKRoomMemberDetailsViewControllerDelegate - (void)roomMemberDetailsViewController:(MXKRoomMemberDetailsViewController *)roomMemberDetailsViewController startChatWithMemberId:(NSString *)matrixId completion:(void (^)(void))completion @@ -5810,7 +5824,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; MXWeakify(self); RoomContextualMenuItem *replyMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionReply]; - replyMenuItem.isEnabled = [self.roomDataSource canReplyToEventWithId:event.eventId]; + replyMenuItem.isEnabled = [self.roomDataSource canReplyToEventWithId:event.eventId] && !self.voiceMessageController.isRecordingAudio; replyMenuItem.action = ^{ MXStrongifyAndReturnIfNil(self); @@ -6056,7 +6070,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView]; if (roomInputToolbarView) { - [roomInputToolbarView sendSelectedVideo:url isPhotoLibraryAsset:NO]; + AVURLAsset *selectedVideo = [AVURLAsset assetWithURL:url]; + [roomInputToolbarView sendSelectedVideoAsset:selectedVideo isPhotoLibraryAsset:NO]; } } @@ -6080,7 +6095,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; } } -- (void)mediaPickerCoordinatorBridgePresenter:(MediaPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectVideoAt:(NSURL *)url +- (void)mediaPickerCoordinatorBridgePresenter:(MediaPickerCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectVideo:(AVAsset *)videoAsset { [coordinatorBridgePresenter dismissWithAnimated:YES completion:nil]; self.mediaPickerPresenter = nil; @@ -6088,7 +6103,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView]; if (roomInputToolbarView) { - [roomInputToolbarView sendSelectedVideo:url isPhotoLibraryAsset:YES]; + [roomInputToolbarView sendSelectedVideoAsset:videoAsset isPhotoLibraryAsset:YES]; } } @@ -6120,6 +6135,11 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; self.roomInfoCoordinatorBridgePresenter = nil; } +- (void)roomInfoCoordinatorBridgePresenter:(RoomInfoCoordinatorBridgePresenter *)coordinatorBridgePresenter didRequestMentionForMember:(MXRoomMember *)member +{ + [self mention:member]; +} + #pragma mark - RemoveJitsiWidgetViewDelegate - (void)removeJitsiWidgetViewDidCompleteSliding:(RemoveJitsiWidgetView *)view @@ -6153,4 +6173,32 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05; }]; } +#pragma mark - VoiceMessageControllerDelegate + +- (void)voiceMessageControllerDidRequestMicrophonePermission:(VoiceMessageController *)voiceMessageController +{ + NSString *message = [NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"microphone_access_not_granted_for_voice_message"], AppInfo.current.displayName]; + + [MXKTools checkAccessForMediaType:AVMediaTypeAudio + manualChangeMessage: message + showPopUpInViewController:self completionHandler:^(BOOL granted) { + + }]; +} + +- (void)voiceMessageController:(VoiceMessageController *)voiceMessageController + didRequestSendForFileAtURL:(NSURL *)url + duration:(NSUInteger)duration + samples:(NSArray *)samples + completion:(void (^)(BOOL))completion +{ + [self.roomDataSource sendVoiceMessage:url mimeType:nil duration:duration samples:samples success:^(NSString *eventId) { + MXLogDebug(@"Success with event id %@", eventId); + completion(YES); + } failure:^(NSError *error) { + MXLogError(@"Failed sending voice message"); + completion(NO); + }]; +} + @end diff --git a/Riot/Modules/Room/Settings/RoomSettingsViewController.m b/Riot/Modules/Room/Settings/RoomSettingsViewController.m index 634468fe8..08d18f655 100644 --- a/Riot/Modules/Room/Settings/RoomSettingsViewController.m +++ b/Riot/Modules/Room/Settings/RoomSettingsViewController.m @@ -528,7 +528,10 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti { [sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_DIRECT_CHAT]; } - [sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS]; + if (!BuildSettings.roomSettingsScreenShowNotificationsV2) + { + [sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_MUTE_NOTIFICATIONS]; + } [sectionMain addRowWithTag:ROOM_SETTINGS_MAIN_SECTION_ROW_LEAVE]; [tmpSections addObject:sectionMain]; diff --git a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.h b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.h index a97402465..31d5ccaa9 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.h +++ b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.h @@ -31,7 +31,7 @@ extern NSString *const kRoomEncryptedDataBubbleCellTapOnEncryptionIcon; /** Return the icon displayed in front of an event in an encrypted room if needed. - @param event the bubble component. + @param bubbleComponent the bubble component. */ + (UIImage*)encryptionIconForBubbleComponent:(MXKRoomBubbleComponent *)bubbleComponent; diff --git a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageBubbleCell.swift b/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageBubbleCell.swift new file mode 100644 index 000000000..397fc9fe7 --- /dev/null +++ b/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageBubbleCell.swift @@ -0,0 +1,55 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +class VoiceMessageBubbleCell: SizableBaseBubbleCell, BubbleCellReactionsDisplayable { + + private var playbackController: VoiceMessagePlaybackController! + + override func render(_ cellData: MXKCellData!) { + super.render(cellData) + + guard let data = cellData as? RoomBubbleCellData else { + return + } + + guard data.attachment.type == MXKAttachmentTypeVoiceMessage else { + fatalError("Invalid attachment type passed to a voice message cell.") + } + + if playbackController.attachment != data.attachment { + playbackController.attachment = data.attachment + } + } + + override func setupViews() { + super.setupViews() + + bubbleCellContentView?.backgroundColor = .clear + bubbleCellContentView?.showSenderInfo = true + bubbleCellContentView?.showPaginationTitle = false + + guard let contentView = bubbleCellContentView?.innerContentView else { + return + } + + playbackController = VoiceMessagePlaybackController(mediaServiceProvider: VoiceMessageMediaServiceProvider.sharedProvider, + cacheManager: VoiceMessageAttachmentCacheManager.sharedManager) + + contentView.vc_addSubViewMatchingParent(playbackController.playbackView) + } +} diff --git a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift b/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift new file mode 100644 index 000000000..b7e516675 --- /dev/null +++ b/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithPaginationTitleBubbleCell.swift @@ -0,0 +1,25 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +class VoiceMessageWithPaginationTitleBubbleCell: VoiceMessageBubbleCell { + override func setupViews() { + super.setupViews() + + bubbleCellContentView?.showPaginationTitle = true + } +} diff --git a/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift b/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift new file mode 100644 index 000000000..cc091b39d --- /dev/null +++ b/Riot/Modules/Room/Views/BubbleCells/VoiceMessage/VoiceMessageWithoutSenderInfoBubbleCell.swift @@ -0,0 +1,25 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +class VoiceMessageWithoutSenderInfoBubbleCell: VoiceMessageBubbleCell { + override func setupViews() { + super.setupViews() + + bubbleCellContentView?.showSenderInfo = false + } +} diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h index b2ad883b8..430daa2bf 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h @@ -58,7 +58,6 @@ typedef enum : NSUInteger @property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainToolbarMinHeightConstraint; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainToolbarHeightConstraint; -@property (weak, nonatomic) IBOutlet NSLayoutConstraint *messageComposerContainerLeadingConstraint; @property (weak, nonatomic) IBOutlet NSLayoutConstraint *messageComposerContainerTrailingConstraint; @property (weak, nonatomic) IBOutlet UIButton *attachMediaButton; @@ -70,6 +69,7 @@ typedef enum : NSUInteger @property (weak, nonatomic) IBOutlet UILabel *inputContextLabel; @property (weak, nonatomic) IBOutlet UIButton *inputContextButton; @property (weak, nonatomic) IBOutlet RoomActionsBar *actionsBar; +@property (weak, nonatomic) UIView *voiceMessageToolbarView; /** Tell whether the filled data will be sent encrypted. NO by default. diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 26a2f4c69..2234cbf9a 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -34,6 +34,7 @@ const CGFloat kActionMenuAttachButtonSpringVelocity = 7; const CGFloat kActionMenuAttachButtonSpringDamping = .45; const NSTimeInterval kActionMenuContentAlphaAnimationDuration = .2; const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; +const CGFloat kComposerContainerTrailingPadding = 12; @interface RoomInputToolbarView() { @@ -75,6 +76,24 @@ const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; [self.rightInputToolbarButton setTitle:nil forState:UIControlStateHighlighted]; self.isEncryptionEnabled = _isEncryptionEnabled; + + [self updateUIWithTextMessage:nil animated:NO]; +} + +- (void)setVoiceMessageToolbarView:(UIView *)voiceMessageToolbarView +{ + if (RiotSettings.shared.enableVoiceMessages == NO) { + return; + } + + _voiceMessageToolbarView = voiceMessageToolbarView; + self.voiceMessageToolbarView.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:self.voiceMessageToolbarView]; + + [NSLayoutConstraint activateConstraints:@[[self.mainToolbarView.topAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.topAnchor], + [self.mainToolbarView.leftAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.leftAnchor], + [self.mainToolbarView.bottomAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.bottomAnchor], + [self.mainToolbarView.rightAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.rightAnchor]]]; } #pragma mark - Override MXKView @@ -133,7 +152,7 @@ const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; - (void)setTextMessage:(NSString *)textMessage { - [self updateSendButtonWithMessage:textMessage]; + [self updateUIWithTextMessage:textMessage animated:YES]; [super setTextMessage:textMessage]; } @@ -290,7 +309,7 @@ const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; - (BOOL)growingTextView:(HPGrowingTextView *)growingTextView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { NSString *newText = [growingTextView.text stringByReplacingCharactersInRange:range withString:text]; - [self updateSendButtonWithMessage:newText]; + [self updateUIWithTextMessage:newText animated:YES]; return YES; } @@ -354,24 +373,6 @@ const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; [super destroy]; } -- (void)updateSendButtonWithMessage:(NSString *)textMessage -{ - self.actionMenuOpened = NO; - - if (textMessage.length) - { - self.rightInputToolbarButton.alpha = 1; - self.messageComposerContainerTrailingConstraint.constant = self.frame.size.width - self.rightInputToolbarButton.frame.origin.x + 12; - } - else - { - self.rightInputToolbarButton.alpha = 0; - self.messageComposerContainerTrailingConstraint.constant = 12; - } - - [self layoutIfNeeded]; -} - #pragma mark - properties - (void)setActionMenuOpened:(BOOL)actionMenuOpened @@ -406,6 +407,10 @@ const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; [UIView animateWithDuration:kActionMenuContentAlphaAnimationDuration delay:_actionMenuOpened ? 0 : .1 options:UIViewAnimationOptionCurveEaseIn animations:^{ self->messageComposerContainer.alpha = actionMenuOpened ? 0 : 1; self.rightInputToolbarButton.alpha = self->growingTextView.text.length == 0 || actionMenuOpened ? 0 : 1; + if (RiotSettings.shared.enableVoiceMessages) + { + self.voiceMessageToolbarView.alpha = self->growingTextView.text.length > 0 || actionMenuOpened ? 0 : 1; + } } completion:nil]; [UIView animateWithDuration:kActionMenuComposerHeightAnimationDuration animations:^{ @@ -432,4 +437,25 @@ const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; [super paste:sender]; } +#pragma mark - Private + +- (void)updateUIWithTextMessage:(NSString *)textMessage animated:(BOOL)animated +{ + self.actionMenuOpened = NO; + + if (RiotSettings.shared.enableVoiceMessages == NO) { + self.rightInputToolbarButton.alpha = textMessage.length ? 1.0f : 0.0f; + self.messageComposerContainerTrailingConstraint.constant = (textMessage.length ? self.frame.size.width - self.rightInputToolbarButton.frame.origin.x : 0.0f) + kComposerContainerTrailingPadding; + + [self layoutIfNeeded]; + + return; + } + + [UIView animateWithDuration:(animated ? 0.15f : 0.0f) animations:^{ + self.rightInputToolbarButton.alpha = textMessage.length ? 1.0f : 0.0f; + self.voiceMessageToolbarView.alpha = textMessage.length ? 0.0f : 1.0; + }]; +} + @end diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib index aef8b2f81..f0f0c35b3 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib @@ -16,7 +16,7 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift new file mode 100644 index 000000000..4650d6fae --- /dev/null +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.swift @@ -0,0 +1,398 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 UIKit +import Reusable + +protocol VoiceMessageToolbarViewDelegate: AnyObject { + func voiceMessageToolbarViewDidRequestRecordingStart(_ toolbarView: VoiceMessageToolbarView) + func voiceMessageToolbarViewDidRequestRecordingCancel(_ toolbarView: VoiceMessageToolbarView) + func voiceMessageToolbarViewDidRequestRecordingFinish(_ toolbarView: VoiceMessageToolbarView) + func voiceMessageToolbarViewDidRequestLockedModeRecording(_ toolbarView: VoiceMessageToolbarView) + func voiceMessageToolbarViewDidRequestPlaybackToggle(_ toolbarView: VoiceMessageToolbarView) + func voiceMessageToolbarViewDidRequestSend(_ toolbarView: VoiceMessageToolbarView) +} + +enum VoiceMessageToolbarViewUIState { + case idle + case record + case lockedModeRecord + case lockedModePlayback +} + +struct VoiceMessageToolbarViewDetails { + var state: VoiceMessageToolbarViewUIState = .idle + var elapsedTime: String = "" + var audioSamples: [Float] = [] + var isPlaying: Bool = false + var progress: Double = 0.0 + var toastMessage: String? +} + +class VoiceMessageToolbarView: PassthroughView, NibLoadable, Themable, UIGestureRecognizerDelegate, VoiceMessagePlaybackViewDelegate { + + private enum Constants { + static let longPressMinimumDuration: TimeInterval = 1.0 + static let animationDuration: TimeInterval = 0.25 + static let lockModeTransitionAnimationDuration: TimeInterval = 0.5 + static let panDirectionChangeThreshold: CGFloat = 20.0 + static let toastContainerCornerRadii: CGFloat = 8.0 + static let toastDisplayTimeout: TimeInterval = 5.0 + } + + @IBOutlet private var backgroundView: UIView! + + @IBOutlet private var recordingContainerView: UIView! + + @IBOutlet private var recordButtonsContainerView: UIView! + @IBOutlet private var primaryRecordButton: UIButton! + @IBOutlet private var secondaryRecordButton: UIButton! + + @IBOutlet private var recordingChromeContainerView: UIView! + @IBOutlet private var recordingIndicatorView: UIView! + + @IBOutlet private var elapsedTimeLabel: UILabel! + + @IBOutlet private var slideToCancelContainerView: UIView! + @IBOutlet private var slideToCancelLabel: UILabel! + @IBOutlet private var slideToCancelChevron: UIImageView! + @IBOutlet private var slideToCancelGradient: UIImageView! + + @IBOutlet private var lockContainerView: UIView! + @IBOutlet private var lockContainerBackgroundView: UIView! + + @IBOutlet private var lockButtonsContainerView: UIView! + @IBOutlet private var primaryLockButton: UIButton! + @IBOutlet private var secondaryLockButton: UIButton! + @IBOutlet private var lockChevron: UIView! + + @IBOutlet private var lockedModeContainerView: UIView! + @IBOutlet private var deleteButton: UIButton! + @IBOutlet private var playbackViewContainerView: UIView! + @IBOutlet private var sendButton: UIButton! + + @IBOutlet private var toastNotificationContainerView: UIView! + @IBOutlet private var toastNotificationLabel: UILabel! + + private var playbackView: VoiceMessagePlaybackView! + + private var cancelLabelToRecordButtonDistance: CGFloat = 0.0 + private var lockChevronToRecordButtonDistance: CGFloat = 0.0 + private var lockChevronToLockButtonDistance: CGFloat = 0.0 + private var panDirection: UISwipeGestureRecognizer.Direction? + + private var details: VoiceMessageToolbarViewDetails? + + private var currentTheme: Theme? { + didSet { + updateUIWithDetails(details, animated: true) + } + } + + weak var delegate: VoiceMessageToolbarViewDelegate? + + override func awakeFromNib() { + super.awakeFromNib() + + lockContainerBackgroundView.layer.cornerRadius = lockContainerBackgroundView.bounds.width / 2.0 + lockButtonsContainerView.layer.cornerRadius = lockButtonsContainerView.bounds.width / 2.0 + toastNotificationContainerView.layer.cornerRadius = Constants.toastContainerCornerRadii + + let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress)) + longPressGesture.delegate = self + longPressGesture.minimumPressDuration = Constants.longPressMinimumDuration + recordButtonsContainerView.addGestureRecognizer(longPressGesture) + + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan)) + longPressGesture.delegate = self + recordButtonsContainerView.addGestureRecognizer(panGesture) + + playbackView = VoiceMessagePlaybackView.loadFromNib() + playbackView.delegate = self + playbackViewContainerView.vc_addSubViewMatchingParent(playbackView) + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleWaveformTap)) + playbackView.waveformView.addGestureRecognizer(tapGesture) + + updateUIWithDetails(VoiceMessageToolbarViewDetails(), animated: false) + } + + func configureWithDetails(_ details: VoiceMessageToolbarViewDetails) { + elapsedTimeLabel.text = details.elapsedTime + + self.updateToastNotificationsWithDetails(details) + self.updatePlaybackViewWithDetails(details) + + if self.details?.state != details.state { + switch details.state { + case .record: + var convertedFrame = self.convert(slideToCancelLabel.frame, from: slideToCancelContainerView) + cancelLabelToRecordButtonDistance = recordButtonsContainerView.frame.minX - convertedFrame.maxX + + convertedFrame = self.convert(lockChevron.frame, from: lockContainerView) + lockChevronToRecordButtonDistance = recordButtonsContainerView.frame.midY + convertedFrame.maxY + + lockChevronToLockButtonDistance = lockChevron.frame.minY - lockButtonsContainerView.frame.midY + + startAnimatingRecordingIndicator() + default: + cancelDrag() + } + + if details.state == .lockedModeRecord && self.details?.state == .record { + UIView.animate(withDuration: Constants.animationDuration) { + self.lockButtonsContainerView.transform = CGAffineTransform(scaleX: 0.1, y: 0.1) + } completion: { _ in + self.updateUIWithDetails(details, animated: true) + } + } else { + updateUIWithDetails(details, animated: true) + } + } + + self.details = details + } + + func getRequiredNumberOfSamples() -> Int { + return playbackView.getRequiredNumberOfSamples() + } + + // MARK: - Themable + + func update(theme: Theme) { + currentTheme = theme + playbackView.update(theme: theme) + } + + // MARK: - UIGestureRecognizerDelegate + + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + return true + } + + // MARK: - VoiceMessagePlaybackViewDelegate + + func voiceMessagePlaybackViewDidRequestPlaybackToggle() { + delegate?.voiceMessageToolbarViewDidRequestPlaybackToggle(self) + } + + func voiceMessagePlaybackViewDidChangeWidth() { + + } + + // MARK: - Private + + @objc private func handleLongPress(_ gestureRecognizer: UILongPressGestureRecognizer) { + switch gestureRecognizer.state { + case UIGestureRecognizer.State.began: + delegate?.voiceMessageToolbarViewDidRequestRecordingStart(self) + case UIGestureRecognizer.State.ended: + delegate?.voiceMessageToolbarViewDidRequestRecordingFinish(self) + default: + break + } + } + + @objc private func handlePan(_ gestureRecognizer: UIPanGestureRecognizer) { + guard details?.state == .record && gestureRecognizer.state == .changed else { + return + } + + let translation = gestureRecognizer.translation(in: self) + + if abs(translation.x) <= Constants.panDirectionChangeThreshold && abs(translation.y) <= Constants.panDirectionChangeThreshold { + panDirection = nil + } else if panDirection == nil { + if abs(translation.x) >= abs(translation.y) { + panDirection = .left + } else { + panDirection = .up + } + } + + if panDirection == .left { + secondaryRecordButton.transform = CGAffineTransform(translationX: min(translation.x, 0.0), y: 0.0) + slideToCancelContainerView.transform = CGAffineTransform(translationX: min(translation.x + cancelLabelToRecordButtonDistance, 0.0), y: 0.0) + + if abs(translation.x - recordButtonsContainerView.frame.width / 2.0) > self.bounds.width / 2.0 { + delegate?.voiceMessageToolbarViewDidRequestRecordingCancel(self) + } + } else if panDirection == .up { + secondaryRecordButton.transform = CGAffineTransform(translationX: 0.0, y: min(0.0, translation.y)) + + let yTranslation = min(max(translation.y + lockChevronToRecordButtonDistance, -lockChevronToLockButtonDistance), 0.0) + lockChevron.transform = CGAffineTransform(translationX: 0.0, y: yTranslation) + + let transitionPercentage = abs(yTranslation) / lockChevronToLockButtonDistance + + lockChevron.alpha = 1.0 - transitionPercentage + secondaryRecordButton.alpha = 1.0 - transitionPercentage + primaryLockButton.alpha = 1.0 - transitionPercentage + lockContainerBackgroundView.alpha = 1.0 - transitionPercentage + secondaryLockButton.alpha = transitionPercentage + + if transitionPercentage >= 1.0 { + self.delegate?.voiceMessageToolbarViewDidRequestLockedModeRecording(self) + } + + } else { + secondaryRecordButton.transform = CGAffineTransform(translationX: min(0.0, translation.x), y: min(0.0, translation.y)) + } + } + + private func cancelDrag() { + recordButtonsContainerView.gestureRecognizers?.forEach { gestureRecognizer in + gestureRecognizer.isEnabled = false + gestureRecognizer.isEnabled = true + } + } + + private func updateUIWithDetails(_ details: VoiceMessageToolbarViewDetails?, animated: Bool) { + guard let details = details else { + return + } + + UIView.animate(withDuration: (animated ? Constants.animationDuration : 0.0), delay: 0.0, options: .beginFromCurrentState) { + switch details.state { + case .record: + self.lockContainerBackgroundView.alpha = 1.0 + case .idle: + self.lockContainerBackgroundView.alpha = 1.0 + self.primaryLockButton.alpha = 1.0 + self.secondaryLockButton.alpha = 0.0 + self.lockChevron.alpha = 1.0 + default: + break + } + + self.backgroundView.alpha = (details.state == .idle ? 0.0 : 1.0) + self.primaryRecordButton.alpha = (details.state == .idle ? 1.0 : 0.0) + self.secondaryRecordButton.alpha = (details.state == .record ? 1.0 : 0.0) + self.recordingChromeContainerView.alpha = (details.state == .record ? 1.0 : 0.0) + self.lockContainerView.alpha = (details.state == .record ? 1.0 : 0.0) + self.lockedModeContainerView.alpha = (details.state == .lockedModePlayback || details.state == .lockedModeRecord ? 1.0 : 0.0) + self.recordingContainerView.alpha = (details.state == .idle || details.state == .record ? 1.0 : 0.0) + + guard let theme = self.currentTheme else { + return + } + + self.backgroundView.backgroundColor = theme.colors.background + self.slideToCancelGradient.tintColor = theme.colors.background + + self.primaryRecordButton.tintColor = theme.colors.tertiaryContent + self.slideToCancelLabel.textColor = theme.colors.secondaryContent + self.slideToCancelChevron.tintColor = theme.colors.secondaryContent + self.elapsedTimeLabel.textColor = theme.colors.secondaryContent + + self.lockContainerBackgroundView.backgroundColor = theme.colors.navigation + self.lockButtonsContainerView.backgroundColor = theme.colors.navigation + + } completion: { _ in + switch details.state { + case .idle: + self.secondaryRecordButton.transform = .identity + self.slideToCancelContainerView.transform = .identity + self.lockChevron.transform = .identity + self.lockButtonsContainerView.transform = .identity + default: + break + } + } + } + + private var toastIdleTimer: Timer? + private var lastUIState: VoiceMessageToolbarViewUIState = .idle + + private func updateToastNotificationsWithDetails(_ details: VoiceMessageToolbarViewDetails, animated: Bool = true) { + + guard self.toastNotificationLabel.text != details.toastMessage || lastUIState != details.state else { + return + } + + lastUIState = details.state + + let shouldShowNotification = details.state != .idle && details.toastMessage != nil + let requiredAlpha: CGFloat = shouldShowNotification ? 1.0 : 0.0 + + toastIdleTimer?.invalidate() + toastIdleTimer = nil + + if shouldShowNotification { + self.toastNotificationLabel.text = details.toastMessage + } + + UIView.animate(withDuration: (animated ? Constants.animationDuration : 0.0)) { + self.toastNotificationContainerView.alpha = requiredAlpha + } + + if shouldShowNotification { + toastIdleTimer = Timer.scheduledTimer(withTimeInterval: Constants.toastDisplayTimeout, repeats: false) { [weak self] timer in + guard let self = self else { + return + } + + self.toastIdleTimer?.invalidate() + self.toastIdleTimer = nil + + UIView.animate(withDuration: Constants.animationDuration) { + self.toastNotificationContainerView.alpha = 0 + } + } + } + } + + private func updatePlaybackViewWithDetails(_ details: VoiceMessageToolbarViewDetails, animated: Bool = true) { + UIView.animate(withDuration: (animated ? Constants.animationDuration : 0.0)) { + var playbackViewDetails = VoiceMessagePlaybackViewDetails() + playbackViewDetails.recording = (details.state == .record || details.state == .lockedModeRecord) + playbackViewDetails.playing = details.isPlaying + playbackViewDetails.progress = details.progress + playbackViewDetails.currentTime = details.elapsedTime + playbackViewDetails.samples = details.audioSamples + playbackViewDetails.playbackEnabled = true + self.playbackView.configureWithDetails(playbackViewDetails) + } + } + + private func startAnimatingRecordingIndicator() { + if self.details?.state != .record { + return + } + + UIView.animate(withDuration: Constants.lockModeTransitionAnimationDuration) { + if self.recordingIndicatorView.alpha > 0.0 { + self.recordingIndicatorView.alpha = 0.0 + } else { + self.recordingIndicatorView.alpha = 1.0 + } + } completion: { [weak self] _ in + self?.startAnimatingRecordingIndicator() + } + } + + @IBAction private func onTrashButtonTap(_ sender: UIBarItem) { + delegate?.voiceMessageToolbarViewDidRequestRecordingCancel(self) + } + + @IBAction private func onSendButtonTap(_ sender: UIBarItem) { + delegate?.voiceMessageToolbarViewDidRequestSend(self) + } + + @objc private func handleWaveformTap(_ gestureRecognizer: UITapGestureRecognizer) { + delegate?.voiceMessageToolbarViewDidRequestRecordingFinish(self) + } +} diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.xib b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.xib new file mode 100644 index 000000000..f11674470 --- /dev/null +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageToolbarView.xib @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessageWaveformView.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessageWaveformView.swift new file mode 100644 index 000000000..8685109c4 --- /dev/null +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessageWaveformView.swift @@ -0,0 +1,122 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 UIKit + +class VoiceMessageWaveformView: UIView { + + private let lineWidth: CGFloat = 2.0 + private let linePadding: CGFloat = 2.0 + private let renderingQueue: DispatchQueue = DispatchQueue(label: "io.element.VoiceMessageWaveformView.queue", qos: .userInitiated) + + var samples: [Float] = [] { + didSet { + computeWaveForm() + } + } + + var primaryLineColor = UIColor.lightGray { + didSet { + backgroundLayer.strokeColor = primaryLineColor.cgColor + backgroundLayer.fillColor = primaryLineColor.cgColor + } + } + var secondaryLineColor = UIColor.darkGray { + didSet { + progressLayer.strokeColor = secondaryLineColor.cgColor + progressLayer.fillColor = secondaryLineColor.cgColor + } + } + + private let backgroundLayer = CAShapeLayer() + private let progressLayer = CAShapeLayer() + + var progress = 0.0 { + didSet { + CATransaction.begin() + CATransaction.setDisableActions(true) + progressLayer.frame = CGRect(origin: self.bounds.origin, size: CGSize(width: self.bounds.width * CGFloat(self.progress), height: self.bounds.height)) + CATransaction.commit() + } + } + + var requiredNumberOfSamples: Int { + return Int(self.bounds.size.width / (lineWidth + linePadding)) + } + + override init(frame: CGRect) { + super.init(frame: frame) + + setupAndAdd(backgroundLayer, with: primaryLineColor) + setupAndAdd(progressLayer, with: secondaryLineColor) + progressLayer.masksToBounds = true + + computeWaveForm() + } + + required init?(coder: NSCoder) { + fatalError() + } + + override func layoutSubviews() { + super.layoutSubviews() + + backgroundLayer.frame = self.bounds + progressLayer.frame = CGRect(origin: self.bounds.origin, size: CGSize(width: self.bounds.width * CGFloat(self.progress), height: self.bounds.height)) + computeWaveForm() + } + + // MARK: - Private + + private func computeWaveForm() { + renderingQueue.async { [samples] in // Capture the current samples as a way to provide atomicity + let path = UIBezierPath() + + let drawMappingFactor = self.bounds.size.height + let minimumGraphAmplitude: CGFloat = 1 + + var xOffset: CGFloat = self.lineWidth / 2 + var index = 0 + + while xOffset < self.bounds.width - self.lineWidth { + let sample = CGFloat(index >= samples.count ? 1 : samples[index]) + let invertedDbSample = 1 - sample // sample is in dB, linearly normalized to [0, 1] (1 -> -50 dB) + let drawingAmplitude = max(minimumGraphAmplitude, invertedDbSample * drawMappingFactor) + + path.move(to: CGPoint(x: xOffset, y: self.bounds.midY - drawingAmplitude / 2)) + path.addLine(to: CGPoint(x: xOffset, y: self.bounds.midY + drawingAmplitude / 2)) + + xOffset += self.lineWidth + self.linePadding + + index += 1 + } + + DispatchQueue.main.async { + self.backgroundLayer.path = path.cgPath + self.progressLayer.path = path.cgPath + } + } + } + + private func setupAndAdd(_ shapeLayer: CAShapeLayer, with color: UIColor) { + shapeLayer.frame = self.bounds + shapeLayer.strokeColor = color.cgColor + shapeLayer.fillColor = color.cgColor + shapeLayer.lineCap = .round + shapeLayer.lineWidth = lineWidth + self.layer.addSublayer(shapeLayer) + } +} diff --git a/Riot/Modules/Rooms/RoomsDirectoryCoordinatorType.swift b/Riot/Modules/Rooms/RoomsDirectoryCoordinatorType.swift index a33788a44..40e38c6d4 100644 --- a/Riot/Modules/Rooms/RoomsDirectoryCoordinatorType.swift +++ b/Riot/Modules/Rooms/RoomsDirectoryCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol RoomsDirectoryCoordinatorDelegate: class { +protocol RoomsDirectoryCoordinatorDelegate: AnyObject { func roomsDirectoryCoordinator(_ coordinator: RoomsDirectoryCoordinatorType, didSelectRoom room: MXPublicRoom) func roomsDirectoryCoordinatorDidTapCreateNewRoom(_ coordinator: RoomsDirectoryCoordinatorType) func roomsDirectoryCoordinatorDidComplete(_ coordinator: RoomsDirectoryCoordinatorType) diff --git a/Riot/Modules/Rooms/RoomsViewController.m b/Riot/Modules/Rooms/RoomsViewController.m index a9dc73b5d..979b30610 100644 --- a/Riot/Modules/Rooms/RoomsViewController.m +++ b/Riot/Modules/Rooms/RoomsViewController.m @@ -18,16 +18,11 @@ #import "RecentsDataSource.h" -#import "DirectoryServerPickerViewController.h" - #import "Riot-Swift.h" @interface RoomsViewController () { RecentsDataSource *recentsDataSource; - - // The animated view displayed at the table view bottom when paginating the room directory - UIView* footerSpinnerView; } @end @@ -63,8 +58,6 @@ plusButtonImageView = [self vc_addFABWithImage:[UIImage imageNamed:@"rooms_floating_action"] target:self action:@selector(onPlusButtonPressed)]; - - self.enableStickyHeaders = YES; } - (void)viewWillAppear:(BOOL)animated @@ -74,24 +67,12 @@ [AppDelegate theDelegate].masterTabBarController.navigationItem.title = NSLocalizedStringFromTable(@"title_rooms", @"Vector", nil); [AppDelegate theDelegate].masterTabBarController.tabBar.tintColor = ThemeService.shared.theme.tintColor; - // TODO: Notify RiotSettings.shared.showNSFWPublicRooms change for iPad as viewWillAppear may not be called - recentsDataSource.publicRoomsDirectoryDataSource.showNSFWRooms = RiotSettings.shared.showNSFWPublicRooms; - if ([self.dataSource isKindOfClass:RecentsDataSource.class]) { - BOOL isFirstTime = (recentsDataSource != self.dataSource); - // Take the lead on the shared data source. recentsDataSource = (RecentsDataSource*)self.dataSource; recentsDataSource.areSectionsShrinkable = NO; [recentsDataSource setDelegate:self andRecentsDataSourceMode:RecentsDataSourceModeRooms]; - - if (isFirstTime) - { - // The first time the screen is displayed, make publicRoomsDirectoryDataSource - // start loading data - [recentsDataSource.publicRoomsDirectoryDataSource paginate:nil failure:nil]; - } } } @@ -113,32 +94,19 @@ [super refreshCurrentSelectedCell:forceVisible]; } -- (UIView *)tableView:(UITableView *)tableView viewForStickyHeaderInSection:(NSInteger)section -{ - CGRect frame = [tableView rectForHeaderInSection:section]; - frame.size.height = self.stickyHeaderHeight; - - return [recentsDataSource viewForHeaderInSection:section withFrame:frame]; -} - -- (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)actionIdentifier inCell:(id)cell userInfo:(NSDictionary *)userInfo -{ - if ([actionIdentifier isEqualToString:kRecentsDataSourceTapOnDirectoryServerChange]) - { - // Show the directory server picker - [self performSegueWithIdentifier:@"presentDirectoryServerPicker" sender:self]; - } - else - { - [super dataSource:dataSource didRecognizeAction:actionIdentifier inCell:cell userInfo:userInfo]; - } -} - - (void)onPlusButtonPressed { [self showRoomDirectory]; } +#pragma mark - UITableView delegate + +- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section +{ + // Hide the header to merge Invites and Rooms into a single list. + return 0.0; +} + #pragma mark - - (void)scrollToNextRoomWithMissedNotifications @@ -150,181 +118,6 @@ } } -#pragma mark - Navigation - -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender -{ - [super prepareForSegue:segue sender:sender]; - - UIViewController *pushedViewController = [segue destinationViewController]; - - if ([[segue identifier] isEqualToString:@"presentDirectoryServerPicker"]) - { - UINavigationController *pushedNavigationViewController = (UINavigationController*)pushedViewController; - DirectoryServerPickerViewController* directoryServerPickerViewController = (DirectoryServerPickerViewController*)pushedNavigationViewController.viewControllers.firstObject; - - MXKDirectoryServersDataSource *directoryServersDataSource = [[MXKDirectoryServersDataSource alloc] initWithMatrixSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession]; - [directoryServersDataSource finalizeInitialization]; - - // Add directory servers from the app settings - directoryServersDataSource.roomDirectoryServers = BuildSettings.publicRoomsDirectoryServers; - - __weak typeof(self) weakSelf = self; - - [directoryServerPickerViewController displayWithDataSource:directoryServersDataSource onComplete:^(id cellData) { - if (weakSelf && cellData) - { - typeof(self) self = weakSelf; - - // Use the selected directory server - if (cellData.thirdPartyProtocolInstance) - { - self->recentsDataSource.publicRoomsDirectoryDataSource.thirdpartyProtocolInstance = cellData.thirdPartyProtocolInstance; - } - else if (cellData.homeserver) - { - self->recentsDataSource.publicRoomsDirectoryDataSource.includeAllNetworks = cellData.includeAllNetworks; - self->recentsDataSource.publicRoomsDirectoryDataSource.homeserver = cellData.homeserver; - } - - // Refresh data - [self addSpinnerFooterView]; - - [self->recentsDataSource.publicRoomsDirectoryDataSource paginate:^(NSUInteger roomsAdded) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - // The table view is automatically filled - [self removeSpinnerFooterView]; - - // Make the directory section appear full-page - [self.recentsTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:self->recentsDataSource.directorySection] atScrollPosition:UITableViewScrollPositionTop animated:YES]; - } - - } failure:^(NSError *error) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - [self removeSpinnerFooterView]; - } - }]; - } - }]; - - // Hide back button title - pushedViewController.navigationController.navigationItem.backBarButtonItem =[[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; - } -} - -#pragma mark - UITableView delegate - -- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section -{ - if (section == recentsDataSource.directorySection) - { - // Let the recents dataSource provide the height of this section header - return [recentsDataSource heightForHeaderInSection:section]; - } - - return [super tableView:tableView heightForHeaderInSection:section]; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (indexPath.section == recentsDataSource.directorySection) - { - // Sanity check - MXPublicRoom *publicRoom = [recentsDataSource.publicRoomsDirectoryDataSource roomAtIndexPath:indexPath]; - if (publicRoom) - { - [self openPublicRoomAtIndexPath:indexPath]; - } - } - else - { - [super tableView:tableView didSelectRowAtIndexPath:indexPath]; - } -} - -- (void)scrollViewDidScroll:(UIScrollView *)scrollView -{ - // Trigger inconspicuous pagination on directy when user scrolls down - if ((scrollView.contentSize.height - scrollView.contentOffset.y - scrollView.frame.size.height) < 300) - { - [self triggerDirectoryPagination]; - } - - [super scrollViewDidScroll:scrollView]; -} - -#pragma mark - Private methods - -- (void)openPublicRoomAtIndexPath:(NSIndexPath *)indexPath -{ - MXPublicRoom *publicRoom = [recentsDataSource.publicRoomsDirectoryDataSource roomAtIndexPath:indexPath]; - - [self openPublicRoom:publicRoom]; -} - -- (void)triggerDirectoryPagination -{ - if (!recentsDataSource - || recentsDataSource.state == MXKDataSourceStateUnknown - || recentsDataSource.publicRoomsDirectoryDataSource.hasReachedPaginationEnd - || footerSpinnerView) - { - // We are not yet ready or being killed or we got all public rooms or we are already paginating - // Do nothing - return; - } - - [self addSpinnerFooterView]; - - [recentsDataSource.publicRoomsDirectoryDataSource paginate:^(NSUInteger roomsAdded) { - - // The table view is automatically filled - [self removeSpinnerFooterView]; - - } failure:^(NSError *error) { - - [self removeSpinnerFooterView]; - }]; -} - -- (void)addSpinnerFooterView -{ - if (!footerSpinnerView) - { - UIActivityIndicatorView* spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; - spinner.transform = CGAffineTransformMakeScale(0.75f, 0.75f); - CGRect frame = spinner.frame; - frame.size.height = 80; // 80 * 0.75 = 60 - spinner.bounds = frame; - - spinner.color = [UIColor darkGrayColor]; - spinner.hidesWhenStopped = NO; - spinner.backgroundColor = [UIColor clearColor]; - [spinner startAnimating]; - - // No need to manage constraints here, iOS defines them - self.recentsTableView.tableFooterView = footerSpinnerView = spinner; - } -} - -- (void)removeSpinnerFooterView -{ - if (footerSpinnerView) - { - footerSpinnerView = nil; - - // Hide line separators of empty cells - self.recentsTableView.tableFooterView = [[UIView alloc] init];; - } -} - #pragma mark - Empty view management - (void)updateEmptyView @@ -362,7 +155,6 @@ - (NSUInteger)totalItemCounts { return recentsDataSource.conversationCellDataArray.count - + recentsDataSource.publicRoomsDirectoryDataSource.roomsCount + recentsDataSource.invitesCellDataArray.count; } diff --git a/Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift b/Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift index e072ff350..d6b67472b 100644 --- a/Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift +++ b/Riot/Modules/Rooms/ShowDirectory/Cells/Room/DirectoryRoomTableViewCell.swift @@ -17,7 +17,7 @@ import UIKit import Reusable -protocol DirectoryRoomTableViewCellDelegate: class { +protocol DirectoryRoomTableViewCellDelegate: AnyObject { func directoryRoomTableViewCellDidTapJoin(_ cell: DirectoryRoomTableViewCell) } diff --git a/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryCoordinatorType.swift b/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryCoordinatorType.swift index 2290edf48..b46b3ca4f 100644 --- a/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryCoordinatorType.swift +++ b/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol ShowDirectoryCoordinatorDelegate: class { +protocol ShowDirectoryCoordinatorDelegate: AnyObject { func showDirectoryCoordinator(_ coordinator: ShowDirectoryCoordinatorType, didSelectRoom room: MXPublicRoom) func showDirectoryCoordinatorDidTapCreateNewRoom(_ coordinator: ShowDirectoryCoordinatorType) func showDirectoryCoordinatorDidCancel(_ coordinator: ShowDirectoryCoordinatorType) diff --git a/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryViewModelType.swift b/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryViewModelType.swift index 643cf4029..4c4291f99 100644 --- a/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryViewModelType.swift +++ b/Riot/Modules/Rooms/ShowDirectory/ShowDirectoryViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol ShowDirectoryViewModelViewDelegate: class { +protocol ShowDirectoryViewModelViewDelegate: AnyObject { func showDirectoryViewModel(_ viewModel: ShowDirectoryViewModelType, didUpdateViewState viewSate: ShowDirectoryViewState) } -protocol ShowDirectoryViewModelCoordinatorDelegate: class { +protocol ShowDirectoryViewModelCoordinatorDelegate: AnyObject { func showDirectoryViewModelDidSelect(_ viewModel: ShowDirectoryViewModelType, room: MXPublicRoom) func showDirectoryViewModel(_ viewModel: ShowDirectoryViewModelType, didSelectRoomWithIdOrAlias roomIdOrAlias: String) func showDirectoryViewModelDidTapCreateNewRoom(_ viewModel: ShowDirectoryViewModelType) diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift index f0a593f99..74b1b41a2 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol SecretsRecoveryWithKeyCoordinatorDelegate: class { +protocol SecretsRecoveryWithKeyCoordinatorDelegate: AnyObject { func secretsRecoveryWithKeyCoordinatorDidRecover(_ coordinator: SecretsRecoveryWithKeyCoordinatorType) func secretsRecoveryWithKeyCoordinatorDidCancel(_ coordinator: SecretsRecoveryWithKeyCoordinatorType) func secretsRecoveryWithKeyCoordinatorWantsToResetSecrets(_ viewModel: SecretsRecoveryWithKeyCoordinatorType) diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift index a0cc96d35..911f08302 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewController.swift @@ -123,6 +123,8 @@ final class SecretsRecoveryWithKeyViewController: UIViewController { self.updateRecoverButton() self.resetSecretsButton.vc_enableMultiLinesTitle() + + self.resetSecretsButton.isHidden = !RiotSettings.shared.secretsRecoveryAllowReset } private func update(theme: Theme) { diff --git a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift index c747d67ba..b71ffac5d 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithKey/SecretsRecoveryWithKeyViewModelType.swift @@ -16,11 +16,11 @@ import Foundation -protocol SecretsRecoveryWithKeyViewModelViewDelegate: class { +protocol SecretsRecoveryWithKeyViewModelViewDelegate: AnyObject { func secretsRecoveryWithKeyViewModel(_ viewModel: SecretsRecoveryWithKeyViewModelType, didUpdateViewState viewSate: SecretsRecoveryWithKeyViewState) } -protocol SecretsRecoveryWithKeyViewModelCoordinatorDelegate: class { +protocol SecretsRecoveryWithKeyViewModelCoordinatorDelegate: AnyObject { func secretsRecoveryWithKeyViewModelDidRecover(_ viewModel: SecretsRecoveryWithKeyViewModelType) func secretsRecoveryWithKeyViewModelDidCancel(_ viewModel: SecretsRecoveryWithKeyViewModelType) func secretsRecoveryWithKeyViewModelWantsToResetSecrets(_ viewModel: SecretsRecoveryWithKeyViewModelType) diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift index f09c2eb98..3d6d17e4e 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol SecretsRecoveryWithPassphraseCoordinatorDelegate: class { +protocol SecretsRecoveryWithPassphraseCoordinatorDelegate: AnyObject { func secretsRecoveryWithPassphraseCoordinatorDidRecover(_ coordinator: SecretsRecoveryWithPassphraseCoordinatorType) func secretsRecoveryWithPassphraseCoordinatorDoNotKnowPassphrase(_ coordinator: SecretsRecoveryWithPassphraseCoordinatorType) func secretsRecoveryWithPassphraseCoordinatorDidCancel(_ coordinator: SecretsRecoveryWithPassphraseCoordinatorType) diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift index c1fcaaec4..5c64fd51b 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewController.swift @@ -125,6 +125,8 @@ final class SecretsRecoveryWithPassphraseViewController: UIViewController { self.updateRecoverButton() self.resetSecretsButton.vc_enableMultiLinesTitle() + + self.resetSecretsButton.isHidden = !RiotSettings.shared.secretsRecoveryAllowReset } private func update(theme: Theme) { diff --git a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift index d54ae59f2..d1f1860a9 100644 --- a/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift +++ b/Riot/Modules/Secrets/Recover/RecoverWithPassphrase/SecretsRecoveryWithPassphraseViewModelType.swift @@ -16,11 +16,11 @@ import Foundation -protocol SecretsRecoveryWithPassphraseViewModelViewDelegate: class { +protocol SecretsRecoveryWithPassphraseViewModelViewDelegate: AnyObject { func secretsRecoveryWithPassphraseViewModel(_ viewModel: SecretsRecoveryWithPassphraseViewModelType, didUpdateViewState viewSate: SecretsRecoveryWithPassphraseViewState) } -protocol SecretsRecoveryWithPassphraseViewModelCoordinatorDelegate: class { +protocol SecretsRecoveryWithPassphraseViewModelCoordinatorDelegate: AnyObject { func secretsRecoveryWithPassphraseViewModelDidRecover(_ viewModel: SecretsRecoveryWithPassphraseViewModelType) func secretsRecoveryWithPassphraseViewModelDidCancel(_ viewModel: SecretsRecoveryWithPassphraseViewModelType) func secretsRecoveryWithPassphraseViewModelWantsToRecoverByKey(_ viewModel: SecretsRecoveryWithPassphraseViewModelType) diff --git a/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorType.swift b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorType.swift index 64b35f203..f7aea9dba 100644 --- a/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorType.swift +++ b/Riot/Modules/Secrets/Recover/SecretsRecoveryCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol SecretsRecoveryCoordinatorDelegate: class { +protocol SecretsRecoveryCoordinatorDelegate: AnyObject { func secretsRecoveryCoordinatorDidRecover(_ coordinator: SecretsRecoveryCoordinatorType) func secretsRecoveryCoordinatorDidCancel(_ coordinator: SecretsRecoveryCoordinatorType) } diff --git a/Riot/Modules/Secrets/Reset/SecretsResetCoordinatorType.swift b/Riot/Modules/Secrets/Reset/SecretsResetCoordinatorType.swift index 6b28912ce..cd33b058a 100644 --- a/Riot/Modules/Secrets/Reset/SecretsResetCoordinatorType.swift +++ b/Riot/Modules/Secrets/Reset/SecretsResetCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SecretsResetCoordinatorDelegate: class { +protocol SecretsResetCoordinatorDelegate: AnyObject { func secretsResetCoordinatorDidResetSecrets(_ coordinator: SecretsResetCoordinatorType) func secretsResetCoordinatorDidCancel(_ coordinator: SecretsResetCoordinatorType) } diff --git a/Riot/Modules/Secrets/Reset/SecretsResetViewModelType.swift b/Riot/Modules/Secrets/Reset/SecretsResetViewModelType.swift index ce64f4c31..5a5c88077 100644 --- a/Riot/Modules/Secrets/Reset/SecretsResetViewModelType.swift +++ b/Riot/Modules/Secrets/Reset/SecretsResetViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol SecretsResetViewModelViewDelegate: class { +protocol SecretsResetViewModelViewDelegate: AnyObject { func secretsResetViewModel(_ viewModel: SecretsResetViewModelType, didUpdateViewState viewState: SecretsResetViewState) } -protocol SecretsResetViewModelCoordinatorDelegate: class { +protocol SecretsResetViewModelCoordinatorDelegate: AnyObject { func secretsResetViewModel(_ viewModel: SecretsResetViewModelType, needsToAuthenticateWith request: AuthenticatedEndpointRequest) func secretsResetViewModelDidResetSecrets(_ viewModel: SecretsResetViewModelType) func secretsResetViewModelDidCancel(_ viewModel: SecretsResetViewModelType) diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinatorType.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinatorType.swift index 0c2002813..87c885fcd 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinatorType.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SecretsSetupRecoveryKeyCoordinatorDelegate: class { +protocol SecretsSetupRecoveryKeyCoordinatorDelegate: AnyObject { func secretsSetupRecoveryKeyCoordinatorDidComplete(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) func secretsSetupRecoveryKeyCoordinatorDidFailed(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) func secretsSetupRecoveryKeyCoordinatorDidCancel(_ coordinator: SecretsSetupRecoveryKeyCoordinatorType) diff --git a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModelType.swift b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModelType.swift index 3cf3843d2..0922c9347 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModelType.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryKey/SecretsSetupRecoveryKeyViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol SecretsSetupRecoveryKeyViewModelViewDelegate: class { +protocol SecretsSetupRecoveryKeyViewModelViewDelegate: AnyObject { func secretsSetupRecoveryKeyViewModel(_ viewModel: SecretsSetupRecoveryKeyViewModelType, didUpdateViewState viewSate: SecretsSetupRecoveryKeyViewState) } -protocol SecretsSetupRecoveryKeyViewModelCoordinatorDelegate: class { +protocol SecretsSetupRecoveryKeyViewModelCoordinatorDelegate: AnyObject { func secretsSetupRecoveryKeyViewModelDidComplete(_ viewModel: SecretsSetupRecoveryKeyViewModelType) func secretsSetupRecoveryKeyViewModelDidFailed(_ viewModel: SecretsSetupRecoveryKeyViewModelType) func secretsSetupRecoveryKeyViewModelDidCancel(_ viewModel: SecretsSetupRecoveryKeyViewModelType) diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinatorType.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinatorType.swift index 4318c4414..a3e3140d0 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinatorType.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SecretsSetupRecoveryPassphraseCoordinatorDelegate: class { +protocol SecretsSetupRecoveryPassphraseCoordinatorDelegate: AnyObject { func secretsSetupRecoveryPassphraseCoordinator(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType, didEnterNewPassphrase passphrase: String) func secretsSetupRecoveryPassphraseCoordinator(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType, didConfirmPassphrase passphrase: String) func secretsSetupRecoveryPassphraseCoordinatorDidCancel(_ coordinator: SecretsSetupRecoveryPassphraseCoordinatorType) diff --git a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModelType.swift b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModelType.swift index a7f27ad22..91a3eb50b 100644 --- a/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModelType.swift +++ b/Riot/Modules/Secrets/Setup/RecoveryPassphrase/SecretsSetupRecoveryPassphraseViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol SecretsSetupRecoveryPassphraseViewModelViewDelegate: class { +protocol SecretsSetupRecoveryPassphraseViewModelViewDelegate: AnyObject { func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didUpdateViewState viewSate: SecretsSetupRecoveryPassphraseViewState) } -protocol SecretsSetupRecoveryPassphraseViewModelCoordinatorDelegate: class { +protocol SecretsSetupRecoveryPassphraseViewModelCoordinatorDelegate: AnyObject { func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didEnterNewPassphrase passphrase: String) func secretsSetupRecoveryPassphraseViewModel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType, didConfirmPassphrase passphrase: String) func secretsSetupRecoveryPassphraseViewModelDidCancel(_ viewModel: SecretsSetupRecoveryPassphraseViewModelType) diff --git a/Riot/Modules/SecureBackup/Banners/SecureBackupBannerCell.swift b/Riot/Modules/SecureBackup/Banners/SecureBackupBannerCell.swift index a1ff99449..780689396 100644 --- a/Riot/Modules/SecureBackup/Banners/SecureBackupBannerCell.swift +++ b/Riot/Modules/SecureBackup/Banners/SecureBackupBannerCell.swift @@ -16,7 +16,7 @@ import UIKit -@objc protocol SecureBackupBannerCellDelegate: class { +@objc protocol SecureBackupBannerCellDelegate: AnyObject { func secureBackupBannerCellDidTapCloseAction(_ cell: SecureBackupBannerCell) } diff --git a/Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift b/Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift index 0bdd5123c..b95bf6a24 100644 --- a/Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift +++ b/Riot/Modules/SecureBackup/Setup/Intro/SecureBackupSetupIntroViewController.swift @@ -16,7 +16,7 @@ import UIKit -protocol SecureBackupSetupIntroViewControllerDelegate: class { +protocol SecureBackupSetupIntroViewControllerDelegate: AnyObject { func secureBackupSetupIntroViewControllerDidTapUseKey(_ secureBackupSetupIntroViewController: SecureBackupSetupIntroViewController) func secureBackupSetupIntroViewControllerDidTapUsePassphrase(_ secureBackupSetupIntroViewController: SecureBackupSetupIntroViewController) func secureBackupSetupIntroViewControllerDidCancel(_ secureBackupSetupIntroViewController: SecureBackupSetupIntroViewController, showSkipAlert: Bool) diff --git a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinatorType.swift b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinatorType.swift index b4392672f..0ab664999 100644 --- a/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinatorType.swift +++ b/Riot/Modules/SecureBackup/Setup/SecureBackupSetupCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SecureBackupSetupCoordinatorDelegate: class { +protocol SecureBackupSetupCoordinatorDelegate: AnyObject { func secureBackupSetupCoordinatorDidComplete(_ coordinator: SecureBackupSetupCoordinatorType) func secureBackupSetupCoordinatorDidCancel(_ coordinator: SecureBackupSetupCoordinatorType) } diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinatorType.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinatorType.swift index ebf6adc6e..909a0b33f 100644 --- a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinatorType.swift +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol ServiceTermsModalScreenCoordinatorDelegate: class { +protocol ServiceTermsModalScreenCoordinatorDelegate: AnyObject { func serviceTermsModalScreenCoordinatorDidAccept(_ coordinator: ServiceTermsModalScreenCoordinatorType) func serviceTermsModalScreenCoordinator(_ coordinator: ServiceTermsModalScreenCoordinatorType, displayPolicy policy: MXLoginPolicyData) func serviceTermsModalScreenCoordinatorDidDecline(_ coordinator: ServiceTermsModalScreenCoordinatorType) diff --git a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModelType.swift b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModelType.swift index 76fff52d3..4dd4b6317 100644 --- a/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModelType.swift +++ b/Riot/Modules/ServiceTerms/Modal/Modal/ServiceTermsModalScreenViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol ServiceTermsModalScreenViewModelViewDelegate: class { +protocol ServiceTermsModalScreenViewModelViewDelegate: AnyObject { func serviceTermsModalScreenViewModel(_ viewModel: ServiceTermsModalScreenViewModelType, didUpdateViewState viewSate: ServiceTermsModalScreenViewState) } -protocol ServiceTermsModalScreenViewModelCoordinatorDelegate: class { +protocol ServiceTermsModalScreenViewModelCoordinatorDelegate: AnyObject { func serviceTermsModalScreenViewModel(_ coordinator: ServiceTermsModalScreenViewModelType, displayPolicy policy: MXLoginPolicyData) func serviceTermsModalScreenViewModelDidAccept(_ viewModel: ServiceTermsModalScreenViewModelType) func serviceTermsModalScreenViewModelDidDecline(_ viewModel: ServiceTermsModalScreenViewModelType) diff --git a/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorType.swift b/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorType.swift index 84f3386b9..34c09cb18 100644 --- a/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorType.swift +++ b/Riot/Modules/ServiceTerms/Modal/ServiceTermsModalCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol ServiceTermsModalCoordinatorDelegate: class { +protocol ServiceTermsModalCoordinatorDelegate: AnyObject { func serviceTermsModalCoordinatorDidAccept(_ coordinator: ServiceTermsModalCoordinatorType) func serviceTermsModalCoordinatorDidDecline(_ coordinator: ServiceTermsModalCoordinatorType) func serviceTermsModalCoordinatorDidCancel(_ coordinator: ServiceTermsModalCoordinatorType) diff --git a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeCoordinatorType.swift b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeCoordinatorType.swift index 873a365bf..fb2a705bc 100644 --- a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeCoordinatorType.swift +++ b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol EnterPinCodeCoordinatorDelegate: class { +protocol EnterPinCodeCoordinatorDelegate: AnyObject { func enterPinCodeCoordinatorDidComplete(_ coordinator: EnterPinCodeCoordinatorType) func enterPinCodeCoordinatorDidCompleteWithReset(_ coordinator: EnterPinCodeCoordinatorType, dueToTooManyErrors: Bool) func enterPinCodeCoordinator(_ coordinator: EnterPinCodeCoordinatorType, didCompleteWithPin pin: String) diff --git a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModelType.swift b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModelType.swift index eb66cdc8c..89d1bf8f1 100644 --- a/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModelType.swift +++ b/Riot/Modules/SetPinCode/EnterPinCode/EnterPinCodeViewModelType.swift @@ -18,13 +18,13 @@ import Foundation -protocol EnterPinCodeViewModelViewDelegate: class { +protocol EnterPinCodeViewModelViewDelegate: AnyObject { func enterPinCodeViewModel(_ viewModel: EnterPinCodeViewModelType, didUpdateViewState viewSate: EnterPinCodeViewState) func enterPinCodeViewModel(_ viewModel: EnterPinCodeViewModelType, didUpdatePlaceholdersCount count: Int) func enterPinCodeViewModel(_ viewModel: EnterPinCodeViewModelType, didUpdateCancelButtonHidden isHidden: Bool) } -protocol EnterPinCodeViewModelCoordinatorDelegate: class { +protocol EnterPinCodeViewModelCoordinatorDelegate: AnyObject { func enterPinCodeViewModelDidComplete(_ viewModel: EnterPinCodeViewModelType) func enterPinCodeViewModelDidCompleteWithReset(_ viewModel: EnterPinCodeViewModelType, dueToTooManyErrors: Bool) func enterPinCodeViewModel(_ viewModel: EnterPinCodeViewModelType, didCompleteWithPin pin: String) diff --git a/Riot/Modules/SetPinCode/SetPinCoordinatorType.swift b/Riot/Modules/SetPinCode/SetPinCoordinatorType.swift index 3649250b1..ae439b9ee 100644 --- a/Riot/Modules/SetPinCode/SetPinCoordinatorType.swift +++ b/Riot/Modules/SetPinCode/SetPinCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SetPinCoordinatorDelegate: class { +protocol SetPinCoordinatorDelegate: AnyObject { func setPinCoordinatorDidComplete(_ coordinator: SetPinCoordinatorType) func setPinCoordinatorDidCompleteWithReset(_ coordinator: SetPinCoordinatorType, dueToTooManyErrors: Bool) func setPinCoordinatorDidCancel(_ coordinator: SetPinCoordinatorType) diff --git a/Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsCoordinatorType.swift b/Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsCoordinatorType.swift index 2cd9e9f83..f43d7759a 100644 --- a/Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsCoordinatorType.swift +++ b/Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SetupBiometricsCoordinatorDelegate: class { +protocol SetupBiometricsCoordinatorDelegate: AnyObject { func setupBiometricsCoordinatorDidComplete(_ coordinator: SetupBiometricsCoordinatorType) func setupBiometricsCoordinatorDidCompleteWithReset(_ coordinator: SetupBiometricsCoordinatorType, dueToTooManyErrors: Bool) func setupBiometricsCoordinatorDidCancel(_ coordinator: SetupBiometricsCoordinatorType) diff --git a/Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsViewModelType.swift b/Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsViewModelType.swift index 2b2708e1d..bd7f3124f 100644 --- a/Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsViewModelType.swift +++ b/Riot/Modules/SetPinCode/SetupBiometrics/SetupBiometricsViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol SetupBiometricsViewModelViewDelegate: class { +protocol SetupBiometricsViewModelViewDelegate: AnyObject { func setupBiometricsViewModel(_ viewModel: SetupBiometricsViewModelType, didUpdateViewState viewSate: SetupBiometricsViewState) } -protocol SetupBiometricsViewModelCoordinatorDelegate: class { +protocol SetupBiometricsViewModelCoordinatorDelegate: AnyObject { func setupBiometricsViewModelDidComplete(_ viewModel: SetupBiometricsViewModelType) func setupBiometricsViewModelDidCompleteWithReset(_ viewModel: SetupBiometricsViewModelType, dueToTooManyErrors: Bool) func setupBiometricsViewModelDidCancel(_ viewModel: SetupBiometricsViewModelType) diff --git a/Riot/Modules/Settings/Discovery/SettingsDiscoveryTableViewSection.swift b/Riot/Modules/Settings/Discovery/SettingsDiscoveryTableViewSection.swift index 0c9116928..6bee0aebe 100644 --- a/Riot/Modules/Settings/Discovery/SettingsDiscoveryTableViewSection.swift +++ b/Riot/Modules/Settings/Discovery/SettingsDiscoveryTableViewSection.swift @@ -16,7 +16,7 @@ import Foundation -@objc protocol SettingsDiscoveryTableViewSectionDelegate: class { +@objc protocol SettingsDiscoveryTableViewSectionDelegate: AnyObject { func settingsDiscoveryTableViewSection(_ settingsDiscoveryTableViewSection: SettingsDiscoveryTableViewSection, tableViewCellClass: MXKTableViewCell.Type, forRow: Int) -> MXKTableViewCell func settingsDiscoveryTableViewSectionDidUpdate(_ settingsDiscoveryTableViewSection: SettingsDiscoveryTableViewSection) diff --git a/Riot/Modules/Settings/Discovery/SettingsDiscoveryViewModelType.swift b/Riot/Modules/Settings/Discovery/SettingsDiscoveryViewModelType.swift index c3ebf4dab..0435a5eb5 100644 --- a/Riot/Modules/Settings/Discovery/SettingsDiscoveryViewModelType.swift +++ b/Riot/Modules/Settings/Discovery/SettingsDiscoveryViewModelType.swift @@ -16,11 +16,11 @@ import Foundation -protocol SettingsDiscoveryViewModelViewDelegate: class { +protocol SettingsDiscoveryViewModelViewDelegate: AnyObject { func settingsDiscoveryViewModel(_ viewModel: SettingsDiscoveryViewModelType, didUpdateViewState viewState: SettingsDiscoveryViewState) } -@objc protocol SettingsDiscoveryViewModelCoordinatorDelegate: class { +@objc protocol SettingsDiscoveryViewModelCoordinatorDelegate: AnyObject { func settingsDiscoveryViewModel(_ viewModel: SettingsDiscoveryViewModel, didSelectThreePidWith medium: String, and address: String) func settingsDiscoveryViewModelDidTapUserSettingsLink(_ viewModel: SettingsDiscoveryViewModel) } diff --git a/Riot/Modules/Settings/Discovery/ThreePidDetails/SettingsDiscoveryThreePidDetailsViewModelType.swift b/Riot/Modules/Settings/Discovery/ThreePidDetails/SettingsDiscoveryThreePidDetailsViewModelType.swift index ce00918fd..df8ccf6f2 100644 --- a/Riot/Modules/Settings/Discovery/ThreePidDetails/SettingsDiscoveryThreePidDetailsViewModelType.swift +++ b/Riot/Modules/Settings/Discovery/ThreePidDetails/SettingsDiscoveryThreePidDetailsViewModelType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SettingsDiscoveryThreePidDetailsViewModelViewDelegate: class { +protocol SettingsDiscoveryThreePidDetailsViewModelViewDelegate: AnyObject { func settingsDiscoveryThreePidDetailsViewModel(_ viewModel: SettingsDiscoveryThreePidDetailsViewModelType, didUpdateViewState viewSate: SettingsDiscoveryThreePidDetailsViewState) } diff --git a/Riot/Modules/Settings/IdentityServer/SettingsIdentityServerViewModel.swift b/Riot/Modules/Settings/IdentityServer/SettingsIdentityServerViewModel.swift index 0c80415fb..205d7c174 100644 --- a/Riot/Modules/Settings/IdentityServer/SettingsIdentityServerViewModel.swift +++ b/Riot/Modules/Settings/IdentityServer/SettingsIdentityServerViewModel.swift @@ -155,8 +155,7 @@ final class SettingsIdentityServerViewModel: SettingsIdentityServerViewModelType let viewStateUpdate: (SettingsIdentityServerViewState) -> Void = { (viewState) in - // Convert states for .addActionAlert and .disconnectActionAlert to - //.changeActionAlert + // Convert states for .addActionAlert and .disconnectActionAlert to .changeActionAlert var changeViewState = viewState switch viewState { case .alert(let alert, let onContinue): diff --git a/Riot/Modules/Settings/IdentityServer/SettingsIdentityServerViewModelType.swift b/Riot/Modules/Settings/IdentityServer/SettingsIdentityServerViewModelType.swift index 5d8447750..210fcbaef 100644 --- a/Riot/Modules/Settings/IdentityServer/SettingsIdentityServerViewModelType.swift +++ b/Riot/Modules/Settings/IdentityServer/SettingsIdentityServerViewModelType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SettingsIdentityServerViewModelViewDelegate: class { +protocol SettingsIdentityServerViewModelViewDelegate: AnyObject { func settingsIdentityServerViewModel(_ viewModel: SettingsIdentityServerViewModelType, didUpdateViewState viewSate: SettingsIdentityServerViewState) } diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift index d63ce69be..69b64d44f 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupTableViewSection.swift @@ -16,7 +16,7 @@ import UIKit -@objc protocol SettingsKeyBackupTableViewSectionDelegate: class { +@objc protocol SettingsKeyBackupTableViewSectionDelegate: AnyObject { func settingsKeyBackupTableViewSectionDidUpdate(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection) func settingsKeyBackupTableViewSection(_ settingsKeyBackupTableViewSection: SettingsKeyBackupTableViewSection, textCellForRow: Int) -> MXKTableViewCellWithTextView diff --git a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift index 662462145..191f1b802 100644 --- a/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift +++ b/Riot/Modules/Settings/KeyBackup/SettingsKeyBackupViewModelType.swift @@ -16,7 +16,7 @@ import UIKit -protocol SettingsKeyBackupViewModelViewDelegate: class { +protocol SettingsKeyBackupViewModelViewDelegate: AnyObject { func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateViewState viewState: SettingsKeyBackupViewState) func settingsKeyBackupViewModel(_ viewModel: SettingsKeyBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsKeyBackupNetworkRequestViewState) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift index 6ef387a20..cc23c878c 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupTableViewSection.swift @@ -16,7 +16,7 @@ import UIKit -@objc protocol SettingsSecureBackupTableViewSectionDelegate: class { +@objc protocol SettingsSecureBackupTableViewSectionDelegate: AnyObject { // Table view rendering func settingsSecureBackupTableViewSectionDidUpdate(_ settingsSecureBackupTableViewSection: SettingsSecureBackupTableViewSection) diff --git a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift index 4f83b5ccc..4248872dc 100644 --- a/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift +++ b/Riot/Modules/Settings/Security/SecureBackup/SettingsSecureBackupViewModelType.swift @@ -16,7 +16,7 @@ import UIKit -protocol SettingsSecureBackupViewModelViewDelegate: class { +protocol SettingsSecureBackupViewModelViewDelegate: AnyObject { func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateViewState viewState: SettingsSecureBackupViewState) func settingsSecureBackupViewModel(_ viewModel: SettingsSecureBackupViewModelType, didUpdateNetworkRequestViewState networkRequestViewSate: SettingsSecureBackupNetworkRequestViewState) diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index a6dad221f..11bbcb3d8 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -142,7 +142,8 @@ enum enum { - LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX = 0 + LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX = 0, + LABS_ENABLE_VOICE_MESSAGES = 1 }; enum @@ -487,6 +488,7 @@ TableViewSectionsDelegate> { Section *sectionLabs = [Section sectionWithTag:SECTION_TAG_LABS]; [sectionLabs addRowWithTag:LABS_ENABLE_RINGING_FOR_GROUP_CALLS_INDEX]; + [sectionLabs addRowWithTag:LABS_ENABLE_VOICE_MESSAGES]; sectionLabs.headerTitle = NSLocalizedStringFromTable(@"settings_labs", @"Vector", nil); if (sectionLabs.hasAnyRows) { @@ -2263,6 +2265,17 @@ TableViewSectionsDelegate> [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableRingingForGroupCalls:) forControlEvents:UIControlEventValueChanged]; + cell = labelAndSwitchCell; + } else if (row == LABS_ENABLE_VOICE_MESSAGES) + { + MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; + + labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_voice_messages", @"Vector", nil); + labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.enableVoiceMessages; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; + + [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleEnableVoiceMessages:) forControlEvents:UIControlEventValueChanged]; + cell = labelAndSwitchCell; } } @@ -2789,7 +2802,7 @@ TableViewSectionsDelegate> } } -- (void)togglePushNotifications:(id)sender +- (void)togglePushNotifications:(UISwitch *)sender { // Check first whether the user allow notification from device settings UIUserNotificationType currentUserNotificationTypes = UIApplication.sharedApplication.currentUserNotificationSettings.types; @@ -2819,7 +2832,7 @@ TableViewSectionsDelegate> [self presentViewController:currentAlert animated:YES completion:nil]; // Keep off the switch - ((UISwitch*)sender).on = NO; + sender.on = NO; } else if ([MXKAccountManager sharedManager].activeAccounts.count) { @@ -2842,7 +2855,7 @@ TableViewSectionsDelegate> [[AppDelegate theDelegate] registerForRemoteNotificationsWithCompletion:^(NSError * error) { if (error) { - [(UISwitch *)sender setOn:NO animated:YES]; + [sender setOn:NO animated:YES]; [self stopActivityIndicator]; } else @@ -2858,49 +2871,42 @@ TableViewSectionsDelegate> } } -- (void)toggleCallKit:(id)sender +- (void)toggleCallKit:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - [MXKAppSettings standardAppSettings].enableCallKit = switchButton.isOn; + [MXKAppSettings standardAppSettings].enableCallKit = sender.isOn; } -- (void)toggleStunServerFallback:(id)sender +- (void)toggleStunServerFallback:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - RiotSettings.shared.allowStunServerFallback = switchButton.isOn; + RiotSettings.shared.allowStunServerFallback = sender.isOn; self.mainSession.callManager.fallbackSTUNServer = RiotSettings.shared.allowStunServerFallback ? BuildSettings.stunServerFallbackUrlString : nil; } -- (void)toggleAllowIntegrations:(id)sender +- (void)toggleAllowIntegrations:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - MXSession *session = self.mainSession; [self startActivityIndicator]; - + __block RiotSharedSettings *sharedSettings = [[RiotSharedSettings alloc] initWithSession:session]; - [sharedSettings setIntegrationProvisioningWithEnabled:switchButton.on success:^{ + [sharedSettings setIntegrationProvisioningWithEnabled:sender.isOn success:^{ sharedSettings = nil; [self stopActivityIndicator]; } failure:^(NSError * _Nullable error) { sharedSettings = nil; - [switchButton setOn:!switchButton.on animated:YES]; + [sender setOn:!sender.isOn animated:YES]; [self stopActivityIndicator]; }]; } -- (void)toggleShowDecodedContent:(id)sender +- (void)toggleShowDecodedContent:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - RiotSettings.shared.showDecryptedContentInNotifications = switchButton.isOn; + RiotSettings.shared.showDecryptedContentInNotifications = sender.isOn; } -- (void)toggleLocalContactsSync:(id)sender +- (void)toggleLocalContactsSync:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - - if (switchButton.on) + if (sender.on) { [MXKContactManager requestUserConfirmationForLocalContactsSyncInViewController:self completionHandler:^(BOOL granted) { @@ -2941,47 +2947,36 @@ TableViewSectionsDelegate> } } -- (void)toggleEnableRageShake:(id)sender +- (void)toggleEnableRageShake:(UISwitch *)sender { - if (sender && [sender isKindOfClass:UISwitch.class]) - { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.enableRageShake = switchButton.isOn; - - [self updateSections]; - } + RiotSettings.shared.enableRageShake = sender.isOn; + + [self updateSections]; } - (void)toggleEnableRingingForGroupCalls:(UISwitch *)sender { - if (sender) - { - RiotSettings.shared.enableRingingForGroupCalls = sender.isOn; - - [self.tableView reloadData]; - } + RiotSettings.shared.enableRingingForGroupCalls = sender.isOn; } -- (void)togglePinRoomsWithMissedNotif:(id)sender +- (void)toggleEnableVoiceMessages:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome = switchButton.on; + RiotSettings.shared.enableVoiceMessages = sender.isOn; } -- (void)togglePinRoomsWithUnread:(id)sender +- (void)togglePinRoomsWithMissedNotif:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome = switchButton.on; + RiotSettings.shared.pinRoomsWithMissedNotificationsOnHome = sender.isOn; } -- (void)toggleCommunityFlair:(id)sender +- (void)togglePinRoomsWithUnread:(UISwitch *)sender { - UISwitch *switchButton = (UISwitch*)sender; - - NSIndexPath *indexPath = [NSIndexPath indexPathForRow:switchButton.tag inSection:groupsDataSource.joinedGroupsSection]; + RiotSettings.shared.pinRoomsWithUnreadMessagesOnHome = sender.on; +} + +- (void)toggleCommunityFlair:(UISwitch *)sender +{ + NSIndexPath *indexPath = [NSIndexPath indexPathForRow:sender.tag inSection:groupsDataSource.joinedGroupsSection]; id groupCellData = [groupsDataSource cellDataAtIndex:indexPath]; MXGroup *group = groupCellData.group; @@ -2991,7 +2986,7 @@ TableViewSectionsDelegate> __weak typeof(self) weakSelf = self; - [self.mainSession updateGroupPublicity:group isPublicised:switchButton.on success:^{ + [self.mainSession updateGroupPublicity:group isPublicised:sender.isOn success:^{ if (weakSelf) { @@ -3007,7 +3002,7 @@ TableViewSectionsDelegate> [self stopActivityIndicator]; // Come back to previous state button - [switchButton setOn:!switchButton.isOn animated:YES]; + [sender setOn:!sender.isOn animated:YES]; // Notify user [[AppDelegate theDelegate] showErrorAsAlert:error]; @@ -3653,16 +3648,9 @@ TableViewSectionsDelegate> animated:YES]; } -- (void)toggleNSFWPublicRoomsFiltering:(id)sender +- (void)toggleNSFWPublicRoomsFiltering:(UISwitch *)sender { - if (sender && [sender isKindOfClass:UISwitch.class]) - { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.showNSFWPublicRooms = switchButton.isOn; - - [self.tableView reloadData]; - } + RiotSettings.shared.showNSFWPublicRooms = sender.isOn; } #pragma mark - TextField listener diff --git a/Riot/Modules/Settings/SignOut/SignOutAlertPresenter.swift b/Riot/Modules/Settings/SignOut/SignOutAlertPresenter.swift index faaba6827..0231aef4e 100644 --- a/Riot/Modules/Settings/SignOut/SignOutAlertPresenter.swift +++ b/Riot/Modules/Settings/SignOut/SignOutAlertPresenter.swift @@ -16,7 +16,7 @@ import UIKit -@objc protocol SignOutAlertPresenterDelegate: class { +@objc protocol SignOutAlertPresenterDelegate: AnyObject { func signOutAlertPresenterDidTapSignOutAction(_ presenter: SignOutAlertPresenter) func signOutAlertPresenterDidTapBackupAction(_ presenter: SignOutAlertPresenter) } diff --git a/Riot/Modules/SideMenu/SideMenuActionView.swift b/Riot/Modules/SideMenu/SideMenuActionView.swift index 72ff6a852..689472fc1 100644 --- a/Riot/Modules/SideMenu/SideMenuActionView.swift +++ b/Riot/Modules/SideMenu/SideMenuActionView.swift @@ -17,7 +17,7 @@ import UIKit import Reusable -protocol SideMenuActionViewDelegate: class { +protocol SideMenuActionViewDelegate: AnyObject { func sideMenuActionView(_ actionView: SideMenuActionView, didTapMenuItem sideMenuItem: SideMenuItem?) } diff --git a/Riot/Modules/SideMenu/SideMenuCoordinatorType.swift b/Riot/Modules/SideMenu/SideMenuCoordinatorType.swift index f193c5a73..9223eca59 100644 --- a/Riot/Modules/SideMenu/SideMenuCoordinatorType.swift +++ b/Riot/Modules/SideMenu/SideMenuCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol SideMenuCoordinatorDelegate: class { +protocol SideMenuCoordinatorDelegate: AnyObject { func sideMenuCoordinator(_ coordinator: SideMenuCoordinatorType, didTapMenuItem menuItem: SideMenuItem, fromSourceView sourceView: UIView) } diff --git a/Riot/Modules/SideMenu/SideMenuViewModelType.swift b/Riot/Modules/SideMenu/SideMenuViewModelType.swift index 1ab51f3cf..92479a5ad 100644 --- a/Riot/Modules/SideMenu/SideMenuViewModelType.swift +++ b/Riot/Modules/SideMenu/SideMenuViewModelType.swift @@ -18,11 +18,11 @@ import Foundation -protocol SideMenuViewModelViewDelegate: class { +protocol SideMenuViewModelViewDelegate: AnyObject { func sideMenuViewModel(_ viewModel: SideMenuViewModelType, didUpdateViewState viewSate: SideMenuViewState) } -protocol SideMenuViewModelCoordinatorDelegate: class { +protocol SideMenuViewModelCoordinatorDelegate: AnyObject { func sideMenuViewModel(_ viewModel: SideMenuViewModelType, didTapMenuItem menuItem: SideMenuItem, fromSourceView sourceView: UIView) } diff --git a/Riot/Modules/SlidingModal/SlidingModalContainerView.swift b/Riot/Modules/SlidingModal/SlidingModalContainerView.swift index a494351f2..5a2c6b8d5 100644 --- a/Riot/Modules/SlidingModal/SlidingModalContainerView.swift +++ b/Riot/Modules/SlidingModal/SlidingModalContainerView.swift @@ -17,7 +17,7 @@ import UIKit import Reusable -protocol SlidingModalContainerViewDelegate: class { +protocol SlidingModalContainerViewDelegate: AnyObject { func slidingModalContainerViewDidTapBackground(_ view: SlidingModalContainerView) } diff --git a/Riot/Modules/Spaces/BetaAnnounceCell.swift b/Riot/Modules/Spaces/BetaAnnounceCell.swift index dac6dca86..9897c9417 100644 --- a/Riot/Modules/Spaces/BetaAnnounceCell.swift +++ b/Riot/Modules/Spaces/BetaAnnounceCell.swift @@ -17,7 +17,7 @@ import UIKit import Reusable -@objc protocol BetaAnnounceCellDelegate: class { +@objc protocol BetaAnnounceCellDelegate: AnyObject { func betaAnnounceCellDidTapCloseButton(_ cell: BetaAnnounceCell) } diff --git a/Riot/Modules/SplitView/SplitViewCoordinatorType.swift b/Riot/Modules/SplitView/SplitViewCoordinatorType.swift index 0162e9808..d87138978 100644 --- a/Riot/Modules/SplitView/SplitViewCoordinatorType.swift +++ b/Riot/Modules/SplitView/SplitViewCoordinatorType.swift @@ -16,7 +16,7 @@ import Foundation -protocol SplitViewCoordinatorDelegate: class { +protocol SplitViewCoordinatorDelegate: AnyObject { // TODO: Remove this method, authentication should not be handled by SplitViewCoordinator func splitViewCoordinatorDidCompleteAuthentication(_ coordinator: SplitViewCoordinatorType) } diff --git a/Riot/Modules/SplitView/SplitViewPresentable.swift b/Riot/Modules/SplitView/SplitViewPresentable.swift index c1bfa2bf7..f09134d3f 100644 --- a/Riot/Modules/SplitView/SplitViewPresentable.swift +++ b/Riot/Modules/SplitView/SplitViewPresentable.swift @@ -16,12 +16,12 @@ import UIKit -protocol SplitViewMasterPresentableDelegate: class { +protocol SplitViewMasterPresentableDelegate: AnyObject { func splitViewMasterPresentable(_ presentable: Presentable, wantsToDisplay detailPresentable: Presentable) } /// Protocol used by the master view presentable of a UISplitViewController -protocol SplitViewMasterPresentable: class, Presentable { +protocol SplitViewMasterPresentable: AnyObject, Presentable { var splitViewMasterPresentableDelegate: SplitViewMasterPresentableDelegate? { get set } diff --git a/Riot/Modules/StartChat/InviteFriendsHeaderView.swift b/Riot/Modules/StartChat/InviteFriendsHeaderView.swift index ceffd0cbb..c1d81ea06 100644 --- a/Riot/Modules/StartChat/InviteFriendsHeaderView.swift +++ b/Riot/Modules/StartChat/InviteFriendsHeaderView.swift @@ -18,7 +18,7 @@ import UIKit import Reusable @objc -protocol InviteFriendsHeaderViewDelegate: class { +protocol InviteFriendsHeaderViewDelegate: AnyObject { func inviteFriendsHeaderView(_ headerView: InviteFriendsHeaderView, didTapButton button: UIButton) } diff --git a/Riot/Modules/TabBar/MasterTabBarController.h b/Riot/Modules/TabBar/MasterTabBarController.h index 6ef661948..1d5f41a12 100644 --- a/Riot/Modules/TabBar/MasterTabBarController.h +++ b/Riot/Modules/TabBar/MasterTabBarController.h @@ -90,7 +90,7 @@ @param roomId the room identifier. @param eventId if not nil, the room will be opened on this event. - @param mxSession the matrix session in which the room should be available. + @param matrixSession the matrix session in which the room should be available. @param completion the block to execute at the end of the operation. */ - (void)selectRoomWithId:(NSString*)roomId andEventId:(NSString*)eventId inMatrixSession:(MXSession*)matrixSession completion:(void (^)(void))completion; @@ -113,7 +113,7 @@ Open a GroupDetailsViewController to display the information of the provided group. @param group - @param mxSession the matrix session in which the group should be available. + @param matrixSession the matrix session in which the group should be available. */ - (void)selectGroup:(MXGroup*)group inMatrixSession:(MXSession*)matrixSession; diff --git a/Riot/Modules/TabBar/TabBarCoordinator.swift b/Riot/Modules/TabBar/TabBarCoordinator.swift index 0efc1c03c..cb28c4427 100644 --- a/Riot/Modules/TabBar/TabBarCoordinator.swift +++ b/Riot/Modules/TabBar/TabBarCoordinator.swift @@ -245,9 +245,9 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { } private func setupSideMenuGestures() { - self.parameters.appNavigator.sideMenu.addScreenEdgePanGesturesToPresent(to: self.masterNavigationController.view) - - self.parameters.appNavigator.sideMenu.addPanGestureToPresent(to: self.masterNavigationController.navigationBar) + if let rootViewController = self.masterNavigationController.viewControllers.first { + self.parameters.appNavigator.sideMenu.addScreenEdgePanGesturesToPresent(to: rootViewController.view) + } } // MARK: Navigation diff --git a/Riot/Modules/TabBar/TabBarCoordinatorType.swift b/Riot/Modules/TabBar/TabBarCoordinatorType.swift index 6cd58a485..50d1cc36e 100644 --- a/Riot/Modules/TabBar/TabBarCoordinatorType.swift +++ b/Riot/Modules/TabBar/TabBarCoordinatorType.swift @@ -18,7 +18,7 @@ import Foundation -protocol TabBarCoordinatorDelegate: class { +protocol TabBarCoordinatorDelegate: AnyObject { // TODO: Remove this method, authentication should not be handled by TabBarCoordinator func tabBarCoordinatorDidCompleteAuthentication(_ coordinator: TabBarCoordinatorType) } diff --git a/Riot/Routers/NavigationRouterType.swift b/Riot/Routers/NavigationRouterType.swift index c32ea4e8e..a04e1f430 100755 --- a/Riot/Routers/NavigationRouterType.swift +++ b/Riot/Routers/NavigationRouterType.swift @@ -18,7 +18,7 @@ import UIKit /// Protocol describing a router that wraps a UINavigationController and add convenient completion handlers. Completions are called when a Presentable is removed. /// Routers are used to be passed between coordinators. They handles only `physical` navigation. -protocol NavigationRouterType: class, Presentable { +protocol NavigationRouterType: AnyObject, Presentable { /// Present modally a view controller on the navigation controller /// diff --git a/Riot/Routers/RootRouterType.swift b/Riot/Routers/RootRouterType.swift index b185b0723..633da610c 100755 --- a/Riot/Routers/RootRouterType.swift +++ b/Riot/Routers/RootRouterType.swift @@ -18,7 +18,7 @@ import UIKit /// Protocol describing a router that wraps the root navigation of the application. /// Routers are used to be passed between coordinators. They handles only `physical` navigation. -protocol RootRouterType: class { +protocol RootRouterType: AnyObject { /// Update the root view controller /// diff --git a/Riot/Utils/DelegateContainer.swift b/Riot/Utils/DelegateContainer.swift new file mode 100644 index 000000000..c4323e980 --- /dev/null +++ b/Riot/Utils/DelegateContainer.swift @@ -0,0 +1,47 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +/** + Object container storing references weakly. Ideal for implementing simple multiple delegation. + */ +struct DelegateContainer { + + private let hashTable: NSHashTable + + var delegates: [AnyObject] { + return hashTable.allObjects + } + + init() { + hashTable = NSHashTable(options: .weakMemory) + } + + func registerDelegate(_ delegate: AnyObject) { + hashTable.add(delegate) + } + + func deregisterDelegate(_ delegate: AnyObject) { + hashTable.remove(delegate) + } + + func notifyDelegatesWithBlock(_ block: (AnyObject) -> Void) { + for delegate in hashTable.allObjects { + block(delegate) + } + } +} diff --git a/Riot/Utils/PassthroughView.swift b/Riot/Utils/PassthroughView.swift new file mode 100644 index 000000000..b7dad91b0 --- /dev/null +++ b/Riot/Utils/PassthroughView.swift @@ -0,0 +1,32 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 UIKit + +/** + UIView subclass that ignores touches on itself. + */ +class PassthroughView: UIView { + public override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let hitTarget = super.hitTest(point, with: event) + + guard hitTarget == self else { + return hitTarget + } + + return nil + } +} diff --git a/Riot/Utils/WeakTarget.swift b/Riot/Utils/WeakTarget.swift new file mode 100644 index 000000000..695df92e8 --- /dev/null +++ b/Riot/Utils/WeakTarget.swift @@ -0,0 +1,37 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 Foundation + +/** + Used to avoid retain cycles by creating a proxy that holds a weak reference to the original object. + One example of that would be using CADisplayLink, which strongly retains its target, when manually invalidating it is unfeasable. + */ +class WeakTarget: NSObject { + private(set) weak var target: AnyObject? + let selector: Selector + + static let triggerSelector = #selector(WeakTarget.handleTick(parameter:)) + + init(_ target: AnyObject, selector: Selector) { + self.target = target + self.selector = selector + } + + @objc private func handleTick(parameter: Any) { + _ = self.target?.perform(self.selector, with: parameter) + } +} diff --git a/RiotShareExtension/Managers/ShareExtensionManager.m b/RiotShareExtension/Managers/ShareExtensionManager.m index 47dfb03c4..5b69743be 100644 --- a/RiotShareExtension/Managers/ShareExtensionManager.m +++ b/RiotShareExtension/Managers/ShareExtensionManager.m @@ -1165,8 +1165,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } // Retrieve the video frame at 1 sec to define the video thumbnail - AVURLAsset *urlAsset = [[AVURLAsset alloc] initWithURL:videoLocalUrl options:nil]; - AVAssetImageGenerator *assetImageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset]; + AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:videoLocalUrl options:nil]; + AVAssetImageGenerator *assetImageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:videoAsset]; assetImageGenerator.appliesPreferredTrackTransform = YES; CMTime time = CMTimeMake(1, 1); CGImageRef imageRef = [assetImageGenerator copyCGImageAtTime:time actualTime:NULL error:nil]; @@ -1174,7 +1174,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) UIImage *videoThumbnail = [[UIImage alloc] initWithCGImage:imageRef]; CFRelease(imageRef); - [room sendVideo:videoLocalUrl withThumbnail:videoThumbnail localEcho:nil success:^(NSString *eventId) { + [room sendVideoAsset:videoAsset withThumbnail:videoThumbnail localEcho:nil success:^(NSString *eventId) { if (successBlock) { successBlock(); diff --git a/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.m b/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.m index 777249a6e..94acaa453 100644 --- a/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.m +++ b/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.m @@ -129,6 +129,13 @@ } } +- (void)setKeyboardHeight:(CGFloat)keyboardHeight +{ + // Bypass inherited keyboard handling to fix layout when searching. + // There are no sticky headers to worry about updating. + return; +} + #pragma mark - Private - (void)showShareAlertForRoomPath:(NSIndexPath *)indexPath @@ -289,6 +296,12 @@ // Refresh display [self refreshRecentsTable]; } + + // Dismiss the keyboard when scrolling to match the behaviour of the main app. + if (self.recentsSearchBar.isFirstResponder) + { + [self.recentsSearchBar resignFirstResponder]; + } } } } diff --git a/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.xib b/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.xib index 630d940a3..bc77d3275 100644 --- a/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.xib +++ b/RiotShareExtension/Modules/Share/Listing/RoomsListViewController.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -39,7 +37,6 @@ - @@ -49,6 +46,7 @@ + diff --git a/RiotTests/RoomNotificationSettingsViewModelTests.swift b/RiotTests/RoomNotificationSettingsViewModelTests.swift new file mode 100644 index 000000000..b10dbd63b --- /dev/null +++ b/RiotTests/RoomNotificationSettingsViewModelTests.swift @@ -0,0 +1,146 @@ +// +// Copyright 2021 New Vector Ltd +// +// 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 XCTest +@testable import Riot + + +class MockRoomNotificationSettingsService: RoomNotificationSettingsServiceType { + + var listener: RoomNotificationStateCallback? + var notificationState: RoomNotificationState + + init(initialState: RoomNotificationState) { + notificationState = initialState + } + + func observeNotificationState(listener: @escaping RoomNotificationStateCallback) { + self.listener = listener + } + + func update(state: RoomNotificationState, completion: @escaping UpdateRoomNotificationStateCompletion) { + self.notificationState = state + completion() + listener?(state) + } +} + +class MockRoomNotificationSettingsView: RoomNotificationSettingsViewModelViewDelegate { + + var viewState: RoomNotificationSettingsViewStateType? + + func roomNotificationSettingsViewModel(_ viewModel: RoomNotificationSettingsViewModelType, didUpdateViewState viewState: RoomNotificationSettingsViewStateType) { + self.viewState = viewState + } +} + +class MockRoomNotificationSettingsCoordinator: RoomNotificationSettingsViewModelCoordinatorDelegate { + + var didComplete = false + var didCancel = false + func roomNotificationSettingsViewModelDidComplete(_ viewModel: RoomNotificationSettingsViewModelType) { + didComplete = true + } + + func roomNotificationSettingsViewModelDidCancel(_ viewModel: RoomNotificationSettingsViewModelType) { + didCancel = true + } +} + +class RoomNotificationSettingsViewModelTests: XCTestCase { + + enum Constants{ + static let roomDisplayName: String = "Test Room Name" + static let roomId: String = "1" + static let avatarUrl: String = "http://test.url.com" + static let avatarData = RoomAvatarViewData(roomId: "1", displayName: roomDisplayName, avatarUrl: avatarUrl, mediaManager: MXMediaManager()) + } + + var coordinator: MockRoomNotificationSettingsCoordinator! + var service: MockRoomNotificationSettingsService! + var view: MockRoomNotificationSettingsView! + var viewModel: RoomNotificationSettingsViewModel! + + override func setUpWithError() throws { + service = MockRoomNotificationSettingsService(initialState: .all) + view = MockRoomNotificationSettingsView() + coordinator = MockRoomNotificationSettingsCoordinator() + } + + func setupViewModel(roomEncrypted: Bool, showAvatar: Bool) { + let avatarData = showAvatar ? Constants.avatarData : nil + let viewModel = RoomNotificationSettingsViewModel(roomNotificationService: service, roomEncrypted: roomEncrypted, avatarViewData: avatarData) + viewModel.viewDelegate = view + viewModel.coordinatorDelegate = coordinator + self.viewModel = viewModel + } + + func testUnloaded() throws { + setupViewModel(roomEncrypted: true, showAvatar: false) + XCTAssertNil(view.viewState) + } + + func testUnencryptedOptions() throws { + setupViewModel(roomEncrypted: false, showAvatar: false) + viewModel.process(viewAction: .load) + XCTAssertNotNil(view.viewState) + XCTAssertTrue(view.viewState!.notificationOptions.count == 3) + } + + func testEncryptedOptions() throws { + setupViewModel(roomEncrypted: true, showAvatar: false) + viewModel.process(viewAction: .load) + XCTAssertNotNil(view.viewState) + XCTAssertTrue(view.viewState!.notificationOptions.count == 2) + } + + func testAvatar() throws { + setupViewModel(roomEncrypted: true, showAvatar: true) + viewModel.process(viewAction: .load) + XCTAssertNotNil(view.viewState?.avatarData) + XCTAssertEqual(view.viewState!.avatarData!.avatarUrl, Constants.avatarUrl) + } + + func testSelectionUpdateAndSave() throws { + setupViewModel(roomEncrypted: false, showAvatar: false) + viewModel.process(viewAction: .load) + XCTAssertNotNil(view.viewState) + XCTAssertTrue(view.viewState!.notificationState == .all) + viewModel.process(viewAction: .selectNotificationState(.mentionsAndKeywordsOnly)) + XCTAssertTrue(view.viewState!.notificationState == .mentionsAndKeywordsOnly) + viewModel.process(viewAction: .save) + XCTAssertTrue(service.notificationState == .mentionsAndKeywordsOnly) + XCTAssertTrue(coordinator.didComplete) + } + + func testCancel() throws { + setupViewModel(roomEncrypted: false, showAvatar: false) + viewModel.process(viewAction: .load) + XCTAssertNotNil(view.viewState) + viewModel.process(viewAction: .cancel) + XCTAssertTrue(coordinator.didCancel) + } + + func testMentionsOnlyNotAvaileOnEncryptedRoom() throws { + service = MockRoomNotificationSettingsService(initialState: .mentionsAndKeywordsOnly) + setupViewModel(roomEncrypted: true, showAvatar: false) + + viewModel.process(viewAction: .load) + XCTAssertNotNil(view.viewState) + XCTAssertTrue(view.viewState!.notificationState == .mute) + } + +} diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift index a211ab040..2f51aa2c2 100644 --- a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift index bb7f824e4..25024f94d 100644 --- a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift index 4fa5cc5dc..8caa44cf8 100644 --- a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ import Foundation -protocol FlowTemplateCoordinatorDelegate: class { +protocol FlowTemplateCoordinatorDelegate: AnyObject { func flowTemplateCoordinatorDidComplete(_ coordinator: FlowTemplateCoordinatorType) } diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift index 78ebc30f8..276fff2a8 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift index 745997b9c..191a4198b 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ import Foundation -protocol TemplateScreenCoordinatorDelegate: class { +protocol TemplateScreenCoordinatorDelegate: AnyObject { func templateScreenCoordinator(_ coordinator: TemplateScreenCoordinatorType, didCompleteWithUserDisplayName userDisplayName: String?) func templateScreenCoordinatorDidCancel(_ coordinator: TemplateScreenCoordinatorType) } diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewAction.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewAction.swift index 0bfc0f7f2..fc25efd98 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewAction.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewAction.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift index f3a30d180..78175a877 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModel.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModel.swift index 05c07ba58..4c386a8f7 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModel.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModel.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModelType.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModelType.swift index fd03254da..81900b629 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModelType.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModelType.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,11 +16,11 @@ import Foundation -protocol TemplateScreenViewModelViewDelegate: class { +protocol TemplateScreenViewModelViewDelegate: AnyObject { func templateScreenViewModel(_ viewModel: TemplateScreenViewModelType, didUpdateViewState viewSate: TemplateScreenViewState) } -protocol TemplateScreenViewModelCoordinatorDelegate: class { +protocol TemplateScreenViewModelCoordinatorDelegate: AnyObject { func templateScreenViewModel(_ viewModel: TemplateScreenViewModelType, didCompleteWithUserDisplayName userDisplayName: String?) func templateScreenViewModelDidCancel(_ viewModel: TemplateScreenViewModelType) } diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewState.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewState.swift index cc347baaa..4cebae662 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewState.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewState.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Tools/Templates/buildable/SimpleScreenTemplate/SimpleScreenTemplateViewController.swift b/Tools/Templates/buildable/SimpleScreenTemplate/SimpleScreenTemplateViewController.swift index 2955714c9..4513240b0 100644 --- a/Tools/Templates/buildable/SimpleScreenTemplate/SimpleScreenTemplateViewController.swift +++ b/Tools/Templates/buildable/SimpleScreenTemplate/SimpleScreenTemplateViewController.swift @@ -1,5 +1,5 @@ /* - Copyright 2020 New Vector Ltd + Copyright 2021 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,7 +16,7 @@ import UIKit -protocol SimpleScreenTemplateViewControllerDelegate: class { +protocol SimpleScreenTemplateViewControllerDelegate: AnyObject { func simpleScreenTemplateViewControllerDidTapSetupAction(_ viewController: SimpleScreenTemplateViewController) func simpleScreenTemplateViewControllerDidCancel(_ viewController: SimpleScreenTemplateViewController) }