diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index f170a1fd1..88458a696 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -57,8 +57,9 @@ body: id: homeserver attributes: label: Homeserver - description: Which server is your account registered on? - placeholder: e.g. matrix.org + description: | + Which server is your account registered on? If it is a local or non-public homeserver, please tell us what is the homeserver implementation (ex: Synapse/Dendrite/etc.) and the version. + placeholder: e.g. matrix.org or Synapse 1.50.0rc1 validations: required: false - type: dropdown diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 68a169237..24e8ab2e6 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -11,7 +11,6 @@ on: env: # Make the git branch for a PR available to our Fastfile MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }} - MapTilerAPIKey: ${{ secrets.MAPTILER_API_KEY }} jobs: build: diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 3fcc19ed3..00d4fa2f9 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -12,7 +12,6 @@ on: env: # Make the git branch for a PR available to our Fastfile MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }} - MapTilerAPIKey: ${{ secrets.MAPTILER_API_KEY }} jobs: tests: diff --git a/.github/workflows/release-alpha.yml b/.github/workflows/release-alpha.yml index 7ab8b331d..c952f6a4a 100644 --- a/.github/workflows/release-alpha.yml +++ b/.github/workflows/release-alpha.yml @@ -11,10 +11,23 @@ on: env: # Make the git branch for a PR available to our Fastfile MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }} - MapTilerAPIKey: ${{ secrets.MAPTILER_API_KEY }} jobs: + check-secret: + runs-on: macos-11 + outputs: + out-key: ${{ steps.out-key.outputs.defined }} + steps: + - id: out-key + env: + P12_KEY: ${{ secrets.ALPHA_CERTIFICATES_P12 }} + P12_PASSWORD_KEY: ${{ secrets.ALPHA_CERTIFICATES_P12 }} + if: "${{ env.P12_KEY != '' || env.P12_PASSWORD_KEY != '' }}" + run: echo "::set-output name=defined::true" build: + # Run job if secrets are avilable (not avaiable for forks). + needs: [check-secret] + if: needs.check-secret.outputs.out-key == 'true' name: Release runs-on: macos-11 diff --git a/.github/workflows/triage-move-labelled.yml b/.github/workflows/triage-move-labelled.yml index 320360fd5..4645e9b02 100644 --- a/.github/workflows/triage-move-labelled.yml +++ b/.github/workflows/triage-move-labelled.yml @@ -190,3 +190,49 @@ jobs: env: PROJECT_ID: "PN_kwDOAM0swc3m-g" GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} + +move_FTUE_issues: + name: Z-FTUE to FTUE board + runs-on: ubuntu-latest + if: > + contains(github.event.issue.labels.*.name, 'Z-FTUE') + steps: + - uses: octokit/graphql-action@v2.x + with: + headers: '{"GraphQL-Features": "projects_next_graphql"}' + query: | + mutation add_to_project($projectid:ID!,$contentid:ID!) { + addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) { + projectNextItem { + id + } + } + } + projectid: ${{ env.PROJECT_ID }} + contentid: ${{ github.event.issue.node_id }} + env: + PROJECT_ID: "PN_kwDOAM0swc4AAqVx" + GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} + +move_WTF_issues: + name: Z-WTF to WTF board + runs-on: ubuntu-latest + if: > + contains(github.event.issue.labels.*.name, 'Z-WTF') + steps: + - uses: octokit/graphql-action@v2.x + with: + headers: '{"GraphQL-Features": "projects_next_graphql"}' + query: | + mutation add_to_project($projectid:ID!,$contentid:ID!) { + addProjectNextItem(input:{projectId:$projectid contentId:$contentid}) { + projectNextItem { + id + } + } + } + projectid: ${{ env.PROJECT_ID }} + contentid: ${{ github.event.issue.node_id }} + env: + PROJECT_ID: "PN_kwDOAM0swc4AArk0" + GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }} diff --git a/CHANGES.md b/CHANGES.md index 8c2112418..c5c23b33f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,35 @@ +## Changes in 1.7.0 (2022-01-25) + +✨ Features + +- Message bubbles: Text message layout. ([#5208](https://github.com/vector-im/element-ios/issues/5208)) +- Message Bubbles: Layout for Media. ([#5209](https://github.com/vector-im/element-ios/issues/5209)) +- Message Bubbles: Support URL Previews. ([#5212](https://github.com/vector-im/element-ios/issues/5212)) +- Message Bubbles: Support reactions. ([#5214](https://github.com/vector-im/element-ios/issues/5214)) +- Added static location sharing sending and rendering support. ([#5298](https://github.com/vector-im/element-ios/issues/5298)) +- Message bubbles: Add settings and build flag. ([#5321](https://github.com/vector-im/element-ios/issues/5321)) + +🙌 Improvements + +- Upgrade MatrixSDK version ([v0.21.0](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.21.0)). +- Using mutable room list fetch sort options after chaning them to be a structure. Adaptation to MXStore api changes. ([#4384](https://github.com/vector-im/element-ios/issues/4384)) +- Reduce grace period to report decryption failure ([#5345](https://github.com/vector-im/element-ios/issues/5345)) + +🐛 Bugfixes + +- Fixed home screen not updating properly on theme changes. ([#4208](https://github.com/vector-im/element-ios/issues/4208)) +- Fixes DTMF(dial tones) during voice calls. ([#5375](https://github.com/vector-im/element-ios/issues/5375)) +- Fix crash when uploading a video on iPad when "Confirm size when sending" is enabled in settings. ([#5399](https://github.com/vector-im/element-ios/issues/5399)) +- Fix BuildSetting to show/hide the "Invite Friends" button in the side SideMenu. ([#5402](https://github.com/vector-im/element-ios/issues/5402)) +- Add BuildSetting to hide social login in favour of the simple SSO button. ([#5404](https://github.com/vector-im/element-ios/issues/5404)) +- Fix grey spinner showing indefinitely over the home view after launch. ([#5407](https://github.com/vector-im/element-ios/issues/5407)) +- RecentsViewController: Update tab bar badges on section-only updates. ([#5421](https://github.com/vector-im/element-ios/issues/5421)) + +Others + +- Fix graphql warnings in issue workflow automation ([#5294](https://github.com/vector-im/element-ios/issues/5294)) + + ## Changes in 1.6.12 (2022-01-11) 🙌 Improvements diff --git a/Config/AppVersion.xcconfig b/Config/AppVersion.xcconfig index f0f3b8161..cc05f87f8 100644 --- a/Config/AppVersion.xcconfig +++ b/Config/AppVersion.xcconfig @@ -15,5 +15,5 @@ // // Version -MARKETING_VERSION = 1.6.13 -CURRENT_PROJECT_VERSION = 1.6.13 +MARKETING_VERSION = 1.7.1 +CURRENT_PROJECT_VERSION = 1.7.1 diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index fe1bdcf8e..07645cff8 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -15,7 +15,6 @@ // import Foundation -import Keys /// BuildSettings provides settings computed at build time. /// In future, it may be automatically generated from xcconfig files @@ -213,8 +212,10 @@ final class BuildSettings: NSObject { static let allowInviteExernalUsers: Bool = true + // MARK: - Side Menu static let enableSideMenu: Bool = true - + static let sideMenuShowInviteFriends: Bool = true + /// Whether to read the `io.element.functional_members` state event and exclude any service members when computing a room's name and avatar. static let supportFunctionalMembers: Bool = true @@ -261,7 +262,6 @@ final class BuildSettings: NSObject { static let settingsScreenAllowBugReportingManually: Bool = true static let settingsScreenAllowDeactivatingAccount: Bool = true static let settingsScreenShowChangePassword:Bool = true - static let settingsScreenShowInviteFriends:Bool = true static let settingsScreenShowEnableStunServerFallback: Bool = true static let settingsScreenShowNotificationDecodedContentOption: Bool = true static let settingsScreenShowNsfwRoomsOption: Bool = true @@ -295,7 +295,7 @@ final class BuildSettings: NSObject { // Timeline style static let roomScreenAllowTimelineStyleConfiguration: Bool = false static let roomScreenTimelineDefaultStyleIdentifier: RoomTimelineStyleIdentifier = .plain - static var roomScreenEnableMessageBubblesByDefault: Bool { + static var isRoomScreenEnableMessageBubblesByDefault: Bool { return self.roomScreenTimelineDefaultStyleIdentifier == .bubble } @@ -349,6 +349,10 @@ final class BuildSettings: NSObject { static let authScreenShowPhoneNumber = true static let authScreenShowForgotPassword = true static let authScreenShowCustomServerOptions = true + static let authScreenShowSocialLoginSection = true + + // MARK: - Authentication Options + static let authEnableRefreshTokens = false // MARK: - Unified Search static let unifiedSearchScreenShowPublicDirectory = true @@ -368,13 +372,13 @@ final class BuildSettings: NSObject { // MARK: - Location Sharing - static let tileServerMapURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=" + RiotKeys().mapTilerAPIKey)! + static let tileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx")! static var locationSharingEnabled: Bool { guard #available(iOS 14, *) else { return false } - return false + return true } } diff --git a/Config/CommonConfiguration.swift b/Config/CommonConfiguration.swift index 5edcacc37..9567a696d 100644 --- a/Config/CommonConfiguration.swift +++ b/Config/CommonConfiguration.swift @@ -74,8 +74,13 @@ class CommonConfiguration: NSObject, Configurable { // Disable key backup on common sdkOptions.enableKeyBackupWhenStartingMXCrypto = false + + // Pass threading option to the SDK + sdkOptions.enableThreads = RiotSettings.shared.enableThreads sdkOptions.clientPermalinkBaseUrl = BuildSettings.clientPermalinkBaseUrl + + sdkOptions.authEnableRefreshTokens = BuildSettings.authEnableRefreshTokens // Configure key provider delegate MXKeyProvider.sharedInstance().delegate = EncryptionKeyManager.shared } diff --git a/Gemfile b/Gemfile index ea061a17e..53efbaf92 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,6 @@ source "https://rubygems.org" gem "xcode-install" gem "fastlane" gem "cocoapods", '~>1.11.2' -gem "cocoapods-keys" plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index 78fe028a3..610cbbadd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,9 +3,6 @@ GEM specs: CFPropertyList (3.0.5) rexml - RubyInline (3.12.5) - ZenTest (~> 4.3) - ZenTest (4.12.0) activesupport (6.1.4.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) @@ -67,9 +64,6 @@ GEM typhoeus (~> 1.0) cocoapods-deintegrate (1.0.5) cocoapods-downloader (1.5.1) - cocoapods-keys (2.2.1) - dotenv - osx_keychain cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.1) @@ -231,8 +225,6 @@ GEM netrc (0.11.0) optparse (0.1.1) os (1.1.4) - osx_keychain (1.0.2) - RubyInline (~> 3) plist (3.6.0) public_suffix (4.0.6) rake (13.0.6) @@ -300,7 +292,6 @@ PLATFORMS DEPENDENCIES cocoapods (~> 1.11.2) - cocoapods-keys fastlane fastlane-plugin-diawi fastlane-plugin-versioning @@ -308,4 +299,4 @@ DEPENDENCIES xcode-install BUNDLED WITH - 2.2.28 + 2.2.32 diff --git a/Podfile b/Podfile index 6a5620b94..286135afb 100644 --- a/Podfile +++ b/Podfile @@ -13,7 +13,7 @@ use_frameworks! # - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI # # Warning: our internal tooling depends on the name of this variable name, so be sure not to change it -$matrixSDKVersion = '= 0.20.16' +$matrixSDKVersion = '= 0.21.0' # $matrixSDKVersion = :local # $matrixSDKVersion = { :branch => 'develop'} # $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } } @@ -87,6 +87,7 @@ abstract_target 'RiotPods' do import_SwiftUI_pods pod 'DGCollectionViewLeftAlignFlowLayout', '~> 1.0.4' + pod 'UICollectionViewRightAlignedLayout', '~> 0.0.3' pod 'KTCenterFlowLayout', '~> 1.3.1' pod 'ZXingObjC', '~> 3.6.5' pod 'FlowCommoniOS', '~> 1.12.0' @@ -129,11 +130,6 @@ abstract_target 'RiotPods' do end -plugin 'cocoapods-keys', { - :project => "Riot", - :keys => ["MapTilerAPIKey"] -} - post_install do |installer| installer.pods_project.targets.each do |target| @@ -155,4 +151,4 @@ post_install do |installer| config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' end end -end +end \ No newline at end of file diff --git a/Podfile.lock b/Podfile.lock index 749fe065f..366d83a2e 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -49,7 +49,6 @@ PODS: - Introspect (0.1.3) - JitsiMeetSDK (3.10.2) - KeychainAccess (4.2.2) - - Keys (1.0.1) - KituraContracts (1.2.1): - LoggerAPI (~> 1.7) - KTCenterFlowLayout (1.3.1) @@ -58,16 +57,16 @@ PODS: - LoggerAPI (1.9.200): - Logging (~> 1.1) - Logging (1.4.0) - - MatrixSDK (0.20.16): - - MatrixSDK/Core (= 0.20.16) - - MatrixSDK/Core (0.20.16): + - MatrixSDK (0.21.0): + - MatrixSDK/Core (= 0.21.0) + - MatrixSDK/Core (0.21.0): - AFNetworking (~> 4.0.0) - GZIP (~> 1.3.0) - libbase58 (~> 0.1.4) - OLMKit (~> 3.2.5) - Realm (= 10.16.0) - SwiftyBeaver (= 1.9.5) - - MatrixSDK/JingleCallStack (0.20.16): + - MatrixSDK/JingleCallStack (0.21.0): - JitsiMeetSDK (= 3.10.2) - MatrixSDK/Core - OLMKit (3.2.5): @@ -115,11 +114,10 @@ DEPENDENCIES: - HPGrowingTextView (~> 1.1) - Introspect (~> 0.1) - KeychainAccess (~> 4.2.2) - - Keys (from `Pods/CocoaPodsKeys`) - KTCenterFlowLayout (~> 1.3.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.20.16) - - MatrixSDK/JingleCallStack (= 0.20.16) + - MatrixSDK (= 0.21.0) + - MatrixSDK/JingleCallStack (= 0.21.0) - OLMKit - PostHog (~> 1.4.4) - ReadMoreTextView (~> 3.0.1) @@ -179,12 +177,10 @@ EXTERNAL SOURCES: AnalyticsEvents: :branch: release/swift :git: https://github.com/matrix-org/matrix-analytics-events.git - Keys: - :path: Pods/CocoaPodsKeys CHECKOUT OPTIONS: AnalyticsEvents: - :commit: f1805ad7c3fafa7fd9c6e2eaa9e0165f8142ecd2 + :commit: 8058dc6ec07ce0acfe5fdb19eb7e309b0c13845c :git: https://github.com/matrix-org/matrix-analytics-events.git SPEC CHECKSUMS: @@ -207,14 +203,13 @@ SPEC CHECKSUMS: Introspect: 2be020f30f084ada52bb4387fff83fa52c5c400e JitsiMeetSDK: 2f118fa770f23e518f3560fc224fae3ac7062223 KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 - Keys: a576f4c9c1c641ca913a959a9c62ed3f215a8de9 KituraContracts: e845e60dc8627ad0a76fa55ef20a45451d8f830b KTCenterFlowLayout: 6e02b50ab2bd865025ae82fe266ed13b6d9eaf97 libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d Logging: beeb016c9c80cf77042d62e83495816847ef108b - MatrixSDK: af6a70532bb43af59f43a1f4dae512a26afeab0b + MatrixSDK: cd98e3e4287b8a4f3a5bb47642beae80e8f62534 OLMKit: 9fb4799c4a044dd2c06bda31ec31a12191ad30b5 PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d @@ -230,6 +225,6 @@ SPEC CHECKSUMS: zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: 2493587902f8f28bb2638303dd583c47e9f24d8b +PODFILE CHECKSUM: 39329dd448eb0ad10c396f84f854a18962ab1fc4 COCOAPODS: 1.11.2 diff --git a/Riot/Assets/Base.lproj/Main.storyboard b/Riot/Assets/Base.lproj/Main.storyboard index 0fc069dcc..4708d7dbc 100644 --- a/Riot/Assets/Base.lproj/Main.storyboard +++ b/Riot/Assets/Base.lproj/Main.storyboard @@ -64,27 +64,6 @@ - - - - - - - - - - - - - - - - - - - - - @@ -98,9 +77,6 @@ - - - @@ -543,10 +519,29 @@ + + + + + + + + + + + + + + + + + + + + - diff --git a/Riot/Assets/Images.xcassets/Onboarding/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1.imageset/Contents.json new file mode 100644 index 000000000..1c9083665 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingSplashScreenPage1.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1.imageset/OnboardingSplashScreenPage1.pdf b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1.imageset/OnboardingSplashScreenPage1.pdf new file mode 100644 index 000000000..2afeae294 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1.imageset/OnboardingSplashScreenPage1.pdf @@ -0,0 +1,1067 @@ +%PDF-1.7 + +1 0 obj + << /Type /XObject + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +q +1.000000 0.000000 -0.000000 1.000000 116.000000 142.000000 cm +0.857271 0.866762 0.880049 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +87.500000 13.000000 l +91.089851 13.000000 94.000000 10.089850 94.000000 6.500000 c +94.000000 6.500000 l +94.000000 2.910150 91.089851 0.000000 87.500000 0.000000 c +6.500002 0.000000 l +2.910151 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 116.000000 142.000000 cm +1.000000 0.505882 0.176471 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +87.500000 13.000000 l +91.089851 13.000000 94.000000 10.089850 94.000000 6.500000 c +94.000000 6.500000 l +94.000000 2.910150 91.089851 0.000000 87.500000 0.000000 c +6.500002 0.000000 l +2.910151 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q + +endstream +endobj + +2 0 obj + 882 +endobj + +3 0 obj + << /Type /XObject + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +q +1.000000 0.000000 -0.000000 1.000000 127.119141 85.000000 cm +0.857271 0.866762 0.880049 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +119.500000 13.000000 l +123.089851 13.000000 126.000000 10.089850 126.000000 6.500000 c +126.000000 6.500000 l +126.000000 2.910150 123.089851 0.000000 119.500000 0.000000 c +6.499998 0.000000 l +2.910147 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 127.119141 85.000000 cm +0.454902 0.819608 0.172549 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +119.500000 13.000000 l +123.089851 13.000000 126.000000 10.089850 126.000000 6.500000 c +126.000000 6.500000 l +126.000000 2.910150 123.089851 0.000000 119.500000 0.000000 c +6.499998 0.000000 l +2.910147 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 896 +endobj + +5 0 obj + << /Type /XObject + /Length 6 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +q +1.000000 0.000000 -0.000000 1.000000 102.000000 207.000000 cm +0.857271 0.866762 0.880049 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +119.500000 13.000000 l +123.089851 13.000000 126.000000 10.089850 126.000000 6.500000 c +126.000000 6.500000 l +126.000000 2.910150 123.089851 0.000000 119.500000 0.000000 c +6.499998 0.000000 l +2.910147 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 102.000000 207.000000 cm +0.360784 0.337255 0.960784 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +119.500000 13.000000 l +123.089851 13.000000 126.000000 10.089850 126.000000 6.500000 c +126.000000 6.500000 l +126.000000 2.910150 123.089851 0.000000 119.500000 0.000000 c +6.499998 0.000000 l +2.910147 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q + +endstream +endobj + +6 0 obj + 898 +endobj + +7 0 obj + << /Type /XObject + /Length 8 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << /XObject << /X2 1 0 R + /X3 3 0 R + /X1 5 0 R + >> + /ExtGState << /E6 << /ca 0.125000 >> + /E4 << /ca 0.250000 >> + /E2 << /ca 0.250000 >> + /E5 << /ca 0.500000 >> + /E3 << /ca 0.400000 >> + /E1 << /ca 0.300000 >> + >> + >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 51.000000 183.000000 cm +0.890196 0.909804 0.941176 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 51.000000 183.000000 cm +0.835294 0.847059 0.968627 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E2 gs +/X1 Do +Q +q +1.000000 0.000000 -0.000000 1.000000 102.000000 184.000000 cm +0.890196 0.909804 0.941176 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +141.500000 13.000000 l +145.089859 13.000000 148.000000 10.089850 148.000000 6.500000 c +148.000000 6.500000 l +148.000000 2.910150 145.089844 0.000000 141.500000 0.000000 c +6.499999 0.000000 l +2.910148 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 102.000000 184.000000 cm +0.890196 0.909804 0.941176 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +141.500000 13.000000 l +145.089859 13.000000 148.000000 10.089850 148.000000 6.500000 c +148.000000 6.500000 l +148.000000 2.910150 145.089844 0.000000 141.500000 0.000000 c +6.499999 0.000000 l +2.910148 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 60.000000 119.000000 cm +0.890196 0.909804 0.941176 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E3 gs +1.000000 0.000000 -0.000000 1.000000 60.000000 119.000000 cm +0.964706 0.878431 0.811765 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E4 gs +/X2 Do +Q +q +1.000000 0.000000 -0.000000 1.000000 116.000000 121.000000 cm +0.890196 0.909804 0.941176 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +56.500000 13.000000 l +60.089848 13.000000 63.000000 10.089850 63.000000 6.500000 c +63.000000 6.500000 l +63.000000 2.910150 60.089851 0.000000 56.500000 0.000000 c +6.500001 0.000000 l +2.910151 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 116.000000 121.000000 cm +0.890196 0.909804 0.941176 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +56.500000 13.000000 l +60.089848 13.000000 63.000000 10.089850 63.000000 6.500000 c +63.000000 6.500000 l +63.000000 2.910150 60.089851 0.000000 56.500000 0.000000 c +6.500001 0.000000 l +2.910151 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 189.000000 121.000000 cm +0.890196 0.909804 0.941176 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +57.500000 13.000000 l +61.089851 13.000000 64.000000 10.089850 64.000000 6.500000 c +64.000000 6.500000 l +64.000000 2.910150 61.089851 0.000000 57.500000 0.000000 c +6.500000 0.000000 l +2.910149 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 189.000000 121.000000 cm +0.890196 0.909804 0.941176 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +57.500000 13.000000 l +61.089851 13.000000 64.000000 10.089850 64.000000 6.500000 c +64.000000 6.500000 l +64.000000 2.910150 61.089851 0.000000 57.500000 0.000000 c +6.500000 0.000000 l +2.910149 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 70.000000 60.000000 cm +1.000000 1.000000 1.000000 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 70.000000 60.000000 cm +0.890196 0.909804 0.941176 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E5 gs +1.000000 0.000000 -0.000000 1.000000 70.000000 60.000000 cm +0.894118 0.964706 0.894118 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E6 gs +/X3 Do +Q +q +1.000000 0.000000 -0.000000 1.000000 116.000000 72.000000 cm +1.000000 1.000000 1.000000 scn +0.000000 9.000000 m +0.000000 13.970562 4.029438 18.000000 9.000000 18.000000 c +23.000000 18.000000 l +27.970562 18.000000 32.000000 13.970562 32.000000 9.000000 c +32.000000 9.000000 l +32.000000 4.029437 27.970562 0.000000 23.000000 0.000000 c +9.000000 0.000000 l +4.029438 0.000000 0.000000 4.029437 0.000000 9.000000 c +0.000000 9.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 123.366211 77.722656 cm +0.552000 0.600000 0.648000 scn +4.910892 3.274046 m +4.910892 1.917940 3.811552 0.818600 2.455446 0.818600 c +1.099341 0.818600 0.000000 1.917940 0.000000 3.274046 c +0.000000 4.630152 1.099341 5.729492 2.455446 5.729492 c +3.811552 5.729492 4.910892 4.630152 4.910892 3.274046 c +h +11.458750 2.455564 m +11.458750 1.099458 10.359408 0.000118 9.003304 0.000118 c +7.647198 0.000118 6.547857 1.099458 6.547857 2.455564 c +6.547857 3.811669 7.647198 4.911010 9.003304 4.911010 c +10.359408 4.911010 11.458750 3.811669 11.458750 2.455564 c +h +15.551160 0.000118 m +16.907265 0.000118 18.006607 1.099458 18.006607 2.455564 c +18.006607 3.811669 16.907265 4.911010 15.551160 4.911010 c +14.195054 4.911010 13.095714 3.811669 13.095714 2.455564 c +13.095714 1.099458 14.195054 0.000118 15.551160 0.000118 c +h +f* +n +Q + +endstream +endobj + +8 0 obj + 6594 +endobj + +9 0 obj + << /Length 10 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 1.000000 exch 1.000000 exch 1.000000 exch dup 0.885417 gt { exch pop exch pop exch pop dup 0.885417 sub 0.000000 mul 1.000000 add exch dup 0.885417 sub 0.000000 mul 1.000000 add exch dup 0.885417 sub 0.000000 mul 1.000000 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 1.000000 exch 1.000000 exch 1.000000 exch } if pop } +endstream +endobj + +10 0 obj + 336 +endobj + +11 0 obj + << /Length 12 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 1.000000 exch 1.000000 exch 1.000000 exch dup 0.703125 gt { exch pop exch pop exch pop dup 0.703125 sub 0.000000 mul 1.000000 add exch dup 0.703125 sub 0.000000 mul 1.000000 add exch dup 0.703125 sub 0.000000 mul 1.000000 add exch } if dup 0.927083 gt { exch pop exch pop exch pop 1.000000 exch 1.000000 exch 1.000000 exch } if pop } +endstream +endobj + +12 0 obj + 336 +endobj + +13 0 obj + << /Length 14 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 0.211765 exch 0.545098 exch 0.839216 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub 4.324034 mul 0.211765 add exch dup 0.000000 sub -0.215126 mul 0.545098 add exch dup 0.000000 sub -3.635630 mul 0.839216 add exch } if dup 0.182292 gt { exch pop exch pop exch pop dup 0.182292 sub -1.689030 mul 1.000000 add exch dup 0.182292 sub -1.424484 mul 0.505882 add exch dup 0.182292 sub 2.503021 mul 0.176471 add exch } if dup 0.375000 gt { exch pop exch pop exch pop dup 0.375000 sub -3.348607 mul 0.674510 add exch dup 0.375000 sub 2.377709 mul 0.231373 add exch dup 0.375000 sub -0.772755 mul 0.658824 add exch } if dup 0.572917 gt { exch pop exch pop exch pop dup 0.572917 sub 0.810860 mul 0.011765 add exch dup 0.572917 sub 0.289593 mul 0.701961 add exch dup 0.572917 sub 1.312820 mul 0.505882 add exch } if dup 0.776042 gt { exch pop exch pop exch pop dup 0.776042 sub 3.239398 mul 0.176471 add exch dup 0.776042 sub -2.013680 mul 0.760784 add exch dup 0.776042 sub -1.313270 mul 0.772549 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.901961 exch 0.309804 exch 0.478431 exch } if pop } +endstream +endobj + +14 0 obj + 1120 +endobj + +15 0 obj + << /Length 16 0 R + /Range [ 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 0.000000 exch dup 0.885417 gt { exch pop dup 0.885417 sub 8.727274 mul 0.000000 add exch } if dup 1.000000 gt { exch pop 1.000000 exch } if pop } +endstream +endobj + +16 0 obj + 148 +endobj + +17 0 obj + << /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources << /Pattern << /P1 << /Matrix [ -127.403831 248.798080 -248.798080 -127.403831 347.956787 70.673119 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceGray + /Function 15 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> >> >> + /Subtype /Form + /Length 18 0 R + /Group << /Type /Group + /S /Transparency + /CS /DeviceGray + >> + /Type /XObject + >> +stream +/DeviceGray CS +/DeviceGray cs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +/Pattern cs +/P1 scn +f +n + +endstream +endobj + +18 0 obj + 396 +endobj + +19 0 obj + << /Length 20 0 R + /Range [ 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 1.000000 exch dup 0.703125 gt { exch pop dup 0.703125 sub -4.465117 mul 1.000000 add exch } if dup 0.927083 gt { exch pop 0.000000 exch } if pop } +endstream +endobj + +20 0 obj + 149 +endobj + +21 0 obj + << /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources << /Pattern << /P1 << /Matrix [ 15.144229 -277.163422 277.163422 15.144229 -3.725922 294.591370 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceGray + /Function 19 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> >> >> + /Subtype /Form + /Length 22 0 R + /Group << /Type /Group + /S /Transparency + /CS /DeviceGray + >> + /Type /XObject + >> +stream +/DeviceGray CS +/DeviceGray cs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +/Pattern cs +/P1 scn +f +n + +endstream +endobj + +22 0 obj + 396 +endobj + +23 0 obj + << /Type /XObject + /Length 24 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << /Pattern << /P3 << /Matrix [ -127.403831 248.798080 -248.798080 -127.403831 347.956787 70.673119 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceRGB + /Function 9 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + /P2 << /Matrix [ 15.144229 -277.163422 277.163422 15.144229 -3.725922 294.591370 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceRGB + /Function 11 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + /P1 << /Matrix [ 190.384613 -179.807693 179.807693 190.384613 -122.380447 53.989655 ] + /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ] + /ColorSpace /DeviceRGB + /Function 13 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 2 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + >> + /ExtGState << /E4 << /SMask << /Type /Mask + /G 17 0 R + /S /Luminosity + >> + /Type /ExtGState + >> + /E2 << /ca 0.800000 >> + /E3 << /SMask << /Type /Mask + /G 21 0 R + /S /Luminosity + >> + /Type /ExtGState + >> + /E1 << /ca 0.100000 >> + >> + >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P1 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E2 gs +/E3 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P2 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E4 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P3 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q + +endstream +endobj + +24 0 obj + 1166 +endobj + +25 0 obj + << /Length 26 0 R + /Range [ 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 0.000000 exch dup 0.885417 gt { exch pop dup 0.885417 sub 8.727274 mul 0.000000 add exch } if dup 1.000000 gt { exch pop 1.000000 exch } if pop } +endstream +endobj + +26 0 obj + 148 +endobj + +27 0 obj + << /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources << /Pattern << /P1 << /Matrix [ -127.403831 248.798080 -248.798080 -127.403831 347.956787 70.673119 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceGray + /Function 25 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> >> >> + /Subtype /Form + /Length 28 0 R + /Group << /Type /Group + /S /Transparency + /CS /DeviceGray + >> + /Type /XObject + >> +stream +/DeviceGray CS +/DeviceGray cs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +/Pattern cs +/P1 scn +f +n + +endstream +endobj + +28 0 obj + 396 +endobj + +29 0 obj + << /Length 30 0 R + /Range [ 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 1.000000 exch dup 0.703125 gt { exch pop dup 0.703125 sub -4.465117 mul 1.000000 add exch } if dup 0.927083 gt { exch pop 0.000000 exch } if pop } +endstream +endobj + +30 0 obj + 149 +endobj + +31 0 obj + << /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources << /Pattern << /P1 << /Matrix [ 15.144229 -277.163422 277.163422 15.144229 -3.725922 294.591370 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceGray + /Function 29 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> >> >> + /Subtype /Form + /Length 32 0 R + /Group << /Type /Group + /S /Transparency + /CS /DeviceGray + >> + /Type /XObject + >> +stream +/DeviceGray CS +/DeviceGray cs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +/Pattern cs +/P1 scn +f +n + +endstream +endobj + +32 0 obj + 396 +endobj + +33 0 obj + << /Length 34 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 1.000000 exch 1.000000 exch 1.000000 exch dup 0.885417 gt { exch pop exch pop exch pop dup 0.885417 sub 0.000000 mul 1.000000 add exch dup 0.885417 sub 0.000000 mul 1.000000 add exch dup 0.885417 sub 0.000000 mul 1.000000 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 1.000000 exch 1.000000 exch 1.000000 exch } if pop } +endstream +endobj + +34 0 obj + 336 +endobj + +35 0 obj + << /Length 36 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 1.000000 exch 1.000000 exch 1.000000 exch dup 0.703125 gt { exch pop exch pop exch pop dup 0.703125 sub 0.000000 mul 1.000000 add exch dup 0.703125 sub 0.000000 mul 1.000000 add exch dup 0.703125 sub 0.000000 mul 1.000000 add exch } if dup 0.927083 gt { exch pop exch pop exch pop 1.000000 exch 1.000000 exch 1.000000 exch } if pop } +endstream +endobj + +36 0 obj + 336 +endobj + +37 0 obj + << /Length 38 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 0.211765 exch 0.545098 exch 0.839216 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub 4.324034 mul 0.211765 add exch dup 0.000000 sub -0.215126 mul 0.545098 add exch dup 0.000000 sub -3.635630 mul 0.839216 add exch } if dup 0.182292 gt { exch pop exch pop exch pop dup 0.182292 sub -1.689030 mul 1.000000 add exch dup 0.182292 sub -1.424484 mul 0.505882 add exch dup 0.182292 sub 2.503021 mul 0.176471 add exch } if dup 0.375000 gt { exch pop exch pop exch pop dup 0.375000 sub -3.348607 mul 0.674510 add exch dup 0.375000 sub 2.377709 mul 0.231373 add exch dup 0.375000 sub -0.772755 mul 0.658824 add exch } if dup 0.572917 gt { exch pop exch pop exch pop dup 0.572917 sub 0.810860 mul 0.011765 add exch dup 0.572917 sub 0.289593 mul 0.701961 add exch dup 0.572917 sub 1.312820 mul 0.505882 add exch } if dup 0.776042 gt { exch pop exch pop exch pop dup 0.776042 sub 3.239398 mul 0.176471 add exch dup 0.776042 sub -2.013680 mul 0.760784 add exch dup 0.776042 sub -1.313270 mul 0.772549 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.901961 exch 0.309804 exch 0.478431 exch } if pop } +endstream +endobj + +38 0 obj + 1120 +endobj + +39 0 obj + << /XObject << /X1 7 0 R >> + /ExtGState << /E5 << /SMask << /Type /Mask + /G 23 0 R + /S /Alpha + >> + /Type /ExtGState + >> + /E4 << /SMask << /Type /Mask + /G 27 0 R + /S /Luminosity + >> + /Type /ExtGState + >> + /E2 << /ca 0.800000 >> + /E3 << /SMask << /Type /Mask + /G 31 0 R + /S /Luminosity + >> + /Type /ExtGState + >> + /E1 << /ca 0.100000 >> + >> + /Pattern << /P3 << /Matrix [ -127.403831 248.798080 -248.798080 -127.403831 347.956787 70.673119 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceRGB + /Function 33 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + /P2 << /Matrix [ 15.144229 -277.163422 277.163422 15.144229 -3.725922 294.591370 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceRGB + /Function 35 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + /P1 << /Matrix [ 190.384613 -179.807693 179.807693 190.384613 -122.380447 53.989655 ] + /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ] + /ColorSpace /DeviceRGB + /Function 37 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 2 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + >> + >> +endobj + +40 0 obj + << /Length 41 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +0.956863 0.964706 0.980392 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P1 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E2 gs +/E3 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P2 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E4 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P3 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E5 gs +/X1 Do +Q + +endstream +endobj + +41 0 obj + 1565 +endobj + +42 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources 39 0 R + /Contents 40 0 R + /Parent 43 0 R + >> +endobj + +43 0 obj + << /Kids [ 42 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +44 0 obj + << /Pages 43 0 R + /Type /Catalog + >> +endobj + +xref +0 45 +0000000000 65535 f +0000000010 00000 n +0000001142 00000 n +0000001164 00000 n +0000002310 00000 n +0000002332 00000 n +0000003480 00000 n +0000003502 00000 n +0000010866 00000 n +0000010889 00000 n +0000011410 00000 n +0000011433 00000 n +0000011955 00000 n +0000011978 00000 n +0000013284 00000 n +0000013308 00000 n +0000013606 00000 n +0000013629 00000 n +0000015078 00000 n +0000015101 00000 n +0000015400 00000 n +0000015423 00000 n +0000016868 00000 n +0000016891 00000 n +0000021481 00000 n +0000021505 00000 n +0000021803 00000 n +0000021826 00000 n +0000023275 00000 n +0000023298 00000 n +0000023597 00000 n +0000023620 00000 n +0000025065 00000 n +0000025088 00000 n +0000025610 00000 n +0000025633 00000 n +0000026155 00000 n +0000026178 00000 n +0000027484 00000 n +0000027508 00000 n +0000030295 00000 n +0000031918 00000 n +0000031942 00000 n +0000032121 00000 n +0000032197 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 44 0 R + /Size 45 +>> +startxref +32258 +%%EOF \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1Dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1Dark.imageset/Contents.json new file mode 100644 index 000000000..5bb2cdab2 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1Dark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingSplashScreenPage1-Dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1Dark.imageset/OnboardingSplashScreenPage1-Dark.pdf b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1Dark.imageset/OnboardingSplashScreenPage1-Dark.pdf new file mode 100644 index 000000000..be0081499 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage1Dark.imageset/OnboardingSplashScreenPage1-Dark.pdf @@ -0,0 +1,872 @@ +%PDF-1.7 + +1 0 obj + << /Type /XObject + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +q +1.000000 0.000000 -0.000000 1.000000 116.000000 142.000000 cm +0.083333 0.083333 0.083333 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +87.500000 13.000000 l +91.089851 13.000000 94.000000 10.089850 94.000000 6.500000 c +94.000000 6.500000 l +94.000000 2.910150 91.089851 0.000000 87.500000 0.000000 c +6.500002 0.000000 l +2.910151 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 116.000000 142.000000 cm +1.000000 0.505882 0.176471 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +87.500000 13.000000 l +91.089851 13.000000 94.000000 10.089850 94.000000 6.500000 c +94.000000 6.500000 l +94.000000 2.910150 91.089851 0.000000 87.500000 0.000000 c +6.500002 0.000000 l +2.910151 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q + +endstream +endobj + +2 0 obj + 882 +endobj + +3 0 obj + << /Type /XObject + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +q +1.000000 0.000000 -0.000000 1.000000 127.119080 85.000000 cm +0.083333 0.083333 0.083333 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +119.500000 13.000000 l +123.089851 13.000000 126.000000 10.089850 126.000000 6.500000 c +126.000000 6.500000 l +126.000000 2.910150 123.089851 0.000000 119.500000 0.000000 c +6.499998 0.000000 l +2.910147 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 127.119080 85.000000 cm +0.454902 0.819608 0.172549 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +119.500000 13.000000 l +123.089851 13.000000 126.000000 10.089850 126.000000 6.500000 c +126.000000 6.500000 l +126.000000 2.910150 123.089851 0.000000 119.500000 0.000000 c +6.499998 0.000000 l +2.910147 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 896 +endobj + +5 0 obj + << /Type /XObject + /Length 6 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +q +1.000000 0.000000 -0.000000 1.000000 102.000000 207.000000 cm +0.083333 0.083333 0.083333 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +119.500000 13.000000 l +123.089851 13.000000 126.000000 10.089850 126.000000 6.500000 c +126.000000 6.500000 l +126.000000 2.910150 123.089851 0.000000 119.500000 0.000000 c +6.499998 0.000000 l +2.910147 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 102.000000 207.000000 cm +0.360784 0.337255 0.960784 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +119.500000 13.000000 l +123.089851 13.000000 126.000000 10.089850 126.000000 6.500000 c +126.000000 6.500000 l +126.000000 2.910150 123.089851 0.000000 119.500000 0.000000 c +6.499998 0.000000 l +2.910147 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q + +endstream +endobj + +6 0 obj + 898 +endobj + +7 0 obj + << /Type /XObject + /Length 8 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << /XObject << /X2 1 0 R + /X3 3 0 R + /X1 5 0 R + >> + /ExtGState << /E6 << /ca 0.400000 >> + /E4 << /ca 0.500000 >> + /E2 << /ca 0.500000 >> + /E5 << /ca 0.700000 >> + /E3 << /ca 0.700000 >> + /E1 << /ca 0.700000 >> + >> + >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 51.000000 183.000000 cm +0.054167 0.054167 0.054167 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 51.000000 183.000000 cm +0.835294 0.847059 0.968627 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E2 gs +/X1 Do +Q +q +1.000000 0.000000 -0.000000 1.000000 102.000000 184.000000 cm +0.054167 0.054167 0.054167 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +141.500000 13.000000 l +145.089859 13.000000 148.000000 10.089850 148.000000 6.500000 c +148.000000 6.500000 l +148.000000 2.910150 145.089844 0.000000 141.500000 0.000000 c +6.499999 0.000000 l +2.910148 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 102.000000 184.000000 cm +0.556863 0.600000 0.643137 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +141.500000 13.000000 l +145.089859 13.000000 148.000000 10.089850 148.000000 6.500000 c +148.000000 6.500000 l +148.000000 2.910150 145.089844 0.000000 141.500000 0.000000 c +6.499999 0.000000 l +2.910148 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 60.000000 119.000000 cm +0.054167 0.054167 0.054167 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E3 gs +1.000000 0.000000 -0.000000 1.000000 60.000000 119.000000 cm +0.964706 0.878431 0.811765 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E4 gs +/X2 Do +Q +q +1.000000 0.000000 -0.000000 1.000000 116.000000 121.000000 cm +0.054167 0.054167 0.054167 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +56.500000 13.000000 l +60.089848 13.000000 63.000000 10.089850 63.000000 6.500000 c +63.000000 6.500000 l +63.000000 2.910150 60.089851 0.000000 56.500000 0.000000 c +6.500001 0.000000 l +2.910151 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 116.000000 121.000000 cm +0.556863 0.600000 0.643137 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +56.500000 13.000000 l +60.089848 13.000000 63.000000 10.089850 63.000000 6.500000 c +63.000000 6.500000 l +63.000000 2.910150 60.089851 0.000000 56.500000 0.000000 c +6.500001 0.000000 l +2.910151 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 189.000000 121.000000 cm +0.054167 0.054167 0.054167 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +57.500000 13.000000 l +61.089851 13.000000 64.000000 10.089850 64.000000 6.500000 c +64.000000 6.500000 l +64.000000 2.910150 61.089851 0.000000 57.500000 0.000000 c +6.500000 0.000000 l +2.910149 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 189.000000 121.000000 cm +0.556863 0.600000 0.643137 scn +0.000000 6.500000 m +0.000000 10.089850 2.910149 13.000000 6.500000 13.000000 c +57.500000 13.000000 l +61.089851 13.000000 64.000000 10.089850 64.000000 6.500000 c +64.000000 6.500000 l +64.000000 2.910150 61.089851 0.000000 57.500000 0.000000 c +6.500000 0.000000 l +2.910149 0.000000 0.000000 2.910150 0.000000 6.500000 c +0.000000 6.500000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 69.999939 60.000000 cm +1.000000 1.000000 1.000000 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 69.999939 60.000000 cm +0.054167 0.054167 0.054167 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E5 gs +1.000000 0.000000 -0.000000 1.000000 69.999939 60.000000 cm +0.894118 0.964706 0.894118 scn +38.000000 19.000000 m +38.000000 8.506590 29.493410 0.000000 19.000000 0.000000 c +8.506590 0.000000 0.000000 8.506590 0.000000 19.000000 c +0.000000 29.493410 8.506590 38.000000 19.000000 38.000000 c +29.493410 38.000000 38.000000 29.493410 38.000000 19.000000 c +h +f +n +Q +q +/E6 gs +/X3 Do +Q +q +1.000000 0.000000 -0.000000 1.000000 115.999939 72.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 9.000000 m +0.000000 13.970562 4.029438 18.000000 9.000000 18.000000 c +23.000000 18.000000 l +27.970562 18.000000 32.000000 13.970562 32.000000 9.000000 c +32.000000 9.000000 l +32.000000 4.029437 27.970562 0.000000 23.000000 0.000000 c +9.000000 0.000000 l +4.029438 0.000000 0.000000 4.029437 0.000000 9.000000 c +0.000000 9.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 123.366089 77.722778 cm +0.557143 0.600000 0.642857 scn +4.910892 3.273924 m +4.910892 1.917818 3.811552 0.818478 2.455446 0.818478 c +1.099341 0.818478 0.000000 1.917818 0.000000 3.273924 c +0.000000 4.630030 1.099341 5.729370 2.455446 5.729370 c +3.811552 5.729370 4.910892 4.630030 4.910892 3.273924 c +h +11.458750 2.455442 m +11.458750 1.099336 10.359408 -0.000004 9.003304 -0.000004 c +7.647198 -0.000004 6.547857 1.099336 6.547857 2.455442 c +6.547857 3.811547 7.647198 4.910888 9.003304 4.910888 c +10.359408 4.910888 11.458750 3.811547 11.458750 2.455442 c +h +15.551160 -0.000004 m +16.907265 -0.000004 18.006607 1.099336 18.006607 2.455442 c +18.006607 3.811547 16.907265 4.910888 15.551160 4.910888 c +14.195054 4.910888 13.095714 3.811547 13.095714 2.455442 c +13.095714 1.099336 14.195054 -0.000004 15.551160 -0.000004 c +h +f* +n +Q + +endstream +endobj + +8 0 obj + 6601 +endobj + +9 0 obj + << /Length 10 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 0.890196 exch 0.909804 exch 0.941176 exch dup 0.885417 gt { exch pop exch pop exch pop dup 0.885417 sub -1.163636 mul 0.890196 add exch dup 0.885417 sub -1.163636 mul 0.909804 add exch dup 0.885417 sub -1.197861 mul 0.941176 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.756863 exch 0.776471 exch 0.803922 exch } if pop } +endstream +endobj + +10 0 obj + 339 +endobj + +11 0 obj + << /Length 12 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 0.560784 exch 0.592157 exch 0.639216 exch dup 0.436121 gt { exch pop exch pop exch pop dup 0.436121 sub -1.132947 mul 0.560784 add exch dup 0.436121 sub -1.040270 mul 0.592157 add exch dup 0.436121 sub -1.080743 mul 0.639216 add exch } if dup 0.797508 gt { exch pop exch pop exch pop 0.151351 exch 0.216216 exch 0.248649 exch } if pop } +endstream +endobj + +12 0 obj + 339 +endobj + +13 0 obj + << /Length 14 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 0.211765 exch 0.545098 exch 0.839216 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub 4.324034 mul 0.211765 add exch dup 0.000000 sub -0.215126 mul 0.545098 add exch dup 0.000000 sub -3.635630 mul 0.839216 add exch } if dup 0.182292 gt { exch pop exch pop exch pop dup 0.182292 sub -0.472174 mul 1.000000 add exch dup 0.182292 sub -0.944349 mul 0.505882 add exch dup 0.182292 sub 1.454296 mul 0.176471 add exch } if dup 0.389925 gt { exch pop exch pop exch pop dup 0.389925 sub -4.864687 mul 0.901961 add exch dup 0.389925 sub 2.143034 mul 0.309804 add exch dup 0.389925 sub 0.150013 mul 0.478431 add exch } if dup 0.572917 gt { exch pop exch pop exch pop dup 0.572917 sub 0.810860 mul 0.011765 add exch dup 0.572917 sub 0.289593 mul 0.701961 add exch dup 0.572917 sub 1.312820 mul 0.505882 add exch } if dup 0.776042 gt { exch pop exch pop exch pop dup 0.776042 sub 2.223803 mul 0.176471 add exch dup 0.776042 sub -2.363885 mul 0.760784 add exch dup 0.776042 sub -0.507798 mul 0.772549 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.674510 exch 0.231373 exch 0.658824 exch } if pop } +endstream +endobj + +14 0 obj + 1119 +endobj + +15 0 obj + << /Length 16 0 R + /Range [ 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 0.000000 exch dup 0.885417 gt { exch pop dup 0.885417 sub 8.727274 mul 0.000000 add exch } if dup 1.000000 gt { exch pop 1.000000 exch } if pop } +endstream +endobj + +16 0 obj + 148 +endobj + +17 0 obj + << /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources << /Pattern << /P1 << /Matrix [ -127.403831 248.798080 -248.798080 -127.403831 347.956726 70.673119 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceGray + /Function 15 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> >> >> + /Subtype /Form + /Length 18 0 R + /Group << /Type /Group + /S /Transparency + /CS /DeviceGray + >> + /Type /XObject + >> +stream +/DeviceGray CS +/DeviceGray cs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +/Pattern cs +/P1 scn +f +n + +endstream +endobj + +18 0 obj + 396 +endobj + +19 0 obj + << /Type /XObject + /Length 20 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << /Pattern << /P3 << /Matrix [ -127.403831 248.798080 -248.798080 -127.403831 347.956726 70.673119 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceRGB + /Function 9 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + /P2 << /Matrix [ 105.999977 -284.000061 361.623688 134.972168 -115.811859 256.513977 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceRGB + /Function 11 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + /P1 << /Matrix [ 256.865387 -254.788467 254.788467 256.865387 -268.954285 56.983734 ] + /Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ] + /ColorSpace /DeviceRGB + /Function 13 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 2 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> + >> + /ExtGState << /E4 << /SMask << /Type /Mask + /G 17 0 R + /S /Luminosity + >> + /Type /ExtGState + >> + /E2 << /ca 0.500000 >> + /E3 << /ca 0.400000 >> + /E1 << /ca 0.200000 >> + >> + >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +q +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +0.098039 0.113725 0.129412 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P1 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E2 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P2 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q +q +/E3 gs +/E4 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P3 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q + +endstream +endobj + +20 0 obj + 1519 +endobj + +21 0 obj + << /Length 22 0 R + /Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 1.000000 exch 1.000000 exch 1.000000 exch dup 0.258514 gt { exch pop exch pop exch pop dup 0.258514 sub 0.000000 mul 1.000000 add exch dup 0.258514 sub 0.000000 mul 1.000000 add exch dup 0.258514 sub 0.000000 mul 1.000000 add exch } if dup 0.942462 gt { exch pop exch pop exch pop 1.000000 exch 1.000000 exch 1.000000 exch } if pop } +endstream +endobj + +22 0 obj + 336 +endobj + +23 0 obj + << /Length 24 0 R + /Range [ 0.000000 1.000000 ] + /Domain [ 0.000000 1.000000 ] + /FunctionType 4 + >> +stream +{ 1.000000 exch dup 0.258514 gt { exch pop dup 0.258514 sub -1.462099 mul 1.000000 add exch } if dup 0.942462 gt { exch pop 0.000000 exch } if pop } +endstream +endobj + +24 0 obj + 149 +endobj + +25 0 obj + << /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources << /Pattern << /P1 << /Matrix [ 18.000000 -296.999969 296.999969 18.000000 -16.499969 313.000000 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceGray + /Function 23 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> >> >> + /Subtype /Form + /Length 26 0 R + /Group << /Type /Group + /S /Transparency + /CS /DeviceGray + >> + /Type /XObject + >> +stream +/DeviceGray CS +/DeviceGray cs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +/Pattern cs +/P1 scn +f +n + +endstream +endobj + +26 0 obj + 396 +endobj + +27 0 obj + << /Type /XObject + /Length 28 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << /Pattern << /P1 << /Matrix [ 18.000000 -296.999969 296.999969 18.000000 -16.499969 313.000000 ] + /Shading << /Coords [ 0.500000 0.500000 0.000000 0.500000 0.500000 0.500000 ] + /ColorSpace /DeviceRGB + /Function 21 0 R + /Domain [ 0.000000 1.000000 ] + /ShadingType 3 + /Extend [ true true ] + >> + /PatternType 2 + /Type /Pattern + >> >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 25 0 R + /S /Luminosity + >> + /Type /ExtGState + >> >> + >> + /BBox [ 0.000000 0.000000 300.000000 300.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +1.000000 0.000000 -0.000000 1.000000 25.000000 25.000000 cm +/Pattern cs +/P1 scn +250.000000 125.000000 m +250.000000 55.964401 194.035599 0.000000 125.000000 0.000000 c +55.964405 0.000000 0.000000 55.964401 0.000000 125.000000 c +0.000000 194.035599 55.964405 250.000000 125.000000 250.000000 c +194.035599 250.000000 250.000000 194.035599 250.000000 125.000000 c +h +f +n +Q + +endstream +endobj + +28 0 obj + 405 +endobj + +29 0 obj + << /XObject << /X2 7 0 R + /X1 19 0 R + >> + /ExtGState << /E2 << /SMask << /Type /Mask + /G 27 0 R + /S /Alpha + >> + /Type /ExtGState + >> + /E1 << /ca 0.600000 >> + >> + >> +endobj + +30 0 obj + << /Length 31 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q +q +/E2 gs +/X2 Do +Q + +endstream +endobj + +31 0 obj + 64 +endobj + +32 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources 29 0 R + /Contents 30 0 R + /Parent 33 0 R + >> +endobj + +33 0 obj + << /Kids [ 32 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +34 0 obj + << /Pages 33 0 R + /Type /Catalog + >> +endobj + +xref +0 35 +0000000000 65535 f +0000000010 00000 n +0000001142 00000 n +0000001164 00000 n +0000002310 00000 n +0000002332 00000 n +0000003480 00000 n +0000003502 00000 n +0000010873 00000 n +0000010896 00000 n +0000011420 00000 n +0000011443 00000 n +0000011968 00000 n +0000011991 00000 n +0000013296 00000 n +0000013320 00000 n +0000013618 00000 n +0000013641 00000 n +0000015090 00000 n +0000015113 00000 n +0000019782 00000 n +0000019806 00000 n +0000020328 00000 n +0000020351 00000 n +0000020650 00000 n +0000020673 00000 n +0000022119 00000 n +0000022142 00000 n +0000023918 00000 n +0000023941 00000 n +0000024341 00000 n +0000024463 00000 n +0000024485 00000 n +0000024664 00000 n +0000024740 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 34 0 R + /Size 35 +>> +startxref +24801 +%%EOF \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2.imageset/Contents.json new file mode 100644 index 000000000..2d4d81978 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingSplashScreenPage2.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2.imageset/OnboardingSplashScreenPage2.pdf b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2.imageset/OnboardingSplashScreenPage2.pdf new file mode 100644 index 000000000..283dbcc94 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2.imageset/OnboardingSplashScreenPage2.pdf differ diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2Dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2Dark.imageset/Contents.json new file mode 100644 index 000000000..f48d39559 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2Dark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingSplashScreenPage2-Dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2Dark.imageset/OnboardingSplashScreenPage2-Dark.pdf b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2Dark.imageset/OnboardingSplashScreenPage2-Dark.pdf new file mode 100644 index 000000000..9c1d4b260 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage2Dark.imageset/OnboardingSplashScreenPage2-Dark.pdf differ diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3.imageset/Contents.json new file mode 100644 index 000000000..18c1506a2 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingSplashScreenPage3.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3.imageset/OnboardingSplashScreenPage3.pdf b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3.imageset/OnboardingSplashScreenPage3.pdf new file mode 100644 index 000000000..7c1c6dc76 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3.imageset/OnboardingSplashScreenPage3.pdf @@ -0,0 +1,1451 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 84.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 94.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 70.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 70.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 66.000000 70.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 178.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 42.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 42.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.000000 42.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000000 210.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000000 182.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000000 154.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000000 42.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 42.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 70.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 234.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 98.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 98.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 262.000000 98.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 98.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 98.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 98.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 126.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 126.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 126.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 70.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 70.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 262.000000 70.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 42.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 42.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 42.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 122.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 234.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 234.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 122.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 238.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 238.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 122.000000 238.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 42.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 42.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 66.000000 42.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 178.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000000 238.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 238.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 206.000000 238.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 206.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 66.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 70.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 70.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.000000 70.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 238.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 238.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 38.000000 238.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 122.000000 14.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 94.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 210.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 182.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 182.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 210.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 154.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 126.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 42.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 70.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 238.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 126.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 42.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 70.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 266.000000 cm +0.854902 0.850980 0.992157 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 154.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q + +endstream +endobj + +3 0 obj + 37227 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 300.000000 300.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000037317 00000 n +0000037341 00000 n +0000037516 00000 n +0000037590 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +37649 +%%EOF \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3Dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3Dark.imageset/Contents.json new file mode 100644 index 000000000..25ceecb9f --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3Dark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingSplashScreenPage3-Dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3Dark.imageset/OnboardingSplashScreenPage3-Dark.pdf b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3Dark.imageset/OnboardingSplashScreenPage3-Dark.pdf new file mode 100644 index 000000000..ef2dcdbc8 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage3Dark.imageset/OnboardingSplashScreenPage3-Dark.pdf @@ -0,0 +1,1451 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 84.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 94.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 55.999996 70.000122 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 70.000122 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 66.000000 70.000122 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 178.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 41.999878 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 41.999878 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.000000 41.999878 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000015 210.000061 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000015 182.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000015 154.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 223.999985 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000015 41.999878 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 223.999985 41.999878 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 111.999992 70.000122 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 182.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 182.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.000000 182.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 223.999985 210.000061 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 251.999985 210.000061 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 233.999985 210.000061 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 154.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 154.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 154.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 98.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 98.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 262.000000 98.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 98.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 98.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 98.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 126.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 126.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 126.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 70.000122 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 70.000122 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 262.000000 70.000122 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 41.999878 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 41.999878 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 41.999878 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 28.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +28.000000 20.000000 l +28.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 111.999992 154.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 154.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 121.999992 154.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 223.999985 182.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 182.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 233.999985 182.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 223.999985 154.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 154.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 233.999985 154.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 111.999992 210.000061 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 210.000061 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 121.999992 210.000061 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 111.999992 237.999939 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 237.999939 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 121.999992 237.999939 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 55.999996 41.999878 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 112.000000 41.999878 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 66.000000 41.999878 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.000000 126.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 178.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000015 237.999939 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 237.999939 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 206.000015 237.999939 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 196.000015 14.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 14.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 206.000015 14.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 14.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 14.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 14.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 55.999996 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 66.000000 98.000000 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 70.000122 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 224.000000 70.000122 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 150.000000 70.000122 cm +0.049479 0.742188 0.545395 scn +0.000000 20.000000 m +84.000000 20.000000 l +84.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 27.999998 237.999939 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 237.999939 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 38.000000 237.999939 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 182.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 182.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 182.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 210.000061 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 56.000000 210.000061 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 10.000000 210.000061 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 111.999992 14.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 168.000000 14.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 121.999992 14.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 140.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 94.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +0.000000 20.000000 m +56.000000 20.000000 l +56.000000 0.000000 l +0.000000 0.000000 l +0.000000 20.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 210.000061 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 182.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 111.999992 182.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 210.000061 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 55.999996 154.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 126.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 252.000000 41.999878 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 70.000122 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 0.000000 237.999939 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 55.999996 266.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 126.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 41.999878 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 27.999998 70.000122 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 280.000000 266.000000 cm +0.223529 0.250980 0.286275 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 84.000000 154.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 55.999996 126.000000 cm +0.049479 0.742188 0.545395 scn +20.000000 10.000000 m +20.000000 4.477153 15.522847 0.000000 10.000000 0.000000 c +4.477152 0.000000 0.000000 4.477153 0.000000 10.000000 c +0.000000 15.522848 4.477152 20.000000 10.000000 20.000000 c +15.522847 20.000000 20.000000 15.522848 20.000000 10.000000 c +h +f +n +Q + +endstream +endobj + +3 0 obj + 37227 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 300.000031 300.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000037317 00000 n +0000037341 00000 n +0000037516 00000 n +0000037590 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +37649 +%%EOF \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4.imageset/Contents.json new file mode 100644 index 000000000..f5374ef56 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingSplashScreenPage4.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4.imageset/OnboardingSplashScreenPage4.pdf b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4.imageset/OnboardingSplashScreenPage4.pdf new file mode 100644 index 000000000..aac8e76c0 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4.imageset/OnboardingSplashScreenPage4.pdf differ diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4Dark.imageset/Contents.json b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4Dark.imageset/Contents.json new file mode 100644 index 000000000..70a47dd70 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4Dark.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingSplashScreenPage4-Dark.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4Dark.imageset/OnboardingSplashScreenPage4-Dark.pdf b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4Dark.imageset/OnboardingSplashScreenPage4-Dark.pdf new file mode 100644 index 000000000..05a60bf8e Binary files /dev/null and b/Riot/Assets/Images.xcassets/Onboarding/OnboardingSplashScreenPage4Dark.imageset/OnboardingSplashScreenPage4-Dark.pdf differ diff --git a/Riot/Assets/Images.xcassets/Room/Actions/action_location.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Actions/action_location.imageset/Contents.json index 5bb98bf57..3007f6fc5 100644 --- a/Riot/Assets/Images.xcassets/Room/Actions/action_location.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/Actions/action_location.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/Actions/action_poll.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Actions/action_poll.imageset/Contents.json index 7c6421fc2..2839a73cc 100644 --- a/Riot/Assets/Images.xcassets/Room/Actions/action_poll.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/Actions/action_poll.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/ContextMenu/Contents.json b/Riot/Assets/Images.xcassets/Room/ContextMenu/Contents.json index da4a164c9..73c00596a 100644 --- a/Riot/Assets/Images.xcassets/Room/ContextMenu/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/ContextMenu/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Contents.json new file mode 100644 index 000000000..d2c033d2d --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Thread.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Thread@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Thread@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread.png new file mode 100644 index 000000000..632a63307 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread.png differ diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread@2x.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread@2x.png new file mode 100644 index 000000000..dcd35cf62 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread@3x.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread@3x.png new file mode 100644 index 000000000..32d13c5db Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_thread.imageset/Thread@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Location/location_marker_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Location/location_marker_icon.imageset/Contents.json index 707b2f06b..0bf02ac7f 100644 --- a/Riot/Assets/Images.xcassets/Room/Location/location_marker_icon.imageset/Contents.json +++ b/Riot/Assets/Images.xcassets/Room/Location/location_marker_icon.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/Polls/poll_type_checkbox_default.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/Contents.json new file mode 100644 index 000000000..945c5c337 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "poll_type_checkbox_default.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "poll_type_checkbox_default@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "poll_type_checkbox_default@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default.png b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default.png new file mode 100644 index 000000000..84e419079 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default@2x.png b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default@2x.png new file mode 100644 index 000000000..7e6083bc3 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default@3x.png b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default@3x.png new file mode 100644 index 000000000..316a8eab7 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_default.imageset/poll_type_checkbox_default@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/Contents.json new file mode 100644 index 000000000..b7fbce06b --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "poll_type_checkbox_selected.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "poll_type_checkbox_selected@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "poll_type_checkbox_selected@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected.png b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected.png new file mode 100644 index 000000000..6a744d6be Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected@2x.png b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected@2x.png new file mode 100644 index 000000000..67c3bbd64 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected@3x.png b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected@3x.png new file mode 100644 index 000000000..a4cd21452 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Polls/poll_type_checkbox_selected.imageset/poll_type_checkbox_selected@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Threads/Contents.json b/Riot/Assets/Images.xcassets/Room/Threads/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Threads/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/Contents.json new file mode 100644 index 000000000..7c7666862 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "filter_list_black_24dp 1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "filter_list_black_24dp 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "filter_list_black_24dp 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1.png b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1.png new file mode 100644 index 000000000..7710fa90e Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1@2x.png b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1@2x.png new file mode 100644 index 000000000..b9cff9c1b Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1@3x.png b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1@3x.png new file mode 100644 index 000000000..5fec0029d Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter.imageset/filter_list_black_24dp 1@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_filter_applied.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter_applied.imageset/Contents.json new file mode 100644 index 000000000..5d848cb50 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter_applied.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "filter-on.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_filter_applied.imageset/filter-on.svg b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter_applied.imageset/filter-on.svg new file mode 100644 index 000000000..26b34d0e2 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Threads/threads_filter_applied.imageset/filter-on.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Contents.json new file mode 100644 index 000000000..92eec1362 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "Thread.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Thread@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Thread@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread.png b/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread.png new file mode 100644 index 000000000..21add0227 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread@2x.png b/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread@2x.png new file mode 100644 index 000000000..e5c85a57f Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread@3x.png b/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread@3x.png new file mode 100644 index 000000000..2204e1ccf Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/Threads/threads_icon.imageset/Thread@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/Contents.json new file mode 100644 index 000000000..1b8f71d8d --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "file_attachment.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "file_attachment@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "file_attachment@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment.png b/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment.png new file mode 100644 index 000000000..cbefc09be Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment.png differ diff --git a/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment@2x.png b/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment@2x.png new file mode 100644 index 000000000..0ded843f2 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment@3x.png b/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment@3x.png new file mode 100644 index 000000000..d2d626082 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/file_attachment.imageset/file_attachment@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Contents.json new file mode 100644 index 000000000..b5cd56ac8 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "Link.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Link@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Link@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link.png b/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link.png new file mode 100644 index 000000000..546ec9f08 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link.png differ diff --git a/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link@2x.png b/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link@2x.png new file mode 100644 index 000000000..85c2c1bce Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link@3x.png b/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link@3x.png new file mode 100644 index 000000000..9e44e1899 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Room/link_icon.imageset/Link@3x.png differ diff --git a/Riot/Assets/cs.lproj/InfoPlist.strings b/Riot/Assets/cs.lproj/InfoPlist.strings new file mode 100644 index 000000000..88c506408 --- /dev/null +++ b/Riot/Assets/cs.lproj/InfoPlist.strings @@ -0,0 +1,7 @@ + + +// Permissions usage explanations +"NSCameraUsageDescription" = "Kamera slouží k focení, zachycení videí a k videohovorům."; +"NSPhotoLibraryUsageDescription" = "Galerie se používá k posílání obrázků a videí."; +"NSCalendarsUsageDescription" = "Zobrazuje události v aplikaci."; +"NSFaceIDUsageDescription" = "Face ID se používá k přístupu do aplikace."; diff --git a/Riot/Assets/cs.lproj/Localizable.strings b/Riot/Assets/cs.lproj/Localizable.strings index 8b1378917..15e6ddbbf 100644 --- a/Riot/Assets/cs.lproj/Localizable.strings +++ b/Riot/Assets/cs.lproj/Localizable.strings @@ -1 +1,170 @@ + + +/** Reactions **/ + +/* A user has reacted to a message, including the reaction e.g. "Alice reacted 👍". */ +"REACTION_FROM_USER" = "%@ reagoval/a %@"; + +/* Look, stuff's happened, alright? Just open the app. */ +"MSGS_IN_TWO_PLUS_ROOMS" = "%@ nových zpráv v %@, %@ a dalších"; + +/* Multiple messages in two rooms */ +"MSGS_IN_TWO_ROOMS" = "%@ nových zpráv v %@ a %@"; + +/* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */ +"MSGS_FROM_TWO_PLUS_USERS" = "%@ nových zpráv od %@, %@ a dalších"; + +/* Multiple unread messages from three people */ +"MSGS_FROM_THREE_USERS" = "%@ nových zpráv od %@, %@ a %@"; + +/* Multiple unread messages from two people */ +"MSGS_FROM_TWO_USERS" = "%@ nových zpráv od %@ a %@"; + +/* Multiple unread messages from a specific person, not referencing a room */ +"MSGS_FROM_USER" = "%@ nové zprávy od %@"; + +/** Coalesced messages **/ + +/* Multiple unread messages in a room */ +"UNREAD_IN_ROOM" = "%@ nové zprávy v %@"; + +/* New message with hidden content due to PIN enabled */ +"MESSAGE_PROTECTED" = "Nová zpráva"; + +/* New message indicator on a room */ +"MESSAGE_IN_X" = "Zpráva v %@"; + +/* New message indicator from a DM */ +"MESSAGE_FROM_X" = "Zpráva od %@"; + +/** Notification messages **/ + +/* New message indicator on unknown room */ +"MESSAGE" = "Zpráva"; + +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ poslal/a nálepku"; + +/* A single unread message */ +"SINGLE_UNREAD" = "Dostali jste zprávu"; + +/* A single unread message in a room */ +"SINGLE_UNREAD_IN_ROOM" = "V %@ vám přišla zpráva"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ sdílel/a svou polohu"; + +/** Single, unencrypted messages (where we can include the content */ + +/* New message from a specific person, not referencing a room. Content included. */ +"MSG_FROM_USER_WITH_CONTENT" = "%@: %@"; + +/** Key verification **/ + +"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ žádá o ověření"; + +/* Group call from user, CallKit caller name */ +"GROUP_CALL_FROM_USER" = "%@ (Skupinový hovor)"; + +/* A user added a Jitsi call to a room */ +"GROUP_CALL_STARTED" = "Začal skupinový hovor"; + +/* Incoming named video conference invite from a specific person */ +"VIDEO_CONF_NAMED_FROM_USER" = "Skupinový videohovor od %@: '%@'"; + +/* Incoming named voice conference invite from a specific person */ +"VOICE_CONF_NAMED_FROM_USER" = "Skupinový hovor od %@: '%@'"; + +/* Incoming unnamed video conference invite from a specific person */ +"VIDEO_CONF_FROM_USER" = "Skupinový videohovor od %@"; + +/* Incoming unnamed voice conference invite from a specific person */ +"VOICE_CONF_FROM_USER" = "Skupinový hovor od %@"; + +/* Incoming one-to-one video call */ +"VIDEO_CALL_FROM_USER" = "Videohovor od %@"; + +/** Calls **/ + +/* Incoming one-to-one voice call */ +"VOICE_CALL_FROM_USER" = "Hovor od %@"; + +/* A user's membership has updated in an unknown way */ +"USER_MEMBERSHIP_UPDATED" = "%@ aktualizoval/a svůj profil"; + +/* A user has change their avatar */ +"USER_UPDATED_AVATAR" = "%@ změnil/a svůj avatar"; + +/* A user has change their name to a new name which we don't know */ +"GENERIC_USER_UPDATED_DISPLAYNAME" = "%@ změnil/a svůj alias"; + +/** Membership Updates **/ + +/* A user has change their name to a new name */ +"USER_UPDATED_DISPLAYNAME" = "%@ změnil/a svůj alias na %@"; + +/* A user has invited you to a named room */ +"USER_INVITE_TO_NAMED_ROOM" = "%@ vás pozval/a do %@"; + +/* A user has invited you to an (unamed) group chat */ +"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ vás pozval/a ke skupinové konverzaci"; + +/** Invites **/ + +/* A user has invited you to a chat */ +"USER_INVITE_TO_CHAT" = "%@ vás pozval/a ke konverzaci"; + +/* A user has reacted to a message, but the reaction content is unknown */ +"GENERIC_REACTION_FROM_USER" = "%@ poslal/a reakci"; + +/* New message from a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM" = "%@ něco zveřejnil v %@"; + +/* New file message from a specific person, not referencing a room. */ +"FILE_FROM_USER" = "%@ poslal/a soubor %@"; + +/* New voice message from a specific person, not referencing a room. */ +"VOICE_MESSAGE_FROM_USER" = "%@ poslal/a zvukový soubor"; + +/* New audio message from a specific person, not referencing a room. */ +"AUDIO_FROM_USER" = "%@ poslal/a zvukový soubor %@"; + +/* New video message from a specific person, not referencing a room. */ +"VIDEO_FROM_USER" = "%@ poslal/a video"; + +/* New image message from a specific person in a named room. */ +"IMAGE_FROM_USER_IN_ROOM" = "%@ zveřejnil/a obrázek %@ v %@"; + +/** Media Messages **/ + +/* New image message from a specific person, not referencing a room. */ +"PICTURE_FROM_USER" = "%@ poslal/a obrázek"; + +/* New action message from a specific person in a named room. */ +"ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; + +/* New action message from a specific person, not referencing a room. */ +"ACTION_FROM_USER" = "* %@ %@"; + +/* New message from a specific person in a named room. Content included. */ +"MSG_FROM_USER_IN_ROOM_WITH_CONTENT" = "%@ v %@: %@"; + +/** Single, end-to-end encrypted messages (ie. we don't know what they say) */ + +/* New message from a specific person, not referencing a room */ +"MSG_FROM_USER" = "%@ poslal/a zprávu"; + +/* New message reply from a specific person in a named room. */ +"REPLY_FROM_USER_IN_ROOM_TITLE" = "%@ odpověděl/a v %@"; + +/* New message reply from a specific person, not referencing a room. */ +"REPLY_FROM_USER_TITLE" = "%@ odpověděl/a"; + +/** Titles **/ + +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ v %@"; +/** General **/ + +"NOTIFICATION" = "Oznámení"; diff --git a/Riot/Assets/de.lproj/InfoPlist.strings b/Riot/Assets/de.lproj/InfoPlist.strings index 571d56f44..594fb19a0 100644 --- a/Riot/Assets/de.lproj/InfoPlist.strings +++ b/Riot/Assets/de.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSContactsUsageDescription" = "Element zeigt deine Kontakte an, damit du sie zum chatten einladen kannst."; "NSCalendarsUsageDescription" = "Sieh dir deine geplanten Meetings in der App an."; "NSFaceIDUsageDescription" = "Face-ID wird zum Zugriff auf deine App verwendet."; +"NSLocationWhenInUseUsageDescription" = "Wenn du deinen Standort mit Personen teilst, braucht Element Zugriff um ihnen eine Karte anzuzeigen."; diff --git a/Riot/Assets/de.lproj/Localizable.strings b/Riot/Assets/de.lproj/Localizable.strings index 3c7e1f066..a5edb4e62 100644 --- a/Riot/Assets/de.lproj/Localizable.strings +++ b/Riot/Assets/de.lproj/Localizable.strings @@ -116,3 +116,6 @@ /* A user's membership has updated in an unknown way */ "USER_MEMBERSHIP_UPDATED" = "Profil von %@ geupdatet"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ hat den eigenen Standort geteilt"; diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index f0fb19fc2..df03d8813 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -136,12 +136,12 @@ "room_participants_action_section_other" = "Optionen"; "room_participants_action_invite" = "Einladen"; "room_participants_action_leave" = "Diesen Raum verlassen"; -"room_participants_action_remove" = "Von diesem Raum entfernen"; +"room_participants_action_remove" = "Aus diesem Raum entfernen"; "room_participants_action_ban" = "Aus diesem Raum bannen"; -"room_participants_action_ignore" = "Alle Nachrichten von diesem Nutzer verbergen"; -"room_participants_action_unignore" = "Zeige alle Nachrichten von diesem Nutzer"; -"room_participants_action_set_moderator" = "Gib Moderationsrechte"; -"room_participants_action_set_admin" = "Mache zum Administrator"; +"room_participants_action_ignore" = "Alle Nachrichten dieser Person verbergen"; +"room_participants_action_unignore" = "Alle Nachrichten dieser Person zeigen"; +"room_participants_action_set_moderator" = "Moderationsrechte vergeben"; +"room_participants_action_set_admin" = "Administrationsrechte vergeben"; "room_participants_action_start_new_chat" = "Starte neuen Chat"; "room_participants_action_start_video_call" = "Starte Video-Anruf"; "room_participants_action_mention" = "Erwähnen"; @@ -299,7 +299,7 @@ "room_participants_ago" = "her"; "room_participants_action_section_admin_tools" = "Admin-Werkzeuge"; "room_participants_action_unban" = "Entsperren"; -"room_participants_action_set_default_power_level" = "Zurück auf normale Berechtigung"; +"room_participants_action_set_default_power_level" = "Besondere Berechtigungen entziehen"; "room_participants_action_start_voice_call" = "Starte Sprach-Anruf"; "room_ongoing_conference_call" = "Laufender Konferenz-Anruf. Trete bei als %@ oder %@."; "room_event_action_redact" = "Entfernen"; @@ -509,7 +509,7 @@ "room_action_send_photo_or_video" = "Foto oder Video senden"; "room_action_send_sticker" = "Aufkleber senden"; "settings_deactivate_account" = "DEAKTIVIERTES KONTO"; -"settings_deactivate_my_account" = "Benutzerkonto deaktiveren"; +"settings_deactivate_my_account" = "Mein Konto deaktivieren"; "widget_sticker_picker_no_stickerpacks_alert" = "Du hast aktuell keine Aufkleberpakete aktiviert."; "widget_sticker_picker_no_stickerpacks_alert_add_now" = "Welche hinzufügen?"; // GDPR @@ -544,12 +544,12 @@ "room_event_action_view_decrypted_source" = "Zeige entschlüsselten Quelltext"; "room_recents_server_notice_section" = "SYSTEMBENACHRICHTIGUNGEN"; "room_resource_limit_exceeded_message_contact_1" = " Bitte "; -"room_resource_limit_exceeded_message_contact_2_link" = "kontaktiere deinen Dienst-Administrator"; +"room_resource_limit_exceeded_message_contact_2_link" = "kontaktiere deine Dienst-Administration"; "room_resource_limit_exceeded_message_contact_3" = " um diesen Dienst weiter zu nutzen."; "homeserver_connection_lost" = "Konnte keine Verbindung zum Heimserver herstellen."; "room_resource_usage_limit_reached_message_1_default" = "Dieser Heimserver hat eine seiner Ressourcengrenzen überschritten, sodass "; -"room_resource_usage_limit_reached_message_1_monthly_active_user" = "Dieser Heimserver hat seine Begrenzung an monatlich aktiven Benutzern überschritten, sodass "; -"room_resource_usage_limit_reached_message_2" = "einige Benutzer nicht in der Lage sein werden, sich anzumelden."; +"room_resource_usage_limit_reached_message_1_monthly_active_user" = "Dieser Heimserver hat seine Grenze an monatlich aktiven Benutzenden erreicht, sodass "; +"room_resource_usage_limit_reached_message_2" = "einige Benutzende nicht in der Lage sein werden, sich anzumelden."; "room_resource_usage_limit_reached_message_contact_3" = " um diese Obergrenze erhöhen zu lassen."; "auth_accept_policies" = "Bitte Regeln dieses Heimservers ansehen und akzeptieren:"; "settings_key_backup" = "SCHLÜSSEL-SICHERHEITSKOPIE"; @@ -634,13 +634,13 @@ "sign_out_existing_key_backup_alert_sign_out_action" = "Abmelden"; "sign_out_non_existing_key_backup_alert_title" = "Du verlierst den Zugriff auf deine verschlüsselten Nachrichten, wenn du dich jetzt abmeldest"; "sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Beginne Schlüsselsicherung zu nutzen"; -"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Ich möchte meine verschlüsselten Nachrichten nicht"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Ich brauche meine verschlüsselten Nachrichten nicht"; "sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Du wirst deine verschlüsselten Nachrichten verlieren"; "sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Du verlierst den Zugriff auf deine verschlüsselten Nachrichten, es sei denn, du sicherst deine Schlüssel, bevor du dich abmeldest."; "sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Abmelden"; "sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Sicherungskopie"; "sign_out_key_backup_in_progress_alert_title" = "Schlüsselsicherung läuft. Wenn du dich jetzt abmeldest, verlierst du den Zugriff auf deine verschlüsselten Nachrichten."; -"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Ich möchte meine verschlüsselten Nachrichten nicht"; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Ich brauche meine verschlüsselten Nachrichten nicht"; "sign_out_key_backup_in_progress_alert_cancel_action" = "Ich werde warten"; // Key backup wrong version "e2e_key_backup_wrong_version_title" = "Neue Schlüsselsicherung"; @@ -734,7 +734,7 @@ "close" = "Schließen"; "auth_softlogout_signed_out" = "Du bist abgemeldet"; "auth_softlogout_sign_in" = "Anmelden"; -"auth_softlogout_reason" = "Deine Heimserver-Administrator (%1$@) hat dich von deinem Konto %2$@ (%3$@) abgemeldet."; +"auth_softlogout_reason" = "Die Administration deines Heimservers (%1$@) hat dich von deinem Konto %2$@ (%3$@) abgemeldet."; "auth_softlogout_recover_encryption_keys" = "Melde dich an, um ausschließlich auf diesem Gerät gespeicherte Verschlüsselungsschlüssel wiederherzustellen. Du benötigst sie, um deine verschlüsselten Nachrichten auf jedem Gerät zu lesen."; "auth_softlogout_clear_data" = "Persönliche Daten löschen"; "auth_softlogout_clear_data_message_1" = "Warnung: Deine persönlichen Daten (einschließlich Verschlüsselungsschlüssel) sind noch auf diesem Gerät gespeichert."; @@ -1002,10 +1002,10 @@ "security_settings_crosssigning_info_not_bootstrapped" = "Quersignierung ist bisher nicht konfiguriert."; "room_member_power_level_admin_in" = "Admin in %@"; "room_member_power_level_moderator_in" = "Mod in %@"; -"room_member_power_level_custom_in" = "Benutzerdefiniert (%@) in %@"; +"room_member_power_level_custom_in" = "Selbstdefiniert (%@) in %@"; "room_member_power_level_short_admin" = "Admin"; "room_member_power_level_short_moderator" = "Mod"; -"room_member_power_level_short_custom" = "Benutzerdefiniert"; +"room_member_power_level_short_custom" = "Selbstdefiniert"; "security_settings_secure_backup" = "SICHERE SICHERHEITSKOPIE"; "security_settings_secure_backup_synchronise" = "Synchronisiere"; "security_settings_secure_backup_delete" = "Backup löschen"; @@ -1327,16 +1327,16 @@ "room_accessibility_video_call" = "Videoanruf"; "room_message_replying_to" = "%@ anworten"; "room_message_editing" = "Bearbeitung"; -"space_beta_announce_information" = "Spaces are a new way to group rooms and people. Bald werden sie auch auf iOS verfügbar sein, bis dahin kannst du sie schon auf %@ Web/Desktop testen."; -"space_feature_unavailable_information" = "Wir haben Spaces entwickelt, damit ihr eure vielen Räume besser organisieren könnt.\n\nBald werden sie auch auf iOS verfügbar sein, bis dahin kannst du sie schon auf %@ Web/Desktop testen. Alle Räume die du dort betrittst, sind natürlich auch hier verfügbar."; +"space_beta_announce_information" = "Spaces bieten neue Möglichkeiten um Räume und Personen zu gruppieren. Sie sind noch nicht auf iOS verfügbar, aber du kannst sie jetzt schon mit Web und Desktop nutzen."; +"space_feature_unavailable_information" = "Spaces bieten neue Möglichkeiten um Räume und Personen zu gruppieren.\n\nBald werden sie auch hier verfügbar sein. Für den Moment kannst du ihnen auf einer der anderen Plattformen beitreten und hier auf alle Räume zugreifen, denen du dort beitrittst."; "space_beta_announce_subtitle" = "Die verbesserte Version von Communities"; "space_beta_announce_title" = "Spaces sind bald verfügbar"; "space_beta_announce_badge" = "Beta (in Entwicklung)"; -"space_feature_unavailable_subtitle" = "Spaces sind auf iOS noch nicht verfügbar. Du kannst sie aber schon auf %@ Web oder Desktop ausprobieren"; +"space_feature_unavailable_subtitle" = "Spaces sind auf iOS noch nicht verfügbar. Du kannst sie aber schon auf Web oder Desktop benutzen"; // Mark: - Spaces -"space_feature_unavailable_title" = "Spaces sind noch in der Entwicklung und werden bald verfügbar sein"; +"space_feature_unavailable_title" = "Spaces sind bald verfügbar"; "event_formatter_group_call_incoming" = "%@ in %@"; "event_formatter_group_call_leave" = "Verlassen"; "event_formatter_group_call_join" = "Beitreten"; @@ -1349,7 +1349,7 @@ "event_formatter_call_ringing" = "Läuten…"; "event_formatter_call_connecting" = "Verbinden…"; "settings_labs_enable_ringing_for_group_calls" = "Bei Gruppenanrufen klingeln"; -"room_no_privileges_to_create_group_call" = "Du musst Admin oder Mod sein, um einen Anruf zu starten."; +"room_no_privileges_to_create_group_call" = "Du musst Administrations- oder Moderationsrechte besitzen, um einen Anruf zu starten."; "room_join_group_call" = "Beitreten"; // Chat @@ -1459,12 +1459,12 @@ // Mark: Avatar "space_avatar_view_accessibility_label" = "Avatar"; -"spaces_coming_soon_detail" = "Diese Funktion wurde hier noch nicht eingebaut, kommt aber bald. So lange kannst du dafür Element am Computer nutzen."; +"spaces_coming_soon_detail" = "Diese Funktion wurde hier noch nicht eingebaut, kommt aber bald. So lange kannst du dafür %@ am Computer nutzen."; "spaces_invites_coming_soon_title" = "Einladungen sind bald verfügbar"; "spaces_add_rooms_coming_soon_title" = "Räume hinzufügen ist bald verfügbar"; "spaces_no_result_found_title" = "Keine Ergebnisse gefunden"; "spaces_suggested_room" = "Vorgeschlagen"; -"leave_space_message_admin_warning" = "Du bist Administrator in diesem Space, stelle vor dem Verlassen sicher, dass du die Administrator-Rechte auf ein anderes Mitglied übertragen hast."; +"leave_space_message_admin_warning" = "Du besitzt in diesem Space Administrationsrechte, stelle vor dem Verlassen sicher, dass du die Administrationsrechte auf ein anderes Mitglied übertragen hast."; "leave_space_message" = "Bist du dir sicher, dass du %@ verlassen möchtest? Möchtest du außerdem alle Räume und Spaces in diesem Space verlassen?"; "leave_space_title" = "%@ verlassen"; "space_avatar_view_accessibility_hint" = "Space-Avatar ändern"; @@ -1534,7 +1534,7 @@ "settings_about" = "ÜBER"; "enable" = "Aktivieren"; "analytics_prompt_message_upgrade" = "Du hast in der Vergangenheit bereits zugestimmt anonyme Nutzungsdaten mit uns zu teilen. Jetzt werden wir als Hilfe, um zu verstehen, wie Personen mehrere Geräte benutzen, eine zufällige Kennung generieren, die zwischen deinen Geräten geteilt wird."; -"analytics_prompt_message_new_user" = "Hilf uns dabei Probleme zu identifizieren und Element zu verbessern, indem du anonyme Nutzungsdaten teilst. Um zu verstehen, wie Personen mehrere Geräte benutzen, werden wir eine zufällige Kennung generieren, die zwischen deinen Geräten geteilt wird."; +"analytics_prompt_message_new_user" = "Hilf uns dabei Probleme zu identifizieren und %@ zu verbessern, indem du anonyme Nutzungsdaten teilst. Um zu verstehen, wie Personen mehrere Geräte benutzen, werden wir eine zufällige Kennung generieren, die zwischen deinen Geräten geteilt wird."; "find_your_contacts_title" = "Starte mit der Auflistung deiner Kontakte"; /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Du kannst unsere gesamten Bedingungen %@ nachlesen."; @@ -1567,3 +1567,32 @@ "analytics_prompt_point_1" = "Wir erfassen und analysieren keine Accountdaten"; /* Note: The placeholder is for the contents of analytics_prompt_terms_link_upgrade */ "analytics_prompt_terms_upgrade" = "Alle unsere Bedingungen lesen %@. Bist du damit einverstanden?"; +"ok" = "OK"; +"location_sharing_settings_toggle_title" = "Standortfreigabe aktivieren"; +"location_sharing_settings_header" = "Standortfreigabe"; +"location_sharing_open_google_maps" = "In Google Maps öffnen"; +"location_sharing_open_apple_maps" = "In Apple Karten öffnen"; +"location_sharing_invalid_authorization_settings" = "Einstellungen"; +"location_sharing_invalid_authorization_not_now" = "Nicht jetzt"; +"location_sharing_invalid_authorization_error_title" = "%@ besitzt keine Berechtigung um auf deinen Standort zuzugreifen. Du kannst den Zugriff unter Einstellungen > Standort erlauben"; +"location_sharing_locating_user_error_title" = "%@ konnte nicht auf deinen Standort zugreifen. Bitte versuche es später noch einmal."; +"location_sharing_loading_map_error_title" = "%@ konnte die Karte nicht laden. Bitte versuche es später noch einmal."; +"location_sharing_share_action" = "Teilen"; +"location_sharing_close_action" = "Schließen"; + +// MARK: - Location sharing + +"location_sharing_title" = "Standort"; +"onboarding_splash_page_4_title_no_pun" = "Nachrichtenaustausch für dein Team."; +"onboarding_splash_page_3_title" = "Sicherer Nachrichtenaustausch."; +"onboarding_splash_page_2_message" = "Wähle wo deine Gespräche liegen, für Kontrolle und Unabhängigkeit. Verbunden mit Matrix."; +"onboarding_splash_page_1_title" = "Nimm deine Gespräche in die eigene Hand."; +"onboarding_splash_login_button_title" = "Ich habe bereits ein Konto"; +"onboarding_splash_page_4_message" = "Element ist auch für die Arbeit großartig. Uns vertrauen einige der sichersten Organisationen der Welt."; +"onboarding_splash_page_3_message" = "Ende-zu-Ende-verschlüsselt und ohne Telefonnummer nutzbar. Keine Werbung oder Datenerfassung."; +"onboarding_splash_page_2_title" = "Du hast die Kontrolle."; +"onboarding_splash_page_1_message" = "Sichere und unabhängige Kommunikation, die für die gleiche Vertraulichkeit sorgt, wie ein Gespräch von Angesicht zu Angesicht in deinem eigenen Zuhause."; + +// Onboarding +"onboarding_splash_register_button_title" = "Konto erstellen"; +"settings_enable_room_message_bubbles" = "Nachrichtenblasen"; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 7c2a7276b..36a6e454c 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -345,7 +345,8 @@ Tap the + to start adding people."; "room_member_power_level_short_moderator" = "Mod"; "room_member_power_level_short_custom" = "Custom"; -// Chat +// MARK: - Chat + "room_slide_to_end_group_call" = "Slide to end the call for everyone"; "room_jump_to_first_unread" = "Jump to unread"; "room_accessiblity_scroll_to_bottom" = "Scroll to bottom"; @@ -385,7 +386,8 @@ Tap the + to start adding people."; "room_event_action_more" = "More"; "room_event_action_share" = "Share"; "room_event_action_forward" = "Forward"; -"room_event_action_permalink" = "Permalink"; +"room_event_action_view_in_room" = "View in room"; +"room_event_action_permalink" = "Copy link to message"; "room_event_action_view_source" = "View Source"; "room_event_action_view_decrypted_source" = "View Decrypted Source"; "room_event_action_report" = "Report content"; @@ -402,10 +404,12 @@ Tap the + to start adding people."; "room_event_action_cancel_download" = "Cancel Download"; "room_event_action_view_encryption" = "Encryption Information"; "room_event_action_reply" = "Reply"; +"room_event_action_reply_in_thread" = "Thread"; "room_event_action_edit" = "Edit"; "room_event_action_reaction_show_all" = "Show all"; "room_event_action_reaction_show_less" = "Show less"; "room_event_action_reaction_history" = "Reaction history"; +"room_event_copy_link_info" = "Link copied to clipboard."; "room_warning_about_encryption" = "End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption."; "room_event_failed_to_send" = "Failed to send"; "room_action_camera" = "Take photo or video"; @@ -430,12 +434,27 @@ Tap the + to start adding people."; "room_accessibility_upload" = "Upload"; "room_accessibility_call" = "Call"; "room_accessibility_video_call" = "Video Call"; +"room_accessibility_threads" = "Threads"; "room_accessibility_hangup" = "Hang up"; +"room_accessibility_thread_more" = "More"; "room_place_voice_call" = "Voice call"; "room_open_dialpad" = "Dial pad"; "room_join_group_call" = "Join"; "room_no_privileges_to_create_group_call" = "You need to be an admin or a moderator to start a call."; +// MARK: Threads +"room_thread_title" = "Thread"; +"thread_copy_link_to_thread" = "Copy link to thread"; +"threads_title" = "Threads"; +"threads_action_all_threads" = "All threads"; +"threads_action_my_threads" = "My threads"; +"threads_empty_title" = "Keep discussions organised with threads"; +"threads_empty_info_all" = "Threads help keep your conversations on-topic and easy to track."; +"threads_empty_info_my" = "Reply to an ongoing thread or tap a message and use “Thread” to start a new one."; +"threads_empty_tip" = "Tip: Tap a message and use “Thread” to start one."; +"threads_empty_show_all_threads" = "Show all threads"; +"message_from_a_thread" = "From a thread"; + "media_type_accessibility_image" = "Image"; "media_type_accessibility_audio" = "Audio"; "media_type_accessibility_video" = "Video"; @@ -592,6 +611,7 @@ Tap the + to start adding people."; "settings_labs_message_reaction" = "React to messages with emoji"; "settings_labs_enable_ringing_for_group_calls" = "Ring for group calls"; "settings_labs_enabled_polls" = "Polls"; +"settings_labs_enable_threads" = "Threaded messaging"; "settings_version" = "Version %@"; "settings_olm_version" = "Olm Version %@"; @@ -926,6 +946,7 @@ Tap the + to start adding people."; "event_formatter_group_call_join" = "Join"; "event_formatter_group_call_leave" = "Leave"; "event_formatter_group_call_incoming" = "%@ in %@"; +"event_formatter_message_deleted" = "Message deleted"; // Events formatter with you "event_formatter_widget_added_by_you" = "You added the widget: %@"; @@ -1718,6 +1739,16 @@ Tap the + to start adding people."; "home_empty_view_title" = "Welcome to %@,\n%@"; "home_empty_view_information" = "The all-in-one secure chat app for teams, friends and organisations. Tap the + button below to add people and rooms."; +"home_context_menu_make_dm" = "Move to People"; +"home_context_menu_make_room" = "Move to Rooms"; +"home_context_menu_notifications" = "Notifications"; +"home_context_menu_mute" = "Mute"; +"home_context_menu_unmute" = "Unmute"; +"home_context_menu_favourite" = "Favourite"; +"home_context_menu_unfavourite" = "Remove from Favourites"; +"home_context_menu_low_priority" = "Low priority"; +"home_context_menu_normal_priority" = "Normal priority"; +"home_context_menu_leave" = "Leave"; // MARK: - Favourites @@ -1763,6 +1794,8 @@ Tap the + to start adding people."; "room_invite_to_space_option_detail" = "They can explore %@, but won’t be a member of %@."; "room_invite_to_room_option_title" = "To just this room"; "room_invite_to_room_option_detail" = "They won’t be a part of %@."; +"room_invite_not_enough_permission" = "You do not have permission to invite people to this room"; +"space_invite_not_enough_permission" = "You do not have permission to invite people to this space"; // Mark: - Spaces @@ -1803,6 +1836,9 @@ Tap the + to start adding people."; "space_private_join_rule" = "Private space"; "space_private_join_rule_detail" = "Invite only, best for yourself or teams"; "space_public_join_rule" = "Public space"; +"spaces_invite_people" = "Invite people"; +"spaces_add_room" = "Add room"; +"spaces_add_space" = "Add space"; "space_public_join_rule_detail" = "Open to anyone, best for communities"; "space_topic" = "description"; @@ -1913,6 +1949,8 @@ Tap the + to start adding people."; "poll_edit_form_create_poll" = "Create poll"; +"poll_edit_form_poll_type" = "Poll type"; + "poll_edit_form_poll_question_or_topic" = "Poll question or topic"; "poll_edit_form_question_or_topic" = "Question or topic"; @@ -1929,6 +1967,18 @@ Tap the + to start adding people."; "poll_edit_form_post_failure_subtitle" = "Please try again"; +"poll_edit_form_update_failure_title" = "Failed to update poll"; + +"poll_edit_form_update_failure_subtitle" = "Please try again"; + +"poll_edit_form_poll_type_open" = "Open poll"; + +"poll_edit_form_poll_type_open_description" = "Voters see results as soon as they have voted"; + +"poll_edit_form_poll_type_closed" = "Closed poll"; + +"poll_edit_form_poll_type_closed_description" = "Results are only revealed when you end the poll"; + "poll_timeline_one_vote" = "1 vote"; "poll_timeline_votes_count" = "%lu votes"; @@ -1963,6 +2013,10 @@ Tap the + to start adding people."; "location_sharing_share_action" = "Share"; +"location_sharing_post_failure_title" = "We couldn’t send your location"; + +"location_sharing_post_failure_subtitle" = "%@ could not send your location. Please try again later."; + "location_sharing_loading_map_error_title" = "%@ could not load the map. Please try again later."; "location_sharing_locating_user_error_title" = "%@ could not access your location. Please try again later."; diff --git a/Riot/Assets/et.lproj/InfoPlist.strings b/Riot/Assets/et.lproj/InfoPlist.strings index 25a5d9479..e556d1d6a 100644 --- a/Riot/Assets/et.lproj/InfoPlist.strings +++ b/Riot/Assets/et.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSCalendarsUsageDescription" = "Vaata päevakavasse lisatud koosolekuid vastvast rakendusest."; "NSContactsUsageDescription" = "Element näitab sulle tuttavaid, kellega saad alustada vestlust."; "NSFaceIDUsageDescription" = "Ligipääsuks sinu rakendusele on kasutusel Face ID."; +"NSLocationWhenInUseUsageDescription" = "Kui sa jagad teiste kasutajatega oma asukohta, siis Element vajab õigusi asukoha kuvamiseks kaardil."; diff --git a/Riot/Assets/et.lproj/Localizable.strings b/Riot/Assets/et.lproj/Localizable.strings index 6e4824651..844410852 100644 --- a/Riot/Assets/et.lproj/Localizable.strings +++ b/Riot/Assets/et.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Teavitus"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ jagas oma asukohta"; diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 2a1abaf1d..c88b9e297 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -1420,7 +1420,7 @@ "space_private_join_rule" = "Privaatne kogukond"; "space_participants_action_ban" = "Sea selles kogukonnakeskus suhtluskeeld"; "space_participants_action_remove" = "Eemalda sellest kogukonnakeskusest"; -"spaces_coming_soon_detail" = "See funktsionaalsus pole siin rakenduses hetkel veel saadaval, aga üsna varsti saab olema. Seni saad sa seda toimingut teha Element'i töölauarakenduses."; +"spaces_coming_soon_detail" = "See funktsionaalsus pole siin rakenduses hetkel veel saadaval, aga üsna varsti saab olema. Seni saad sa seda toimingut teha %@'i töölauarakenduses."; "spaces_invites_coming_soon_title" = "Varsti lisandub kutsete saatmine"; "spaces_add_rooms_coming_soon_title" = "Varsti on jututubade lisamine võimalik"; "spaces_coming_soon_title" = "Mõne aja pärast on meil uuendusi"; @@ -1520,10 +1520,39 @@ /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Meie kasutustingimused leiad %@."; "analytics_prompt_message_upgrade" = "Sa oled varem nõustunud meiega anonüümsete andmete jagamisega. Selleks, et mõistaksime, kuidas kasutajad erinevaid seadmeid pruugivad, me loome sinu seadmetele ühise juhusliku tunnuse."; -"analytics_prompt_message_new_user" = "Võimalike vigade leidmiseks ja Element'i arendamiseks jaga meiega anonüümseid andmeid. Selleks, et mõistaksime, kuidas kasutajad erinevaid seadmeid pruugivad me loome sinu seadmetele ühise juhusliku tunnuse."; +"analytics_prompt_message_new_user" = "Võimalike vigade leidmiseks ja %@'i arendamiseks jaga meiega anonüümseid andmeid. Selleks, et mõistaksime, kuidas kasutajad erinevaid seadmeid pruugivad, me loome sinu seadmetele ühise juhusliku tunnuse."; // Analytics "analytics_prompt_title" = "Aita arendada %@ rakendust"; "settings_analytics_and_crash_data" = "Saada rakenduse vigade ja analüütika andmeid"; "accessibility_button_label" = "nupp"; "enable" = "Võta kasutusele"; +"location_sharing_settings_toggle_title" = "Luba asukohta jagada"; +"location_sharing_settings_header" = "Asukoha jagamine"; +"location_sharing_open_google_maps" = "Ava rakendusega Google Maps"; +"location_sharing_open_apple_maps" = "Ava rakendusega Apple Maps"; +"location_sharing_invalid_authorization_settings" = "Seadistused"; +"location_sharing_invalid_authorization_not_now" = "Mitte praegu"; +"location_sharing_invalid_authorization_error_title" = "%@ vajab asukoha määramiseks õigusi, mida saad määrata Seadistused > Asukoht valikust"; +"location_sharing_locating_user_error_title" = "%@ ei saanud asukohta tuvastada. Palun proovi hiljem uuesti."; +"location_sharing_loading_map_error_title" = "%@ ei saanud kaarti avada. Palun proovi hiljem uuesti."; +"location_sharing_share_action" = "Jaga"; +"location_sharing_close_action" = "Sulge"; + +// MARK: - Location sharing + +"location_sharing_title" = "Asukoht"; +"ok" = "Sobib"; +"settings_enable_room_message_bubbles" = "Jutumullid"; +"onboarding_splash_page_4_message" = "Element sobib ideaalselt kasutamiseks töökeskkonnas. Ta on kasutusel ka mitmetes üliturvalistes organisatsioonides."; +"onboarding_splash_page_4_title_no_pun" = "Sõnumisuhtlus sinu tiimi või kogukonna jaoks."; +"onboarding_splash_page_3_message" = "Tagatud on andmete läbiv krüptimine ning oma telefoninumbrit ei pea sa jagama. Pole reklaame ega sinu andmete kogumist."; +"onboarding_splash_page_3_title" = "Turvaline sõnumisuhtlus."; +"onboarding_splash_page_2_message" = "Sa ise valid serveri, kus sinu vestlusi hoitakse ning sellega tagadki kontrolli oma andmete üle. Lahendus põhineb Matrix'i võrgul."; +"onboarding_splash_page_2_title" = "Sul on kontroll oma andmete üle."; +"onboarding_splash_page_1_message" = "Turvaline ja sõltumatu suhtluslahendus, mis tagab sama privaatsuse, kui omavaheline vestlus sinu kodus."; +"onboarding_splash_page_1_title" = "Vestlused, mida sa tegelikult ka omad."; +"onboarding_splash_login_button_title" = "Mul on kasutajakonto juba olemas"; + +// Onboarding +"onboarding_splash_register_button_title" = "Loo kasutajakonto"; diff --git a/Riot/Assets/fr.lproj/InfoPlist.strings b/Riot/Assets/fr.lproj/InfoPlist.strings index 38d044ce1..c2a08df8d 100644 --- a/Riot/Assets/fr.lproj/InfoPlist.strings +++ b/Riot/Assets/fr.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSContactsUsageDescription" = "Element affichera vos contacts pour que vous puissiez les inviter à parler."; "NSCalendarsUsageDescription" = "Voir vos rendez-vous dans l’application."; "NSFaceIDUsageDescription" = "Face ID est utilisé pour accéder à votre application."; +"NSLocationWhenInUseUsageDescription" = "Element doit accéder à votre emplacement pour vous permettre de la partager aux autres utilisateurs sur une carte."; diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index aafd1db9a..0f012f74f 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -1492,7 +1492,7 @@ "space_private_join_rule" = "Espace privé"; "space_participants_action_ban" = "Supprimer de cet espace"; "space_participants_action_remove" = "Enlever de cet espace"; -"spaces_coming_soon_detail" = "Cette fonctionnalité n’a pas été implémentée ici, mais elle arrive. Pour effectuer cette action, vous pouvez utiliser Element sur votre ordinateur."; +"spaces_coming_soon_detail" = "Cette fonctionnalité n’a pas été implémentée ici, mais elle arrive. Pour effectuer cette action, vous pouvez utiliser %@ sur votre ordinateur."; "spaces_invites_coming_soon_title" = "Les invitations arrivent prochainement"; "spaces_add_rooms_coming_soon_title" = "L’ajout de salons arrive bientôt"; "spaces_coming_soon_title" = "Prochainement"; @@ -1581,7 +1581,7 @@ /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Vous pouvez lire nos conditions d’utilisation %@."; "analytics_prompt_message_upgrade" = "Vous aviez consenti précédemment à partager des rapports d’utilisation avec nous. Désormais, pour nous aider à comprendre comment les gens utilisent cette application sur plusieurs appareils, nous allons générer un identifiant aléatoire commun à tous vos appareils."; -"analytics_prompt_message_new_user" = "Aidez nous à identifier les problèmes et améliorer Element en envoyant des rapports d’usage anonymes. Pour comprendre de quelle manière les gens utilisent Element sur plusieurs appareils, nous créeront un identifiant aléatoire commun à tous vos appareils."; +"analytics_prompt_message_new_user" = "Aidez nous à identifier les problèmes et améliorer %@ en envoyant des rapports d’usage anonymes. Pour comprendre de quelle manière les gens utilisent Element sur plusieurs appareils, nous créeront un identifiant aléatoire commun à tous vos appareils."; // Analytics "analytics_prompt_title" = "Aidez à améliorer %@"; @@ -1604,3 +1604,32 @@ "find_your_contacts_title" = "Commencez par lister vos contacts"; "settings_contacts_enable_sync_description" = "Cette fonctionnalité utilisera votre serveur d'identité pour vous connecter avec vos contacts, ainsi que pour les aider à vous trouver."; "settings_contacts_enable_sync" = "Trouvez vos contacts"; +"location_sharing_settings_toggle_title" = "Activer le partage de localisation"; +"location_sharing_settings_header" = "Partage de localisation"; +"location_sharing_open_google_maps" = "Ouvrir dans Google Maps"; +"location_sharing_open_apple_maps" = "Ouvrir dans Apple Plans"; +"location_sharing_invalid_authorization_settings" = "Paramètres"; +"location_sharing_invalid_authorization_not_now" = "Pas maintenant"; +"location_sharing_invalid_authorization_error_title" = "%@ n’a pas les autorisations pour accéder à votre localisation. Vous pouvez l’autoriser dans Réglages > Confidentialité"; +"location_sharing_locating_user_error_title" = "%@ n’a pas pu déterminer votre localisation. Veuillez ré-essayer plus tard."; +"location_sharing_loading_map_error_title" = "%@ n’a pas pu charger la carte. Veuillez ré-essayer plus tard."; +"location_sharing_share_action" = "Partager"; +"location_sharing_close_action" = "Fermer"; + +// MARK: - Location sharing + +"location_sharing_title" = "Localisation"; +"settings_enable_room_message_bubbles" = "Messages en bulles"; +"onboarding_splash_page_4_message" = "Element est parfaite pour le bureau. Les sociétés aux plus gros besoins de sécurité lui font confiance."; +"onboarding_splash_page_4_title_no_pun" = "Une messagerie pour votre équipe."; +"onboarding_splash_page_3_message" = "Chiffrée de bout en bout, aucun numéro de téléphone requis. Pas de pub ni d’exploitation de données."; +"onboarding_splash_page_3_title" = "Messagerie sécurisée."; +"onboarding_splash_page_2_message" = "Décidez où vos conversations sont stockées, en toute liberté et indépendance. Grâce à Matrix."; +"onboarding_splash_page_2_title" = "C’est vous qui décidez."; +"onboarding_splash_page_1_message" = "Une messagerie sécurisée et indépendante qui vous garantit le même niveau de confidentialité qu’une conversation en face à face chez vous."; +"onboarding_splash_page_1_title" = "Maîtrisez vos conversations."; +"onboarding_splash_login_button_title" = "J’ai déjà un compte"; + +// Onboarding +"onboarding_splash_register_button_title" = "Créer un compte"; +"ok" = "Ok"; diff --git a/Riot/Assets/hu.lproj/InfoPlist.strings b/Riot/Assets/hu.lproj/InfoPlist.strings index 0edf4d429..75e748c58 100644 --- a/Riot/Assets/hu.lproj/InfoPlist.strings +++ b/Riot/Assets/hu.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSContactsUsageDescription" = "Element megmutatja a névjegyzéket, hogy beszélgetésbe meghívhasd őket."; "NSCalendarsUsageDescription" = "Nézd meg a találkozóidat az alkalmazásban."; "NSFaceIDUsageDescription" = "Arc felismerés használata az alkalmazás eléréséhez."; +"NSLocationWhenInUseUsageDescription" = "Ha megosztod másokkal a földrajzi helyzetedet, akkor az Elementnek hozzáférésre van szüksége a térképen való megjelenítéshez."; diff --git a/Riot/Assets/hu.lproj/Localizable.strings b/Riot/Assets/hu.lproj/Localizable.strings index caa2f34ad..d0ed4447e 100644 --- a/Riot/Assets/hu.lproj/Localizable.strings +++ b/Riot/Assets/hu.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Értesítés"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ megosztotta a földrajzi helyzetét"; diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 6060f16c3..29e3902c1 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -1483,7 +1483,7 @@ "space_private_join_rule" = "Privát tér"; "space_participants_action_ban" = "Kitiltás a térről"; "space_participants_action_remove" = "Eltávolítás a térről"; -"spaces_coming_soon_detail" = "Ez a lehetőség még nincs meg, de fejlesztés alatt van. Egyenlőre a számítógépeden Elementtel tudod ezt megtenni."; +"spaces_coming_soon_detail" = "Ez a lehetőség még nincs meg, de fejlesztés alatt van. Egyenlőre a számítógépeden %@ alkalmazással tudod ezt megtenni."; "spaces_invites_coming_soon_title" = "Meghívók küldése hamarosan érkezik"; "spaces_add_rooms_coming_soon_title" = "Szobák hozzáadása hamarosan érkezik"; "spaces_coming_soon_title" = "Hamarosan"; @@ -1579,7 +1579,7 @@ "analytics_prompt_terms_link_new_user" = "itt"; /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Az összes feltételünket elolvashatod itt: %@."; -"analytics_prompt_message_new_user" = "Segíts észrevennünk a hibákat, és jobbá tenni az Element-et a névtelen használati adatok küldése által. Ahhoz, hogy megértsük, hogyan használnak a felhasználók egyszerre több eszközt, egy véletlenszerű azonosítót generálunk, ami az eszközeid között meg lesz osztva."; +"analytics_prompt_message_new_user" = "Segíts észrevennünk a hibákat, és jobbá tenni a(z) %@ alkalmazást a névtelen használati adatok küldése által. Ahhoz, hogy megértsük, hogyan használnak a felhasználók egyszerre több eszközt, egy véletlenszerű azonosítót generálunk, ami az eszközeid között meg lesz osztva."; // Analytics "analytics_prompt_title" = "Segíts jobbá tenni %@"; @@ -1590,3 +1590,32 @@ "accessibility_button_label" = "gomb"; "enable" = "Engedélyezés"; "analytics_prompt_message_upgrade" = "Korábban beleegyeztél, hogy velünk anonimizált adatokat osztasz meg. Most, hogy jobban megértsük, hogyan használnak több eszközt az emberek, véletlenszerű azonosítót állítunk elő amit az eszközeid használni fognak."; +"location_sharing_settings_toggle_title" = "Földrajzi hely megosztás engedélyezése"; +"location_sharing_settings_header" = "Földrajzi hely megosztása"; +"location_sharing_open_google_maps" = "Megnyitás a Google Térképen"; +"location_sharing_open_apple_maps" = "Megnyitás az Apple Térképen"; +"location_sharing_invalid_authorization_settings" = "Beállítások"; +"location_sharing_invalid_authorization_not_now" = "Nem most"; +"location_sharing_invalid_authorization_error_title" = "%@ alkalmazásnak nincs jogosultsága a helyadatod eléréséhez. Engedélyezheted a Beállítások > Helyadatokban"; +"location_sharing_loading_map_error_title" = "%@ nem tudja betölteni a térképet. Próbáld újra később."; +"location_sharing_locating_user_error_title" = "%@ nem fér hozzá a helyadatodhoz. Próbáld újra később."; +"location_sharing_share_action" = "Megoszt"; +"location_sharing_close_action" = "Bezár"; + +// MARK: - Location sharing + +"location_sharing_title" = "Földrajzi helyzet"; +"ok" = "OK"; +"onboarding_splash_page_4_message" = "Element megállja a helyét a munkahelyen is. A világ legbiztonságosabb szervezetei bíznak meg benne."; +"onboarding_splash_page_3_message" = "Telefonszám nélkül végpontok között titkosított. Reklámok és adatbányászat nélkül."; +"onboarding_splash_page_2_message" = "Válaszd meg hol legyenek a beszélgetéseid tárolva, visszaadja az irányítást és függetlenné tesz. Csatlakozva a Matrixhoz."; +"onboarding_splash_page_1_message" = "Biztonságos és független kommunikáció ami olyan biztonságos mintha valakivel négyszemközt beszélgetnél a házadban."; +"settings_enable_room_message_bubbles" = "Üzenet buborékok"; +"onboarding_splash_page_4_title_no_pun" = "Üzenetküldés a csoportodnak."; +"onboarding_splash_page_3_title" = "Biztonságos üzenetküldés."; +"onboarding_splash_page_2_title" = "Te irányítasz."; +"onboarding_splash_page_1_title" = "Az ön beszélgetései csak az öné."; +"onboarding_splash_login_button_title" = "Már van fiókom"; + +// Onboarding +"onboarding_splash_register_button_title" = "Fiók létrehozása"; diff --git a/Riot/Assets/id.lproj/InfoPlist.strings b/Riot/Assets/id.lproj/InfoPlist.strings index e74ebc244..3459f17dd 100644 --- a/Riot/Assets/id.lproj/InfoPlist.strings +++ b/Riot/Assets/id.lproj/InfoPlist.strings @@ -7,3 +7,4 @@ "NSCalendarsUsageDescription" = "Lihat pertemuan yang sudah dijadwalkan di aplikasi."; "NSMicrophoneUsageDescription" = "Element membutuhkan akses ke mikrofon Anda untuk melakukan dan menerima panggilan, mengambil video, dan merekam pesan suara."; "NSPhotoLibraryUsageDescription" = "Galeri digunakan untuk mengirim foto dan video."; +"NSLocationWhenInUseUsageDescription" = "Ketika Anda membagikan lokasi Anda ke orang-orang, Element membutuhkan akses untuk menampilkan mereka sebuah peta."; diff --git a/Riot/Assets/id.lproj/Localizable.strings b/Riot/Assets/id.lproj/Localizable.strings index 679a5cbb8..dc244763c 100644 --- a/Riot/Assets/id.lproj/Localizable.strings +++ b/Riot/Assets/id.lproj/Localizable.strings @@ -165,3 +165,6 @@ /** General **/ "NOTIFICATION" = "Notifikasi"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ membagikan lokasinya"; diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index e92ace022..a4fddb9be 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -611,7 +611,7 @@ "open" = "Buka"; "version_check_banner_subtitle_deprecated" = "Kami tidak mendukung %@ di iOS %@ lagi. Untuk terus menggunakan %@ secara maksimal, kami menyarankan Anda untuk meningkatkan versi iOS Anda."; "version_check_banner_subtitle_supported" = "Kami akan segera mengakhiri dukungan untuk %@ di iOS %@. Untuk terus menggunakan %@ secara maksimal, kami menyarankan Anda untuk meningkatkan versi iOS Anda."; -"spaces_coming_soon_detail" = "Fitur ini belum ada di sini, tetapi sedang dalam proses. Untuk saat ini, Anda dapat melakukannya dengan Element di komputer Anda."; +"spaces_coming_soon_detail" = "Fitur ini belum ada di sini, tetapi sedang dalam proses. Untuk saat ini, Anda dapat melakukannya dengan %@ di komputer Anda."; "spaces_no_member_found_detail" = "Mencari seseorang yang tidak ada di %@? Untuk saat ini, Anda dapat mengundangnya di web atau desktop."; "spaces_no_room_found_detail" = "Beberapa hasil mungkin disembunyikan karena ruangannya pribadi dan Anda memerlukan sebuah undangan untuk bergabung."; "spaces_empty_space_detail" = "Beberapa ruangan mungkin disembunyikan karena ruangannya pribadi dan Anda memerlukan sebuah undangan."; @@ -1699,10 +1699,39 @@ /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Anda dapat membaca semua kebijakan kami %@."; "analytics_prompt_message_upgrade" = "Anda sebelumnya setuju untuk mengirimkan data penggunaan anonim dengan kami. Sekarang, supaya kami dapat memahami bagaimana orang-orang menggunakan beberapa perangkat-perangkat, kami akan membuat pengenal acak, yang dibagikan oleh perangkat Anda."; -"analytics_prompt_message_new_user" = "Bantu kami mengidentifikasi masalah-masalah dan membuat Element lebih baik dengan membagikan data penggunaan anonim. Untuk memahami bagaimana orang-orang menggunakan beberapa perangkat-perangkat, kami akan membuat pengenal acak, yang dibagikan oleh perangkat Anda."; +"analytics_prompt_message_new_user" = "Bantu kami mengidentifikasi masalah-masalah dan membuat %@ lebih baik dengan membagikan data penggunaan anonim. Untuk memahami bagaimana orang-orang menggunakan beberapa perangkat-perangkat, kami akan membuat pengenal acak, yang dibagikan oleh perangkat Anda."; // Analytics "analytics_prompt_title" = "Bantu membuat %@ lebih baik"; "settings_analytics_and_crash_data" = "Kirim data crash dan analitik"; "accessibility_button_label" = "tombol"; "enable" = "Aktifkan"; +"location_sharing_invalid_authorization_error_title" = "%@ tidak memiliki izin untuk mengakses lokasi. Anda dapat mengaktifkan akses di Pengaturan > Lokasi"; +"location_sharing_settings_toggle_title" = "Aktifkan pembagian lokasi"; +"location_sharing_settings_header" = "Pembagian lokasi"; +"location_sharing_open_google_maps" = "Buka di Google Maps"; +"location_sharing_open_apple_maps" = "Buka di Apple Maps"; +"location_sharing_invalid_authorization_settings" = "Pengaturan"; +"location_sharing_invalid_authorization_not_now" = "Jangan sekarang"; +"location_sharing_locating_user_error_title" = "%@ tidak dapat mengakses lokasi Anda. Silakan coba lagi nanti."; +"location_sharing_loading_map_error_title" = "%@ tidak dapat memuat peta. Silakan coba lagi nanti."; +"location_sharing_share_action" = "Bagikan"; +"location_sharing_close_action" = "Tutup"; + +// MARK: - Location sharing + +"location_sharing_title" = "Lokasi"; +"ok" = "OKE"; +"settings_enable_room_message_bubbles" = "Gelembung pesan"; +"onboarding_splash_page_4_message" = "Element juga bagus untuk tempat kerja. Terpercayai oleh organisasi paling aman di dunia."; +"onboarding_splash_page_4_title_no_pun" = "Perpesanan untuk tim Anda."; +"onboarding_splash_page_3_message" = "Terenkripsi secara ujung-ke-ujung dan tidak memerlukan nomor telepon. Tidak ada iklan atau penambangan data."; +"onboarding_splash_page_3_title" = "Perpesanan aman."; +"onboarding_splash_page_2_message" = "Pilihlah di mana percakapan Anda disimpan, memberikan Anda kendali dan kebebasan. Terhubung via Matrix."; +"onboarding_splash_page_2_title" = "Anda dalam kendali."; +"onboarding_splash_page_1_message" = "Komunikasi aman dan independen yang memberikan tingkat privasi yang sama seperti percakapan tatap muka di rumah Anda sendiri."; +"onboarding_splash_page_1_title" = "Miliki percakapan Anda."; +"onboarding_splash_login_button_title" = "Saya sudah punya akun"; + +// Onboarding +"onboarding_splash_register_button_title" = "Buat akun"; diff --git a/Riot/Assets/it.lproj/InfoPlist.strings b/Riot/Assets/it.lproj/InfoPlist.strings index 6382ef48e..989ac76d3 100644 --- a/Riot/Assets/it.lproj/InfoPlist.strings +++ b/Riot/Assets/it.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSContactsUsageDescription" = "Element mostrerà i tuoi contatti così da poterli invitare in chat."; "NSCalendarsUsageDescription" = "Vedi le tue riunioni programmate nell'app."; "NSFaceIDUsageDescription" = "Face ID viene usato per accedere all'app."; +"NSLocationWhenInUseUsageDescription" = "Quando condividi la tua posizione con altre persone, Element richiede l'accesso per mostrare loro una mappa."; diff --git a/Riot/Assets/it.lproj/Localizable.strings b/Riot/Assets/it.lproj/Localizable.strings index 25a6a0cca..d8ec6e0cd 100644 --- a/Riot/Assets/it.lproj/Localizable.strings +++ b/Riot/Assets/it.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Notifica"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ ha condiviso la sua posizione"; diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 797589e89..26065f880 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -1454,7 +1454,7 @@ "space_private_join_rule" = "Spazio privato"; "space_participants_action_ban" = "Bandisci da questo spazio"; "space_participants_action_remove" = "Rimuovi da questo spazio"; -"spaces_coming_soon_detail" = "Questa funzionalità non è stata implementata qui, ma è in arrivo. Per adesso, puoi farlo con Element sul tuo computer."; +"spaces_coming_soon_detail" = "Questa funzionalità non è stata implementata qui, ma è in arrivo. Per adesso, puoi farlo con %@ sul tuo computer."; "spaces_invites_coming_soon_title" = "Inviti in arrivo"; "spaces_add_rooms_coming_soon_title" = "Aggiunta di stanze in arrivo"; "spaces_coming_soon_title" = "Prossimamente"; @@ -1554,10 +1554,39 @@ /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Puoi leggere i nostri termini di servizio %@."; "analytics_prompt_message_upgrade" = "Hai acconsentito precedentemente a condividere con noi dati di utilizzo anonimi. Ora, per capire come le persone usano diversi dispositivi, genereremo un identificativo casuale, condiviso dai tuoi dispositivi."; -"analytics_prompt_message_new_user" = "Aiutaci a identificare problemi e a migliorare Element condividendo dati di utilizzo anonimi. Per capire come le persone usano diversi dispositivi, genereremo un identificativo casuale, condiviso dai tuoi dispositivi."; +"analytics_prompt_message_new_user" = "Aiutaci a identificare problemi e a migliorare %@ condividendo dati di utilizzo anonimi. Per capire come le persone usano diversi dispositivi, genereremo un identificativo casuale, condiviso dai tuoi dispositivi."; // Analytics "analytics_prompt_title" = "Aiuta a migliorare %@"; "settings_analytics_and_crash_data" = "Invia crash e dati analitici"; "accessibility_button_label" = "pulsante"; "enable" = "Attiva"; +"location_sharing_settings_toggle_title" = "Attiva condivisione posizione"; +"location_sharing_settings_header" = "Condivisione posizione"; +"location_sharing_open_google_maps" = "Apri in Google Maps"; +"location_sharing_open_apple_maps" = "Apri in Apple Maps"; +"location_sharing_invalid_authorization_settings" = "Impostazioni"; +"location_sharing_invalid_authorization_not_now" = "Non ora"; +"location_sharing_invalid_authorization_error_title" = "%@ non ha l'autorizzazione per accedere alla tua posizione. Puoi attivarne l'accesso in Impostazioni > Localizzazione"; +"location_sharing_locating_user_error_title" = "%@ non ha potuto rilevare la tua posizione. Riprova più tardi."; +"location_sharing_loading_map_error_title" = "%@ non ha potuto caricare la mappa. Riprova più tardi."; +"location_sharing_share_action" = "Condividi"; +"location_sharing_close_action" = "Chiudi"; + +// MARK: - Location sharing + +"location_sharing_title" = "Posizione"; +"ok" = "OK"; +"settings_enable_room_message_bubbles" = "Messaggi"; +"onboarding_splash_page_4_message" = "Element è ottimo anche per i luoghi di lavoro. È considerato affidabile dalle più sicure organizzazioni del mondo."; +"onboarding_splash_page_4_title_no_pun" = "Messaggistica per la tua squadra."; +"onboarding_splash_page_3_message" = "Crittografia end-to-end e nessun numero di telefono richiesto. Niente pubblicità o raccolta di dati."; +"onboarding_splash_page_3_title" = "Messaggistica sicura."; +"onboarding_splash_page_2_message" = "Scegli dove vengono tenute le tue conversazioni, dandoti controllo e indipendenza. Connesso via Matrix."; +"onboarding_splash_page_2_title" = "Tu hai il controllo."; +"onboarding_splash_page_1_message" = "Comunicazioni sicure e indipendenti che ti danno lo stesso livello di riservatezza di una conversazione faccia a faccia in casa tua."; +"onboarding_splash_page_1_title" = "Prendi il controllo delle tue conversazioni."; +"onboarding_splash_login_button_title" = "Ho già un account"; + +// Onboarding +"onboarding_splash_register_button_title" = "Crea account"; diff --git a/Riot/Assets/nl.lproj/InfoPlist.strings b/Riot/Assets/nl.lproj/InfoPlist.strings index c3c846e97..9a4450097 100644 --- a/Riot/Assets/nl.lproj/InfoPlist.strings +++ b/Riot/Assets/nl.lproj/InfoPlist.strings @@ -21,3 +21,4 @@ "NSContactsUsageDescription" = "Element zal uw contacten tonen zodat u ze kunt uitnodigen om te chatten."; "NSCalendarsUsageDescription" = "Bekijk uw geplande afspraken in de app."; "NSFaceIDUsageDescription" = "Face ID wordt gebruikt om toegang te krijgen tot uw app."; +"NSLocationWhenInUseUsageDescription" = "Wanneer u uw locatie deelt met mensen heeft Element toegang nodig om dit op een kaart te tonen."; diff --git a/Riot/Assets/nl.lproj/Localizable.strings b/Riot/Assets/nl.lproj/Localizable.strings index f8c52bb83..7ff69454b 100644 --- a/Riot/Assets/nl.lproj/Localizable.strings +++ b/Riot/Assets/nl.lproj/Localizable.strings @@ -149,3 +149,6 @@ /** General **/ "NOTIFICATION" = "Meldingen"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ deelde hun locatie"; diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 65b0d41c6..b8eefab2b 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -1583,7 +1583,7 @@ "space_private_join_rule" = "Privé Space"; "space_participants_action_ban" = "Uit deze Space verbannen"; "space_participants_action_remove" = "Uit deze kamer verwijderen"; -"spaces_coming_soon_detail" = "Deze functies is nog niet klaar, maar er wordt aan gewerkt. Voor nu kan u dit wel doen met Element via uw computer."; +"spaces_coming_soon_detail" = "Deze functies is nog niet klaar, maar er wordt aan gewerkt. Voor nu kan u dit wel doen met %@ via uw computer."; "spaces_invites_coming_soon_title" = "Uitnodigen komt binnenkort"; "spaces_add_rooms_coming_soon_title" = "Kamers toevoegen komt binnenkort"; "spaces_coming_soon_title" = "Komt binnenkort"; @@ -1680,7 +1680,7 @@ "analytics_prompt_terms_new_user" = "U kunt al onze voorwaarden %@ lezen."; "analytics_prompt_terms_link_new_user" = "hier"; "analytics_prompt_message_upgrade" = "U heeft eerder toestemming gegeven om anonieme gebruiksgegevens met ons te delen. Om beter te begrijpen hoe mensen meerdere apparaten gebruiken, genereren we nu een willekeurige identificatiecode die door uw apparaten wordt gedeeld."; -"analytics_prompt_message_new_user" = "Help ons bij het identificeren van problemen en het verbeteren van Element door anonieme gebruiksgegevens te delen. Om te begrijpen hoe mensen meerdere apparaten gebruiken genereren we een willekeurige identificatie die we verspreiden over uw apparaten."; +"analytics_prompt_message_new_user" = "Help ons bij het identificeren van problemen en het verbeteren van %@ door anonieme gebruiksgegevens te delen. Om te begrijpen hoe mensen meerdere apparaten gebruiken genereren we een willekeurige identificatie die we verspreiden over uw apparaten."; // Analytics "analytics_prompt_title" = "Help %@ verbeteren"; @@ -1690,3 +1690,32 @@ "room_event_action_remove_poll" = "Poll verwijderen"; "accessibility_button_label" = "knop"; "enable" = "Inschakelen"; +"location_sharing_settings_toggle_title" = "Locatie delen inschakelen"; +"location_sharing_settings_header" = "Locatie delen"; +"location_sharing_open_google_maps" = "In Google Maps openen"; +"location_sharing_open_apple_maps" = "In Apple Maps openen"; +"location_sharing_invalid_authorization_settings" = "Instellingen"; +"location_sharing_invalid_authorization_not_now" = "Niet nu"; +"location_sharing_invalid_authorization_error_title" = "%@ heeft geen toegang tot uw locatie. U kan dit inschakelen via Instellingen > Locatie"; +"location_sharing_locating_user_error_title" = "%@ heeft geen toegang tot uw locatie. Probeer het later opnieuw."; +"location_sharing_loading_map_error_title" = "%@ kan de kaart niet laden. Probeer het later opnieuw."; +"location_sharing_share_action" = "Delen"; +"location_sharing_close_action" = "Sluiten"; + +// MARK: - Location sharing + +"location_sharing_title" = "Locatie"; +"ok" = "OK"; +"settings_enable_room_message_bubbles" = "Bericht bubbels"; +"onboarding_splash_page_4_message" = "Element is ook zeer geschikt voor op de werkvloer. Het wordt vertrouwd door 's werelds veiligste organisaties."; +"onboarding_splash_page_4_title_no_pun" = "Berichten voor uw team."; +"onboarding_splash_page_3_message" = "End-to-end versleuteld en geen telefoonnummer vereist. Geen advertenties of datamining."; +"onboarding_splash_page_3_title" = "Veilig berichtenverkeer."; +"onboarding_splash_page_2_message" = "Kies waar u gesprekken worden gehouden, zodat u controle en onafhankelijkheid heeft. Verbonden via Matrix."; +"onboarding_splash_page_2_title" = "U heeft de controle."; +"onboarding_splash_page_1_message" = "Veilige en onafhankelijke communicatie die u dezelfde mate van privacy geeft als een persoonlijk gesprek in uw eigen huis."; +"onboarding_splash_page_1_title" = "Word eigenaar van uw gesprekken."; +"onboarding_splash_login_button_title" = "Ik heb al een account"; + +// Onboarding +"onboarding_splash_register_button_title" = "Account aanmaken"; diff --git a/Riot/Assets/pl.lproj/Localizable.strings b/Riot/Assets/pl.lproj/Localizable.strings index 761802de0..3be30e2fb 100644 --- a/Riot/Assets/pl.lproj/Localizable.strings +++ b/Riot/Assets/pl.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Powiadomienie"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ udostępnił(-a) swoją lokację"; diff --git a/Riot/Assets/pl.lproj/Vector.strings b/Riot/Assets/pl.lproj/Vector.strings index c4445fe9b..4f027c5db 100644 --- a/Riot/Assets/pl.lproj/Vector.strings +++ b/Riot/Assets/pl.lproj/Vector.strings @@ -217,9 +217,9 @@ "settings_calls_settings" = "POŁĄCZENIA"; "settings_user_interface" = "INTERFEJS UŻYTKOWNIKA"; "settings_ignored_users" = "IGNOROWANI UŻYTKOWNICY"; -"settings_contacts" = "LOKALNE KONTAKTY"; +"settings_contacts" = "KONTAKTY NA URZĄDZENIU"; "settings_advanced" = "ZAAWANSOWANE"; -"settings_other" = "Pozostałe powiadomienia"; +"settings_other" = "Pozostałe ustawienia"; "settings_devices" = "SESJE"; "settings_cryptography" = "KRYPTOGRAFIA"; "settings_deactivate_account" = "DEZAKTYWUJ KONTO"; @@ -416,7 +416,7 @@ "auth_email_not_found" = "Nie udało się wysłać wiadomości e-mail: Adres e-mail nie został znaleziony"; "auth_accept_policies" = "Przeczytaj i zaakceptuj zasady tego serwera domowego:"; "room_creation_wait_for_creation" = "Pokój jest już tworzony. Proszę czekaj."; -"room_creation_invite_another_user" = "Szukaj / zaproś przez ID Użytkownika, Nazwę lub e-mail"; +"room_creation_invite_another_user" = "ID użytkownika, nazwa lub email"; "search_people_placeholder" = "Szukaj przez ID Użytkownika, Nazwę lub e-mail"; "contacts_user_directory_offline_section" = "KATALOG UŻYTKOWNIKÓW (offline)"; "room_participants_remove_prompt_msg" = "Czy na pewno chcesz usunąć %@ z tej rozmowy?"; @@ -1265,7 +1265,7 @@ // Widget Picker "widget_picker_title" = "Integracje"; -"widget_integration_manager_disabled" = "Musisz włączyć Menadżer Integracji w ustawieniach"; +"widget_integration_manager_disabled" = "Musisz włączyć menadżer integracji w ustawieniach"; "widget_menu_remove" = "Usuń dla wszystkich"; "widget_menu_revoke_permission" = "Odbierz mi dostęp"; "widget_menu_open_outside" = "Otwórz w przeglądarce"; @@ -1321,7 +1321,7 @@ "identity_server_settings_description" = "Obecnie używasz %@ do wyszukiwania i bycia znalezionym przez kontakty, które znasz."; // Identity server settings -"identity_server_settings_title" = "Serwer Tożsamości"; +"identity_server_settings_title" = "Serwer tożsamości"; // AuthenticatedSessionViewControllerFactory "authenticated_session_flow_not_supported" = "Ta aplikacja nie obsługuje mechanizmu uwierzytelniania na Twoim serwerze domowym."; @@ -1365,7 +1365,7 @@ "settings_discovery_terms_not_signed" = "Zaakceptuj warunki korzystania z usługi serwera tożsamości (%@), aby umożliwić innym osobom wyszukiwanie Ciebie za pomocą adresu e-mail lub numeru telefonu."; "settings_discovery_no_identity_server" = "Obecnie nie używasz serwera tożsamości. Pomyśl o tym, aby go dodać by móc być odnalezionym przez osoby, które znasz."; "settings_devices_description" = "Publiczna nazwa sesji jest widoczna dla osób, z którymi się komunikujesz"; -"settings_integrations_allow_description" = "Użyj Menedżera Integracji (%@) do zarządzania botami, mostami, widżetami i pakietami naklejek.\n\nMenedżerowie integracji otrzymują dane konfiguracyjne i mogą modyfikować widżety, wysyłać zaproszenia do pokojów i ustawiać poziomy mocy w Twoim imieniu."; +"settings_integrations_allow_description" = "Użyj menedżera integracji (%@) do zarządzania botami, mostami, widżetami i pakietami naklejek.\n\nMenedżerowie integracji otrzymują dane konfiguracyjne i mogą modyfikować widżety, wysyłać zaproszenia do pokojów i ustawiać poziomy mocy w Twoim imieniu."; "settings_calls_stun_server_fallback_description" = "Zezwalaj na użycie rezerwowego serwera połączeń %@ w przypadku, gdy Twój serwer domowy go nie oferuje (Twój adres IP będzie udostępniany podczas połączenia)."; "settings_three_pids_management_information_part1" = "Wybierz, które adresy e-mail lub numery telefonów chcesz wykorzystać do logowania się lub odzyskiwania konta. Kontroluj, kto może Cię dzięki nim znaleźć przechodząc do "; "room_multiple_typing_notification" = "%@ i inni"; @@ -1535,7 +1535,58 @@ "settings_default" = "Powiadomienia dotyczące wiadomości"; "settings_notifications" = "POWIADOMIENIA"; "settings_show_url_previews_description" = "Podgląd URL będzie wyświetlany tylko w niezaszyfrowanych pokojach."; -"settings_show_url_previews" = "Pokaż podgląd adresów URL"; +"settings_show_url_previews" = "Pokaż podgląd stron"; "settings_confirm_media_size_description" = "Gdy ta opcja jest włączona, zostaniesz poproszony o potwierdzenie, w jakim rozmiarze obrazy i filmy będą wysyłane."; "settings_confirm_media_size" = "Potwierdź rozmiar podczas wysyłania"; "settings_sending_media" = "WYSYŁANIE ZDJĘĆ I WIDEO"; +"service_terms_modal_table_header_integration_manager" = "WARUNKI KORZYSTANIA Z MENEDŻERA INTEGRACJI"; +"service_terms_modal_table_header_identity_server" = "WARUNKI KORZYSTANIA Z SERWERA TOŻSAMOŚCI"; +"service_terms_modal_footer" = "To może być wyłączone później w ustawieniach."; + +// Service terms +"service_terms_modal_title_message" = "Aby kontynuować, wyraź zgodę na poniższe warunki użytkowania"; +"share_extension_send_now" = "Udostępnij teraz"; +"share_extension_low_quality_video_message" = "Udostępnij w %@ by uzyskać lepszą jakość, lub udostępnij w niskiej jakości poniżej."; +"share_extension_low_quality_video_title" = "Video będzie udostępnianie w niskiej jakości"; +"analytics_prompt_stop" = "Przestań udostępniać"; +"analytics_prompt_yes" = "Tak, to jest w porządku"; +"analytics_prompt_not_now" = "Nie teraz"; +"analytics_prompt_point_3" = "Możesz to wyłączyć w dowolnym momencie w ustawieniach"; +/* Note: The word "don't" is formatted in bold */ +"analytics_prompt_point_2" = "Nie dzielimy się informacjami z żadnymi firmami zewnętrznymi"; +/* Note: The word "don't" is formatted in bold */ +"analytics_prompt_point_1" = "Nie gromadzimy ani profilujemy żadnych danych na temat kont"; +"analytics_prompt_terms_link_upgrade" = "tutaj"; +/* Note: The placeholder is for the contents of analytics_prompt_terms_link_upgrade */ +"analytics_prompt_terms_upgrade" = "Przeczytaj nasze warunki użytkowania %@. Czy odpowiadają one Tobie?"; +"analytics_prompt_terms_link_new_user" = "tutaj"; +/* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ +"analytics_prompt_terms_new_user" = "Tutaj możesz przeczytać nasze warunki użytkowania %@."; +"analytics_prompt_message_upgrade" = "Wcześniej dostaliśmy Twoją zgodę na używanie zanonimizowanych danych na temat użytkowania. Teraz, by zrozumieć jak użytkownicy korzystają z wielu urządzeń, wygenerujemy identyfikator używany przez nie wszystkie."; +"analytics_prompt_message_new_user" = "Pomóż nam zidentyfikować problemy i poprawić @% udostępniając zanonimizowane dane o użytkowaniu. W celu zrozumienia jak użytkownicy korzystają z wielu urządzeń wygenerujemy identyfikator używany przez nie wszystkie."; + +// Analytics +"analytics_prompt_title" = "Pomóż ulepszyć %@"; +"settings_discovery_accept_terms" = "Zaakceptuj warunki używania serwera tożsamości"; +"settings_analytics_and_crash_data" = "Wysyłaj dane analityczne i o zawieszeniu aplikacji"; +"settings_labs_enabled_polls" = "Ankiety"; +"settings_contacts_enable_sync" = "Znajdź swoje kontakty"; +"settings_phone_contacts" = "KONTAKTY TELEFONICZNE"; +"settings_links" = "LINKI"; +"room_event_action_forward" = "Przekaż dalej"; +"room_event_action_end_poll" = "Zakończ ankietę"; +"room_event_action_remove_poll" = "Usuń ankietę"; +"find_your_contacts_identity_service_error" = "Nie można połączyć się s serwerem tożsamości."; +"contacts_address_book_permission_denied_alert_message" = "Aby włączyć kontakty, wejdź do ustawień Twojego urządzenia."; +"contacts_address_book_permission_denied_alert_title" = "Kontakty wyłączone"; +"room_recents_suggested_rooms_section" = "SUGEROWANE POKOJE"; +"onboarding_splash_page_1_message" = "Bezpieczna i niezależna komunikacja dająca Tobie ten sam poziom prywatności jak rozmowa twarzą w twarz w Twoim domu."; +"onboarding_splash_login_button_title" = "Mam już konto"; + +// Onboarding +"onboarding_splash_register_button_title" = "Stwórz konto"; +"accessibility_button_label" = "przycisk"; +"done" = "Gotowe"; +"open" = "Otwórz"; +"enable" = "Włącz"; +"ok" = "OK"; diff --git a/Riot/Assets/pt_BR.lproj/InfoPlist.strings b/Riot/Assets/pt_BR.lproj/InfoPlist.strings index bb44cdb98..28cf8e2b8 100644 --- a/Riot/Assets/pt_BR.lproj/InfoPlist.strings +++ b/Riot/Assets/pt_BR.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSContactsUsageDescription" = "Element vai mostrar seus contatos para que você possa convidá-los a conversar."; "NSCalendarsUsageDescription" = "Ver suas reuniões agendadas no app."; "NSFaceIDUsageDescription" = "Face ID é usada para acessar seu app."; +"NSLocationWhenInUseUsageDescription" = "Quando você compartilha sua localização para pessoas, Element precisa de acesso para mostrar-lhes um mapa."; diff --git a/Riot/Assets/pt_BR.lproj/Localizable.strings b/Riot/Assets/pt_BR.lproj/Localizable.strings index 165ee183e..51f9faf5f 100644 --- a/Riot/Assets/pt_BR.lproj/Localizable.strings +++ b/Riot/Assets/pt_BR.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Notificação"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ compartilhou a localização dela(e)"; diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 7820fc1f4..8e6dbc79e 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -87,7 +87,7 @@ "auth_reset_password_error_unauthorized" = "Falha para verificar endereço de email: assegure que você clicou no link no email"; "auth_reset_password_error_not_found" = "Seu endereço de email não parece estar associado com uma ID Matrix neste servidorcasa."; "auth_reset_password_success_message" = "Sua senha tem sido resettada.\n\nVocê tem sido feito logout de todas as sessões e não vai mais receber notificações push. Para re-habilitar notificações, refaça login em cada dispositivo."; -"auth_add_email_and_phone_warning" = "Registro com email e número de telefone ao mesmo tempo não é suportado ainda até que a api exista. Somente o número de telefone vai ser levado em conta. Você pode adicionar seu email a seu perfil em configurações."; +"auth_add_email_and_phone_warning" = "Registro com email e número de telefone ao mesmo tempo não é suportado ainda até que a api exista. Somente o número de telefone vai ser levado em conta. Você pode adicionar seu email a seu perfil em ajustes."; "room_creation_appearance" = "Aparência"; "room_creation_appearance_name" = "Nome"; "room_creation_appearance_picture" = "Imagem de Chat (opcional)"; @@ -275,7 +275,7 @@ "room_preview_try_join_an_unknown_room" = "Você está tentando acessar %@. Você gostaria de se juntar para participar na discussão?"; "room_preview_try_join_an_unknown_room_default" = "uma sala"; // Settings -"settings_title" = "Configurações"; +"settings_title" = "Ajustes"; "account_logout_all" = "Fazer logout de todas as contas"; "settings_config_no_build_info" = "Nenhuma info de build"; "settings_mark_all_as_read" = "Marcar todas as mensagens como lidas"; @@ -283,7 +283,7 @@ "settings_config_home_server" = "Servidorcasa é %@"; "settings_config_identity_server" = "Servidor de identidade é %@"; "settings_config_user_id" = "Feito login como %@"; -"settings_user_settings" = "CONFIGURAÇÕES DE USUÁRIA(O)"; +"settings_user_settings" = "AJUSTES DE USUÁRIA(O)"; "settings_notifications_settings" = "CONFIGURAÇÕES DE NOTIFICAÇÃO"; "settings_calls_settings" = "CHAMADAS"; "settings_user_interface" = "INTERFACE DE USUÁRIA(O)"; @@ -316,7 +316,7 @@ "settings_fail_to_update_profile" = "Falha para atualizar perfil"; "settings_enable_push_notif" = "Notificações neste dispositivo"; "settings_show_decrypted_content" = "Mostrar conteúdo decriptado"; -"settings_global_settings_info" = "Configurações de notificação globais estão disponíveis em seu cliente web %@"; +"settings_global_settings_info" = "Ajustes de notificação globais estão disponíveis em seu cliente web %@"; "settings_enable_callkit" = "Chamamento integrado"; "settings_callkit_info" = "Receba chamadas entrantes em sua tela de bloqueio. Veja suas chamadas %@ no histórico de chamadas do sistema. Se iCloud está habilitado, este histórico de chamadas vai ser compartilhado com Apple."; "settings_ui_language" = "Língua"; @@ -361,7 +361,7 @@ "settings_deactivate_my_account" = "Desativar minha conta"; "room_details_people" = "Membros"; "room_details_files" = "Uploads"; -"room_details_settings" = "Configurações"; +"room_details_settings" = "Ajustes"; "room_details_photo" = "Foto de Sala"; "room_details_room_name" = "Nome de Sala"; "room_details_topic" = "Tópico"; @@ -482,7 +482,7 @@ "bug_report_prompt" = "O aplicativo tem crashado da última vez. Você gostaria de submeter um reporte de crash?"; "rage_shake_prompt" = "Você parece estar agitando o telefone em frustração. Você gostaria de submeter um reporte de bug?"; "do_not_ask_again" = "Não perguntar de novo"; -"camera_access_not_granted" = "%@ não tem permissão para usar Câmera, por favor mude configurações de privacidade"; +"camera_access_not_granted" = "%@ não tem permissão para usar Câmera, por favor mude ajustes de privacidade"; "large_badge_value_k_format" = "%.1fK"; // Call "call_incoming_voice_prompt" = "Chamada de voz entrante de %@"; @@ -497,7 +497,7 @@ // Crash report "google_analytics_use_prompt" = "Você gostaria de ajudar a melhorar %@ ao reportar automaticamente reportes de crash e dados de uso anônimos?"; // Crypto -"e2e_enabling_on_app_update" = "%@ agora suporta encriptação ponta-a-ponta mas você precisa fazer login de novo para habilitá-la.\n\nVocê pode fazê-lo agora ou mais tarde desde as configurações do aplicativo."; +"e2e_enabling_on_app_update" = "%@ agora suporta encriptação ponta-a-ponta mas você precisa fazer login de novo para habilitá-la.\n\nVocê pode fazê-lo agora ou mais tarde desde os ajustes do aplicativo."; "e2e_need_log_in_again" = "Você precisa fazer login de volta para gerar chaves de encriptação ponta-a-ponta para esta sessão e submeter a chave pública a seu servidorcasa.\nIsto é só desta vez; desculpe pela inconveniência."; // Bug report "bug_report_title" = "Report de Bug"; @@ -528,7 +528,7 @@ "widget_integration_room_not_visible" = "Sala %@ não está visível."; // Share extension "share_extension_auth_prompt" = "Faça login no app principal para compartilhar conteúdo"; -"share_extension_failed_to_encrypt" = "Falha para enviar. Cheque no app principal as configurações de encriptação para esta sala"; +"share_extension_failed_to_encrypt" = "Falha para enviar. Cheque no app principal os ajustes de encriptação para esta sala"; // Room key request dialog "e2e_room_key_request_title" = "Requisição de chave de encriptação"; "e2e_room_key_request_message_new_device" = "Você adicionou uma nova sessão '%@', que está requisitando chaves de encriptação."; @@ -586,8 +586,8 @@ "room_does_not_exist" = "%@ não existe"; // Key backup wrong version "e2e_key_backup_wrong_version_title" = "Novo Backup de Chave"; -"e2e_key_backup_wrong_version" = "Um novo backup de chave de mensagem segura tem sido detectado.\n\nSe isto não foi você, defina uma nova Frase de Segurança em Configurações."; -"e2e_key_backup_wrong_version_button_settings" = "Configurações"; +"e2e_key_backup_wrong_version" = "Um novo backup de chave de mensagem segura tem sido detectado.\n\nSe isto não foi você, defina uma nova Frase de Segurança em Ajustes."; +"e2e_key_backup_wrong_version_button_settings" = "Ajustes"; "e2e_key_backup_wrong_version_button_wasme" = "Foi eu"; "key_backup_setup_title" = "Backup de Chave"; "key_backup_setup_skip_alert_title" = "Você tem certeza?"; @@ -625,7 +625,7 @@ "key_backup_recover_title" = "Mensagens Seguras"; "key_backup_recover_invalid_passphrase_title" = "Frase de Segurança Incorreta"; "key_backup_recover_invalid_passphrase" = "Backup não pôde ser decriptado com esta frase: por favor verifique que você entrou a Frase de Segurança correta."; -"key_backup_recover_invalid_recovery_key_title" = "Disparidade de Chave de Segurança"; +"key_backup_recover_invalid_recovery_key_title" = "Correspondência Errada de Chave de Segurança"; "key_backup_recover_invalid_recovery_key" = "Backup não pôde ser decriptografado com esta chave: por favor verifique que você entrou a Chave de Segurança correta."; "key_backup_recover_from_passphrase_info" = "Use sua Frase de Segurança para destrancar seu histórico de mensagens seguras"; "key_backup_recover_from_passphrase_passphrase_title" = "Entrar"; @@ -638,7 +638,7 @@ "key_backup_recover_from_recovery_key_recovery_key_title" = "Entrar"; "key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Entrar Chave de Segurança"; "key_backup_recover_from_recovery_key_recover_action" = "Destrancar Histórico"; -"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Perdeu sua Chave de Segurança? Você pode configurar uma nova em configurações."; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Perdeu sua Chave de Segurança? Você pode configurar uma nova em ajustes."; "key_backup_recover_success_info" = "Backup Restaurado!"; "key_backup_recover_done_action" = "Feito"; "key_backup_setup_banner_title" = "Nunca perca mensagens criptografadas"; @@ -677,7 +677,7 @@ "secure_key_backup_setup_existing_backup_error_unlock_it" = "Destrancá-lo"; "secure_key_backup_setup_existing_backup_error_delete_it" = "Deletá-lo"; "secure_key_backup_setup_cancel_alert_title" = "Você tem certeza?"; -"secure_key_backup_setup_cancel_alert_message" = "Se você cancelar agora, você pode perder mensagens & dados encriptados se você perder acesso a seus logins.\n\nVocê também pode configurar Backup Seguro & gerenciar suas chaves em Configurações."; +"secure_key_backup_setup_cancel_alert_message" = "Se você cancelar agora, você pode perder mensagens & dados encriptados se você perder acesso a seus logins.\n\nVocê também pode configurar Backup Seguro & gerenciar suas chaves em Ajustes."; "secure_backup_setup_banner_title" = "Backup Seguro"; "secure_backup_setup_banner_subtitle" = "Salvaguardar-se contra perda de acesso a mensagens & dados encriptados"; // Recover from private key @@ -722,7 +722,7 @@ "settings_integrations_allow_button" = "Gerenciar integrações"; "settings_integrations_allow_description" = "Use um gerenciador de integrações (%@) para gerenciar bots, bridges, widgets e pacotes de stickers.\n\nGerenciadores de integrações recebem dados de configuração, e podem modificar widgets, enviar convites de sala e definir níveis de poder em seu nome."; "settings_add_3pid_password_title_email" = "Adicionar endereço de email"; -"settings_discovery_three_pids_management_information_part2" = "Configurações de Usuária(o)"; +"settings_discovery_three_pids_management_information_part2" = "Ajustes de Usuária(o)"; "settings_discovery_three_pids_management_information_part3" = "."; "settings_discovery_three_pid_details_share_action" = "Compartilhar"; "settings_discovery_three_pid_details_revoke_action" = "Revogar"; @@ -747,7 +747,7 @@ // Media picker "media_picker_title" = "Biblioteca de mídia"; "image_picker_action_library" = "Escolher de biblioteca"; -"photo_library_access_not_granted" = "%@ não tem permissão para acessar biblioteca de fotos, por favor mude configurações de privacidade"; +"photo_library_access_not_granted" = "%@ não tem permissão para acessar biblioteca de fotos, por favor mude ajustes de privacidade"; "room_widget_permission_display_name_permission" = "Seu nome de exibição"; "device_verification_security_advice_emoji" = "Compare os emoji únicos, garantindo que eles aparecem na mesma ordem."; "device_verification_self_verify_alert_message" = "Verifique o novo login acessando sua conta: %@"; @@ -794,14 +794,14 @@ "identity_server_settings_disconnect_info" = "Desconectar-se de seu servidor de identidade vai significar que você não vai ser descobertável por outras(os) usuárias(os) e ser capaz de convidar outras(os) por email ou telefone."; "identity_server_settings_alert_disconnect_still_sharing_3pid" = "Você ainda está compartilhando seus dados pessoais no servidor de identidade %@.\n\nNós recomendamos que você remova seus endereços de email e números de telefone do servidor de identidade antes de se desconectar."; "call_no_stun_server_error_title" = "Chamada falhou devido a servidor malconfigurado"; -"widget_integration_manager_disabled" = "Você precisa habilitar gerenciador de integrações em configurações"; +"widget_integration_manager_disabled" = "Você precisa habilitar gerenciador de integrações em ajustes"; "service_terms_modal_description_for_identity_server_1" = "Encontrar outras(os) por telefone ou email"; "service_terms_modal_description_for_identity_server_2" = "Ser encontrada(o) por telefone ou email"; "device_verification_self_verify_wait_additional_information" = "Isto funciona com %@ e outros clientes Matrix capazes de assinatura cruzada."; "key_verification_verified_user_information" = "Mensagens com esta(e) usuária(o) são encriptadas ponta-a-ponta e não podem ser lidas por terceiros."; "device_verification_emoji_smiley" = "Smiley"; // Generic errors -"error_invite_3pid_with_no_identity_server" = "Adicione um servidor de identidade em suas configurações para convidar por email."; +"error_invite_3pid_with_no_identity_server" = "Adicione um servidor de identidade em seus ajustes para convidar por email."; "key_verification_bootstrap_not_setup_message" = "Você precisa fazer bootstrap de assinatura cruzada primeiro."; "user_verification_sessions_list_user_trust_level_warning_title" = "Aviso"; "user_verification_sessions_list_information" = "Mensagens com esta(e) usuária(o) nesta sala são encriptadas ponta-a-ponta e não podem ser lidas por terceiros."; @@ -907,7 +907,7 @@ "event_formatter_widget_added_by_you" = "Você adicionou o widget: %@"; "event_formatter_widget_removed_by_you" = "Você removeu o widget: %@"; "camera_unavailable" = "A câmera está indisponível em seu dispositivo"; -"call_no_stun_server_error_message_2" = "Alternativamente, você pode tentar usar o servidor público em %@, mas isto não vai ser tão confiável, e vai compartilhar seu endereço de IP com esse servidor. Você também pode gerenciar isto em Configurações"; +"call_no_stun_server_error_message_2" = "Alternativamente, você pode tentar usar o servidor público em %@, mas isto não vai ser tão confiável, e vai compartilhar seu endereço de IP com esse servidor. Você também pode gerenciar isto em Ajustes"; "call_no_stun_server_error_use_fallback_button" = "Tentar usar %@"; // Widget "widget_no_integrations_server_configured" = "Nenhum servidor de integrações configurado"; @@ -974,7 +974,7 @@ "key_verification_verify_sas_cancel_action" = "Eles não correspondem"; "key_verification_verify_sas_validate_action" = "Eles correspondem"; "key_verification_verify_sas_additional_information" = "Para segurança ótima, use um outro meio de comunicação confiado ou faça isto em pessoa."; -"key_verification_manually_verify_device_instruction" = "Confirme ao comparar o seguinte com as Configurações de Usuária(o) em sua outra sessão:"; +"key_verification_manually_verify_device_instruction" = "Confirme ao comparar o seguinte com os Ajustes de Usuária(o) em sua outra sessão:"; "key_verification_manually_verify_device_name_title" = "Nome de sessão"; "key_verification_manually_verify_device_id_title" = "ID de sessão"; "key_verification_manually_verify_device_key_title" = "Chave de sessão"; @@ -1350,7 +1350,7 @@ "side_menu_app_version" = "Versão %@"; "side_menu_action_feedback" = "Feedback"; "side_menu_action_help" = "Ajuda"; -"side_menu_action_settings" = "Configurações"; +"side_menu_action_settings" = "Ajustes"; "side_menu_action_invite_friends" = "Convidar amigas(os)"; // Mark: - Side menu @@ -1370,13 +1370,13 @@ "security_settings_secure_backup_reset" = "Resettar"; "security_settings_secure_backup_info_valid" = "Esta sessão está fazendo backup de suas chaves."; "security_settings_secure_backup_info_checking" = "Checando…"; -"settings_ui_theme_picker_message_match_system_theme" = "\"Auto\" corresponde ao tema de sistema de seu dispositivo"; -"settings_ui_theme_picker_message_invert_colours" = "\"Auto\" usa as configurações \"Inverter Cores\" de seu dispositivo"; +"settings_ui_theme_picker_message_match_system_theme" = "\"Auto\" corresponde com o tema de sistema de seu dispositivo"; +"settings_ui_theme_picker_message_invert_colours" = "\"Auto\" usa os ajustes \"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_account_settings" = "Ajustes 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"; @@ -1395,7 +1395,7 @@ "voice_message_release_to_send" = "Segure para gravar, solte para enviar"; "settings_labs_voice_messages" = "Mensagens de voz"; "settings_notifications_disabled_alert_title" = "Notificações desabilitadas"; -"settings_notifications_disabled_alert_message" = "Para habilitar notificações, vá para suas configurações de dispositivo."; +"settings_notifications_disabled_alert_message" = "Para habilitar notificações, vá para os ajustes de seu dispositivo."; "settings_device_notifications" = "Notificações de dispositivo"; "event_formatter_call_incoming_video" = "Chamada de vídeo entrante"; "event_formatter_call_incoming_voice" = "Chamada de voz entrante"; @@ -1451,7 +1451,7 @@ "space_private_join_rule" = "Espaço privado"; "space_participants_action_ban" = "Banir deste espaço"; "space_participants_action_remove" = "Remover deste espaço"; -"spaces_coming_soon_detail" = "Esta funcionalidade não tem sido implementada ainda, mas está a caminho. Por enquanto, você pode fazer isso com Element em seu computador."; +"spaces_coming_soon_detail" = "Esta funcionalidade não tem sido implementada ainda, mas está a caminho. Por enquanto, você pode fazer isso com %@ em seu computador."; "spaces_invites_coming_soon_title" = "Convites chegando em breve"; "spaces_add_rooms_coming_soon_title" = "Adicionar salas chegando em breve"; "spaces_coming_soon_title" = "Chegando em breve"; @@ -1484,7 +1484,7 @@ "service_terms_modal_description_identity_server" = "Isto vai permitir alguém encontrar você se ela/ele tem seu número de telefone ou email salvo nos contatos de telefone dela/dele."; "service_terms_modal_table_header_integration_manager" = "TERMOS DE GERENCIADOR DE INTEGRAÇÕES"; "service_terms_modal_table_header_identity_server" = "TERMOS DE SERVIDOR DE IDENTIDADE"; -"service_terms_modal_footer" = "Isto pode ser desabilitado a qualquer hora em configurações."; +"service_terms_modal_footer" = "Isto pode ser desabilitado a qualquer hora em ajustes."; // Service terms "service_terms_modal_title_message" = "Para continuar, aceite os termos e condições abaixo"; @@ -1492,11 +1492,11 @@ "settings_contacts_enable_sync" = "Encontre seus contatos"; "settings_phone_contacts" = "CONTATOS DE TELEFONE"; "find_your_contacts_identity_service_error" = "Incapaz de se conectar ao servidor de identidade."; -"find_your_contacts_footer" = "Isto pode ser desabilitado a qualquer hora a partir de configurações."; +"find_your_contacts_footer" = "Isto pode ser desabilitado a qualquer hora a partir de ajustes."; "find_your_contacts_button_title" = "Encontre seus contatos"; "find_your_contacts_message" = "Deixe %@ mostrar seus contatos para que você possa rapidamente começar a conversar com aqueles que você conhece melhor."; "find_your_contacts_title" = "Comece por listar seus contatos"; -"contacts_address_book_permission_denied_alert_message" = "Para habilitar contatos, vá para as configurações de seu dispositivo."; +"contacts_address_book_permission_denied_alert_message" = "Para habilitar contatos, vá para os ajustes de seu dispositivo."; "contacts_address_book_permission_denied_alert_title" = "Contatos desabilitados"; "space_home_show_all_rooms" = "Mostrar todas as salas"; "room_event_action_forward" = "Encaminhar"; @@ -1539,7 +1539,7 @@ "analytics_prompt_stop" = "Parar de compartilhar"; "analytics_prompt_yes" = "Sim, pode ser"; "analytics_prompt_not_now" = "Não agora"; -"analytics_prompt_point_3" = "Você pode desativar isto a qualquer hora em configurações"; +"analytics_prompt_point_3" = "Você pode desativar isto a qualquer hora em ajustes"; /* Note: The word "don't" is formatted in bold */ "analytics_prompt_point_2" = "Nós não compartilhamos informação com terceiros"; /* Note: The word "don't" is formatted in bold */ @@ -1551,10 +1551,39 @@ /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Você pode ler todos os nossos termos %@."; "analytics_prompt_message_upgrade" = "Você previamente consentiu a compartilhar dados de uso anônimos conosco. Agora, para ajudar a entender como pessoas usam múltiplos dispositivos, nós vamos gerar um identificador aleatório, compartilhado por seus dispositivos."; -"analytics_prompt_message_new_user" = "Ajude-nos a identificar problemas e melhorar Element ao compartilhar dados de uso anônimos. Para entender como pessoas usam múltiplos dispositivos, nós geramos um identificador aleatório, compartilhado por seus dispositivos."; +"analytics_prompt_message_new_user" = "Ajude-nos a identificar problemas e melhorar %@ ao compartilhar dados de uso anônimos. Para entender como pessoas usam múltiplos dispositivos, nós geramos um identificador aleatório, compartilhado por seus dispositivos."; // Analytics -"analytics_prompt_title" = "Ajudar a melhorar %@"; +"analytics_prompt_title" = "Ajude a melhorar %@"; "settings_analytics_and_crash_data" = "Enviar dados de crash e analítica"; "accessibility_button_label" = "botão"; "enable" = "Habilitar"; +"location_sharing_settings_toggle_title" = "Habilitar compartilhamento de local"; +"location_sharing_settings_header" = "Compartilhamento de local"; +"location_sharing_invalid_authorization_error_title" = "%@ não tem permissão para acessar seu local. Você pode habilitar acesso em Ajustes > Local"; +"location_sharing_locating_user_error_title" = "%@ não pôde acessar seu local. Por favor tente de novo mais tarde."; + +// MARK: - Location sharing + +"location_sharing_title" = "Local"; +"location_sharing_open_google_maps" = "Abrir em Google Maps"; +"location_sharing_open_apple_maps" = "Abrir em Apple Mapas"; +"location_sharing_invalid_authorization_settings" = "Ajustes"; +"location_sharing_invalid_authorization_not_now" = "Não agora"; +"location_sharing_loading_map_error_title" = "%@ não pôde carregar o mapa. Por favor tente de novo mais tarde."; +"location_sharing_share_action" = "Compartilhar"; +"location_sharing_close_action" = "Fechar"; +"ok" = "OK"; +"onboarding_splash_page_1_message" = "Comunicação segura e independente que lhe dá o mesmo nível de privacidade que uma conversa face-a-face em sua própria casa."; +"settings_enable_room_message_bubbles" = "Bolhas de mensagem"; +"onboarding_splash_page_4_message" = "Elemente também é ótimo para o lugar de trabalho. É confiado pelas organizações mais seguras do mundo."; +"onboarding_splash_page_4_title_no_pun" = "Mensageria para seu time."; +"onboarding_splash_page_3_message" = "Encriptado ponta-a-ponta e nenhum número de telefone requerido. Sem publicidade ou datamining."; +"onboarding_splash_page_3_title" = "Mensageria segura."; +"onboarding_splash_page_2_message" = "Escolha onde suas conversas são mantidas, dando-lhe controle e independência. Conectado via Matrix."; +"onboarding_splash_page_2_title" = "Você está em controle."; +"onboarding_splash_page_1_title" = "Tenha posse de suas conversas."; +"onboarding_splash_login_button_title" = "Eu já tenho uma conta"; + +// Onboarding +"onboarding_splash_register_button_title" = "Criar conta"; diff --git a/Riot/Assets/sk.lproj/InfoPlist.strings b/Riot/Assets/sk.lproj/InfoPlist.strings index 9d8892825..704899a7f 100644 --- a/Riot/Assets/sk.lproj/InfoPlist.strings +++ b/Riot/Assets/sk.lproj/InfoPlist.strings @@ -7,3 +7,4 @@ "NSPhotoLibraryUsageDescription" = "Knižnica fotografií sa používa na odosielanie fotografií a videí."; // Permissions usage explanations "NSCameraUsageDescription" = "Fotoaparát sa používa na snímanie fotografií a videí, uskutočňovanie videohovorov."; +"NSLocationWhenInUseUsageDescription" = "Keď zdieľate svoju polohu s ľuďmi, Element potrebuje prístup, aby im mohol zobraziť mapu."; diff --git a/Riot/Assets/sk.lproj/Localizable.strings b/Riot/Assets/sk.lproj/Localizable.strings index 0ba5126ce..fffb34696 100644 --- a/Riot/Assets/sk.lproj/Localizable.strings +++ b/Riot/Assets/sk.lproj/Localizable.strings @@ -165,3 +165,6 @@ /* New message reply from a specific person in a named room. */ "REPLY_FROM_USER_IN_ROOM_TITLE" = "%@ odpovedal/a v %@"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ zdieľal/a svoju polohu"; diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index ded23fa74..b86434589 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -753,7 +753,7 @@ "secrets_recovery_with_passphrase_invalid_passphrase_title" = "Nie je možné získať prístup k tajnému úložisku"; "secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "použiť váš bezpečnostný kľúč"; "secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Neviete svoju bezpečnostnú frázu? Môžete "; -"secrets_recovery_with_passphrase_passphrase_placeholder" = "Vložiť bezpečnostnú frázu"; +"secrets_recovery_with_passphrase_passphrase_placeholder" = "Zadajte bezpečnostnú frázu"; "secrets_recovery_with_passphrase_information_verify_device" = "Na overenie tohto zariadenia použite bezpečnostnú frázu."; "user_verification_session_details_information_untrusted_current_user" = "Overte túto reláciu, aby ste ju označili za dôveryhodnú a udelili jej prístup k zašifrovaným správam:"; "user_verification_session_details_information_trusted_other_user_part2" = " ste ju overili:"; @@ -1186,7 +1186,7 @@ "space_home_show_all_rooms" = "Zobraziť všetky miestnosti"; "space_participants_action_ban" = "Vylúčiť z tohto priestoru"; "space_participants_action_remove" = "Odstrániť z tohto priestoru"; -"spaces_coming_soon_detail" = "Táto funkcia tu ešte nebola zapracovaná, ale je na ceste k jej zapracovaniu. Zatiaľ to môžete urobiť pomocou aplikácie Element v počítači."; +"spaces_coming_soon_detail" = "Táto funkcia tu ešte nebola zapracovaná, ale je na ceste k jej zapracovaniu. Zatiaľ to môžete urobiť pomocou %@ na vašom počítači."; "spaces_invites_coming_soon_title" = "Pozvánky už čoskoro"; "spaces_add_rooms_coming_soon_title" = "Pridávanie miestností už čoskoro"; "spaces_no_member_found_detail" = "Hľadáte niekoho, kto nie je v %@? Zatiaľ ich môžete pozvať na webe alebo na počítači."; @@ -1502,7 +1502,7 @@ "analytics_prompt_stop" = "Zastaviť zdieľanie"; "analytics_prompt_yes" = "Áno, je to v poriadku"; "analytics_prompt_message_upgrade" = "Predtým ste nám udelili súhlas so zdieľaním anonymných údajov o používaní. Teraz, aby sme pomohli pochopiť, ako ľudia používajú viacero zariadení, vygenerujeme náhodný identifikátor zdieľaný vašimi zariadeniami."; -"analytics_prompt_message_new_user" = "Pomôžte nám identifikovať problémy a zlepšiť Element zdieľaním anonymných údajov o používaní. Aby sme pochopili, ako ľudia používajú viacero zariadení, vygenerujeme náhodný identifikátor, ktorý zdieľajú vaše zariadenia."; +"analytics_prompt_message_new_user" = "Pomôžte nám identifikovať problémy a zlepšiť %@ zdieľaním anonymných údajov o používaní. Aby sme pochopili, ako ľudia používajú viacero zariadení, vygenerujeme náhodný identifikátor zdieľaný vašimi zariadeniami."; // Analytics "analytics_prompt_title" = "Pomôžte zlepšiť %@"; @@ -1619,3 +1619,48 @@ // Recover from recovery key "key_backup_recover_from_recovery_key_info" = "Použite svoju bezpečnostný kľúč na odomknutie histórie zabezpečených správ"; +"widget_sticker_picker_no_stickerpacks_alert" = "Momentálne nemáte aktívne žiadne balíčky s nálepkami."; +"widget_integrations_server_failed_to_connect" = "Nepodarilo sa pripojiť k integračnému serveru"; +"no_voip" = "%@ vám volá, ale %@ zatiaľ nepodporuje hovory.\nToto oznámenie môžete ignorovať a prijať hovor z iného zariadenia alebo ho môžete odmietnuť."; +"call_actions_unhold" = "Pokračovať"; +"call_jitsi_error" = "Nepodarilo sa pripojiť ku konferenčnému hovoru."; +"rage_shake_prompt" = "Zdá sa, že rozčúlene trasiete telefónom. Chceli by ste odoslať hlásenie o chybe?"; +"room_details_addresses_disable_main_address_prompt_msg" = "Nebudete mať zadanú žiadnu hlavnú adresu. Predvolená hlavná adresa pre túto miestnosť sa vyberie náhodne"; +"settings_enable_rageshake" = "Zúrivo potraste pre nahlásenie chyby"; +"settings_ui_theme_picker_message_match_system_theme" = "\"Auto\" zodpovedá systémovej téme vášho zariadenia"; +"settings_pin_rooms_with_unread" = "Pripnúť miestnosti s neprečítanými správami"; +"settings_pin_rooms_with_missed_notif" = "Pripnúť miestnosti so zmeškanými oznámeniami"; +"settings_confirm_media_size" = "Potvrdiť veľkosť pri odosielaní"; +"ok" = "OK"; +"poll_edit_form_question_or_topic" = "Otázka alebo téma"; +"poll_timeline_vote_not_registered_title" = "Hlas nebol zaznamenaný"; +"poll_timeline_vote_not_registered_subtitle" = "Je nám ľúto, váš hlas nebol zaznamenaný. Skúste to prosím znova"; +"poll_timeline_not_closed_title" = "Nepodarilo sa ukončiť anketu"; + +// MARK: - Location sharing + +"location_sharing_title" = "Poloha"; +"location_sharing_close_action" = "Zavrieť"; +"location_sharing_share_action" = "Zdieľať"; +"location_sharing_loading_map_error_title" = "%@ sa nepodarilo načítať mapu. Skúste to prosím neskôr."; +"location_sharing_locating_user_error_title" = "%@ sa nepodarilo získať prístup k vašej polohe. Skúste to prosím neskôr."; +"location_sharing_invalid_authorization_error_title" = "%@ nemá povolenie na prístup k vašej polohe. Prístup môžete povoliť v ponuke Nastavenia > Poloha"; +"location_sharing_invalid_authorization_not_now" = "Teraz nie"; +"location_sharing_invalid_authorization_settings" = "Nastavenia"; +"location_sharing_open_apple_maps" = "Otvoriť v službe Apple Mapy"; +"location_sharing_open_google_maps" = "Otvoriť v službe Mapy Google"; +"location_sharing_settings_header" = "Zdieľanie polohy"; +"location_sharing_settings_toggle_title" = "Povoliť zdieľanie polohy"; + +// Onboarding +"onboarding_splash_register_button_title" = "Vytvoriť účet"; +"onboarding_splash_login_button_title" = "Už mám účet"; +"onboarding_splash_page_1_title" = "Vlastnite svoje konverzácie."; +"onboarding_splash_page_1_message" = "Bezpečná a nezávislá komunikácia, ktorá vám poskytuje rovnakú úroveň súkromia ako rozhovor zoči-voči vo vašom vlastnom dome."; +"onboarding_splash_page_2_title" = "Vy máte všetko pod kontrolou."; +"onboarding_splash_page_2_message" = "Vyberte si, kde budú vaše rozhovory uložené, a získajte tak kontrolu a nezávislosť. Pripojené cez Matrix."; +"onboarding_splash_page_3_title" = "Zabezpečené zasielanie správ."; +"onboarding_splash_page_3_message" = "End-to-end šifrovanie a bez potreby telefónneho čísla. Žiadne reklamy ani zneužívanie údajov."; +"onboarding_splash_page_4_title_no_pun" = "Zasielanie správ pre váš tím."; +"onboarding_splash_page_4_message" = "Element je skvelý aj na pracovisku. Dôverujú mu najbezpečnejšie organizácie na svete."; +"settings_enable_room_message_bubbles" = "Správy v bublinách"; diff --git a/Riot/Assets/sq.lproj/InfoPlist.strings b/Riot/Assets/sq.lproj/InfoPlist.strings index ee2f2697a..bfeef7694 100644 --- a/Riot/Assets/sq.lproj/InfoPlist.strings +++ b/Riot/Assets/sq.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSContactsUsageDescription" = "Element-i do të shfaqë kontaktet tuaja, që kështu të mund t’i ftoni për të biseduar."; "NSCalendarsUsageDescription" = "Shihini te aplikacioni takimet tuaja të planifikuara."; "NSFaceIDUsageDescription" = "Face ID përdoret që të hyni në aplikacionin tuaj."; +"NSLocationWhenInUseUsageDescription" = "Kur ndani vendndodhjen tuaj me persona, Element-i ka nevojë për hyrje në të, që t’u trgojë atyre një hartë."; diff --git a/Riot/Assets/sq.lproj/Localizable.strings b/Riot/Assets/sq.lproj/Localizable.strings index 6bec05eba..ad7a81203 100644 --- a/Riot/Assets/sq.lproj/Localizable.strings +++ b/Riot/Assets/sq.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Njoftim"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ tregoi vendndodhjen e vet"; diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index bca45030c..9c8151004 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -1471,7 +1471,7 @@ "space_private_join_rule" = "Hapësirë private"; "space_participants_action_ban" = "Dëboje prej kësaj hapësire"; "space_participants_action_remove" = "Hiqe prej kësaj hapësire"; -"spaces_coming_soon_detail" = "Kjo veçori s’është sendërtuar këtu, por po bëhet. Tani për tani, mund ta bëni këtë me Element-in në kompjuterin tuaj."; +"spaces_coming_soon_detail" = "Kjo veçori s’është sendërtuar këtu, por po bëhet. Tani për tani, mund ta bëni këtë me %@-in në kompjuterin tuaj."; "spaces_invites_coming_soon_title" = "Ftesat vijnë së shpejti"; "spaces_add_rooms_coming_soon_title" = "Shtimi i dhomave vjen së shpejti"; "spaces_coming_soon_title" = "S’afërmi"; @@ -1565,7 +1565,7 @@ /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Mund të lexoni krejt kushtet tona %@."; "analytics_prompt_message_upgrade" = "Keni pranuar më herët të ndani me ne të dhëna anonime përdorimi. Tani, që të na ndihmoni të kuptojmë se si njerëzit përdorin pajisje të shumta, do të prodhojmë një identifikues kuturu, të përbashkët për pajisjet tuaja."; -"analytics_prompt_message_new_user" = "Ndihmonani të identifikojmë probleme dhe të përmirësojmë Element-in, duke ndarë me ne të dhëna anonime përdorimi. Për të kuptuar se si i përdorin njerëzit disa pajisje njëherësh, do të prodhojmë një identifikues kuturu, të përbashkët për pajisjet tuaja."; +"analytics_prompt_message_new_user" = "Ndihmonani të identifikojmë probleme dhe të përmirësojmë %@-in, duke ndarë me ne të dhëna anonime përdorimi. Për të kuptuar se si i përdorin njerëzit disa pajisje njëherësh, do të prodhojmë një identifikues kuturu, të përbashkët për pajisjet tuaja."; // Analytics "analytics_prompt_title" = "Ndihmoni të përmirësohet %@"; @@ -1579,3 +1579,32 @@ "accessibility_button_label" = "kopsë"; "open" = "Hapur"; "enable" = "Aktivizoje"; +"location_sharing_settings_toggle_title" = "Aktivizoni tregim vendndodhjeje"; +"location_sharing_settings_header" = "Tregim vendndodhjeje"; +"location_sharing_open_google_maps" = "Hape në Google Maps"; +"location_sharing_open_apple_maps" = "Hape në Apple Maps"; +"location_sharing_invalid_authorization_settings" = "Rregullime"; +"location_sharing_invalid_authorization_not_now" = "Jo tani"; +"location_sharing_locating_user_error_title" = "%@ s’njohu dot vendndodhjen tuaj. Ju lutemi, riprovoni më vonë."; +"location_sharing_invalid_authorization_error_title" = "%@ s’ka leje të njohë vendndodhjen tuaj. Njohjen mund ta lejoni që nga Rregullime > Vendndodhje"; +"location_sharing_loading_map_error_title" = "%@ s’ngarkoi dot hartën. Ju lutemi, riprovoni më vonë."; +"location_sharing_share_action" = "Ndajeni me të tjerë"; +"location_sharing_close_action" = "Mbylle"; + +// MARK: - Location sharing + +"location_sharing_title" = "Vendndodhje"; +"ok" = "OK"; +"settings_enable_room_message_bubbles" = "Flluska mesazhesh"; +"onboarding_splash_page_4_message" = "Element-i është gjithashtu i goditur për në punë. Është i besuar nga entet më të sigurta të botës."; +"onboarding_splash_page_4_title_no_pun" = "Shkëmbim mesazhesh për ekipin tuaj."; +"onboarding_splash_page_3_message" = "I fshehtëzuar skaj më skaj dhe pa u dashur numër telefoni. Pa reklama, apo shfrytëzim të dhënash."; +"onboarding_splash_page_3_title" = "Shkëmbim i sigurt mesazhesh."; +"onboarding_splash_page_2_message" = "Zgjidhni ku mbahen bisedat tuaja, duke ju dhënë kontroll dhe pavarësi. Të lidhur përmes Matrix-i."; +"onboarding_splash_page_2_title" = "Kontrollin e keni ju."; +"onboarding_splash_page_1_message" = "Komunikim i sigurt dhe i pavarur, që ju jep të njëjtën shkallë privatësie si biseda kokë më kokë, në shtëpinë tuaj."; +"onboarding_splash_page_1_title" = "Jini zot i bisedave tuaja."; +"onboarding_splash_login_button_title" = "Kam tashmë një llogari"; + +// Onboarding +"onboarding_splash_register_button_title" = "Krijoni llogari"; diff --git a/Riot/Assets/sv.lproj/InfoPlist.strings b/Riot/Assets/sv.lproj/InfoPlist.strings index 45ccee26b..4d03e8e0c 100644 --- a/Riot/Assets/sv.lproj/InfoPlist.strings +++ b/Riot/Assets/sv.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSMicrophoneUsageDescription" = "Element behöver åtkomst till din mikrofon för att kunna ringa och ta emot samtal samt spela in video och röstmeddelanden."; "NSContactsUsageDescription" = "Element kommer att visa dina kontakter så du kan bjuda in dem att chatta."; "NSFaceIDUsageDescription" = "Face ID används för att komma åt appen."; +"NSLocationWhenInUseUsageDescription" = "När du delar din plats med folk så behöver Element åtkomst för att visa dem en karta."; diff --git a/Riot/Assets/sv.lproj/Localizable.strings b/Riot/Assets/sv.lproj/Localizable.strings index 4883fb894..9c4df7f81 100644 --- a/Riot/Assets/sv.lproj/Localizable.strings +++ b/Riot/Assets/sv.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Avisering"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ delade sin plats"; diff --git a/Riot/Assets/third_party_licenses.html b/Riot/Assets/third_party_licenses.html index ee1a8d284..8d973a9a6 100644 --- a/Riot/Assets/third_party_licenses.html +++ b/Riot/Assets/third_party_licenses.html @@ -1925,6 +1925,31 @@ Library. SOFTWARE.

+
  • + UICollectionViewRightAlignedLayout (https://github.com/mokagio/UICollectionViewRightAlignedLayout) +

    + The MIT License (MIT) +

    + Copyright (c) 2014 Giovanni Lodi +

    + 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. +

    +
  • diff --git a/Riot/Assets/uk.lproj/InfoPlist.strings b/Riot/Assets/uk.lproj/InfoPlist.strings index 1923cbcbc..35a02253f 100644 --- a/Riot/Assets/uk.lproj/InfoPlist.strings +++ b/Riot/Assets/uk.lproj/InfoPlist.strings @@ -5,3 +5,4 @@ "NSContactsUsageDescription" = "Element покаже ваші контакти, щоб ви могли запросити їх до бесіди."; "NSCalendarsUsageDescription" = "Переглядайте свої заплановані зустрічі в додатку."; "NSFaceIDUsageDescription" = "Face ID використовується для доступу до вашого додатку."; +"NSLocationWhenInUseUsageDescription" = "Коли ви надсилаєте комусь дані про своє місцеперебування, Element потребує дозволу, щоб показати їм карту."; diff --git a/Riot/Assets/uk.lproj/Localizable.strings b/Riot/Assets/uk.lproj/Localizable.strings index 01f01d7ac..0ad0b3aa3 100644 --- a/Riot/Assets/uk.lproj/Localizable.strings +++ b/Riot/Assets/uk.lproj/Localizable.strings @@ -116,3 +116,6 @@ /** General **/ "NOTIFICATION" = "Сповіщення"; + +/* New file message from a specific person, not referencing a room. */ +"LOCATION_FROM_USER" = "%@ надсилає дані про своє місцеперебування"; diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 7694f2d5e..e85515b74 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -1414,7 +1414,7 @@ "spaces_no_room_found_detail" = "Деяких результатів може бути не видно, бо вони закриті й потребують запрошення."; "spaces_no_member_found_detail" = "Шукаєте когось, хто ще не в %@? Запросіть їх вебпереглядачем або комп'ютером."; "spaces_invites_coming_soon_title" = "Запрошення ще в розробці"; -"spaces_coming_soon_detail" = "Ця можливість тут іще не втілена, але скоро буде. Поки що можете зробити це в Element на комп'ютері."; +"spaces_coming_soon_detail" = "Ця можливість ще не втілена, але скоро буде. Поки що можете зробити це в %@ на комп'ютері."; "space_participants_action_remove" = "Вилучити з цього простору"; "space_participants_action_ban" = "Заблокувати в цьому просторі"; @@ -1476,7 +1476,7 @@ /* Note: The placeholder is for the contents of analytics_prompt_terms_link_new_user */ "analytics_prompt_terms_new_user" = "Можете прочитати всі наші умови %@."; "analytics_prompt_message_upgrade" = "Раніше ви погодилися надсилати нам анонімні дані про використання. Тепер, щоб розуміти, як люди використовують кілька пристроїв, ми створимо спільний для ваших пристроїв випадковий ідентифікатор."; -"analytics_prompt_message_new_user" = "Допомагайте нам визначати проблеми й удосконалювати Element, надсилаючи анонімні дані про використання. Щоб розуміти, як люди використовують кілька пристроїв, ми створимо спільний для ваших пристроїв випадковий ідентифікатор."; +"analytics_prompt_message_new_user" = "Допомагайте нам визначати проблеми й удосконалювати %@, надсилаючи анонімні дані про використання. Щоб розуміти, як люди використовують кілька пристроїв, ми створимо спільний для ваших пристроїв випадковий ідентифікатор."; // Analytics "analytics_prompt_title" = "Допоможіть удосконалити %@"; @@ -1702,3 +1702,32 @@ "room_details_flair_section" = "Показувати значки для спільнот"; "settings_flair" = "Показувати значок, де це дозволено"; "room_warning_about_encryption" = "Наскрізне шифрування ще на етапі бета-тестування й може бути ненадійним.\n\nПоки що не варто довіряти йому захист даних.\n\nПристрої ще не зможуть розшифрувати історію до того, як з них приєдналися до кімнати.\n\nЗашифровані повідомлення не буде показано у клієнтах, які ще не використовують шифрування."; +"location_sharing_settings_toggle_title" = "Увімкнути надсилання місцеперебування"; +"location_sharing_settings_header" = "Надсилання місцеперебування"; +"location_sharing_open_google_maps" = "Відкрити в Картах Google"; +"location_sharing_open_apple_maps" = "Відкрити в Apple Maps"; +"location_sharing_invalid_authorization_settings" = "Налаштування"; +"location_sharing_invalid_authorization_not_now" = "Не зараз"; +"location_sharing_invalid_authorization_error_title" = "%@ не має дозволу на доступ до вашого місцеперебування. Ви можете увімкнути доступ у Налаштування > Місцеперебування"; +"location_sharing_locating_user_error_title" = "%@ не може отримати доступ до вашого місцеперебування. Повторіть спробу пізніше."; +"location_sharing_loading_map_error_title" = "%@ не може завантажити карту. Повторіть спробу пізніше."; +"location_sharing_share_action" = "Поділиться"; +"location_sharing_close_action" = "Закрити"; + +// MARK: - Location sharing + +"location_sharing_title" = "Місцеперебування"; +"ok" = "Гаразд"; +"settings_enable_room_message_bubbles" = "Бульбашки повідомлень"; +"onboarding_splash_page_4_message" = "Елемент також чудово підходить для роботи. Йому довіряють найбезпечніші організації світу."; +"onboarding_splash_page_4_title_no_pun" = "Спілкування зі своєю командою."; +"onboarding_splash_page_3_message" = "Наскрізне шифрування та відсутність потреби у телефонному номері. Без реклами чи аналізу даних."; +"onboarding_splash_page_3_title" = "Безпечне спілкування."; +"onboarding_splash_page_2_message" = "Оберіть, де зберігати ваші бесіди, що дасть вам контроль і незалежність. Спілкуйтеся через Matrix."; +"onboarding_splash_page_2_title" = "Ви контролюєте все."; +"onboarding_splash_page_1_message" = "Безпечне та незалежне спілкування, яке дає вам такий же рівень приватності як особиста розмова у вашому власному домі."; +"onboarding_splash_page_1_title" = "Будьте господарем свого спілкування."; +"onboarding_splash_login_button_title" = "У мене вже є обліковий запис"; + +// Onboarding +"onboarding_splash_register_button_title" = "Створити обліковий запис"; diff --git a/Riot/Categories/MXEvent.swift b/Riot/Categories/MXEvent.swift new file mode 100644 index 000000000..5ba0c1c7d --- /dev/null +++ b/Riot/Categories/MXEvent.swift @@ -0,0 +1,29 @@ +// +// 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 MatrixSDK + +extension MXEvent { + + /// Get MXMessageType if any + var messageType: MXMessageType? { + guard let messageTypeString = self.content["msgtype"] as? String else { + return nil + } + return MXMessageType(identifier: messageTypeString) + } +} diff --git a/Riot/Categories/MXKRoomBubbleCellData+Riot.swift b/Riot/Categories/MXKRoomBubbleCellData+Riot.swift new file mode 100644 index 000000000..bfb7d1846 --- /dev/null +++ b/Riot/Categories/MXKRoomBubbleCellData+Riot.swift @@ -0,0 +1,30 @@ +// +// 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 + +extension MXKRoomBubbleCellData { + + // Indicate true if the cell data is collapsable and collapsed + var isCollapsableAndCollapsed: Bool { + return self.collapsable && self.collapsed + } + + var cellDataTag: RoomBubbleCellDataTag { + return RoomBubbleCellDataTag(rawValue: self.tag) ?? .message + } + +} diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index 24a0ff136..cc20ce9d0 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -67,6 +67,17 @@ extern NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePr */ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex; +/** + Add timestamp label for a component in receiver. + + Note: The label added here is automatically removed when [didEndDisplay] is called. + + @param componentIndex index of the component in bubble message data + @param displayOnLeft Indicate true to display label on left and false to display on right + */ +- (void)addTimestampLabelForComponent:(NSUInteger)componentIndex + displayOnLeft:(BOOL)displayOnLeft; + /** Highlight a component in receiver. diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 2d2a0dbfc..98fde6a11 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -36,6 +36,29 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = @implementation MXKRoomBubbleTableViewCell (Riot) - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex +{ + BOOL isFirstDisplayedComponent = (componentIndex == 0); + BOOL isLastMessageMostRecentComponent = NO; + + RoomBubbleCellData *roomBubbleCellData; + + if ([bubbleData isKindOfClass:RoomBubbleCellData.class]) + { + roomBubbleCellData = (RoomBubbleCellData*)bubbleData; + isFirstDisplayedComponent = (componentIndex == roomBubbleCellData.oldestComponentIndex); + isLastMessageMostRecentComponent = roomBubbleCellData.containsLastMessage && (componentIndex == roomBubbleCellData.mostRecentComponentIndex); + } + + // Display timestamp on the left for selected component when it cannot overlap other UI elements like user's avatar + BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible + && !isLastMessageMostRecentComponent + && (!isFirstDisplayedComponent || roomBubbleCellData.shouldHideSenderInformation); + + [self addTimestampLabelForComponent:componentIndex displayOnLeft:displayLabelOnLeft]; +} + +- (void)addTimestampLabelForComponent:(NSUInteger)componentIndex + displayOnLeft:(BOOL)displayLabelOnLeft { MXKRoomBubbleComponent *component; @@ -49,7 +72,6 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = if (component && component.date) { BOOL isFirstDisplayedComponent = (componentIndex == 0); - BOOL isLastMessageMostRecentComponent = NO; RoomBubbleCellData *roomBubbleCellData; @@ -57,14 +79,8 @@ NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = { roomBubbleCellData = (RoomBubbleCellData*)bubbleData; isFirstDisplayedComponent = (componentIndex == roomBubbleCellData.oldestComponentIndex); - isLastMessageMostRecentComponent = roomBubbleCellData.containsLastMessage && (componentIndex == roomBubbleCellData.mostRecentComponentIndex); } - // Display timestamp on the left for selected component when it cannot overlap other UI elements like user's avatar - BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible - && !isLastMessageMostRecentComponent - && ( !isFirstDisplayedComponent || roomBubbleCellData.shouldHideSenderInformation); - [self addTimestampLabelForComponentIndex:componentIndex isFirstDisplayedComponent:isFirstDisplayedComponent viewTag:componentIndex diff --git a/Riot/Categories/UIImage.swift b/Riot/Categories/UIImage.swift index 4b5a9703a..2faec2be1 100644 --- a/Riot/Categories/UIImage.swift +++ b/Riot/Categories/UIImage.swift @@ -59,6 +59,7 @@ extension UIImage { // Based on https://stackoverflow.com/a/31314494 @objc func vc_resized(with targetSize: CGSize) -> UIImage? { + let originalRenderingMode = self.renderingMode let size = self.size let widthRatio = targetSize.width/size.width @@ -79,7 +80,7 @@ extension UIImage { let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() - return newImage + return newImage?.withRenderingMode(originalRenderingMode) } @objc func vc_notRenderedImage() -> UIImage { diff --git a/Riot/Categories/UIView+Toast.swift b/Riot/Categories/UIView+Toast.swift new file mode 100644 index 000000000..e395578e3 --- /dev/null +++ b/Riot/Categories/UIView+Toast.swift @@ -0,0 +1,311 @@ +// +// 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 + +// MARK: - ToastPosition + +/// Vertical position for a toast +@objc +enum ToastPosition: Int { + /// Toast will be placed at the top of the screen, with a margin to the safe area insets of the superview. Max height is also limited with safe area insets. + case top + /// Toast will be placed at the middle of the screen vertically. Max height is also limited with safe area insets. + case middle + /// Toast will be placed at the bottom of the screen, with a margin to the safe area insets of the superview. Max height is also limited with safe area insets. + case bottom +} + +// MARK: - UIView Extension + +extension UIView { + + private enum Constants { + static let defaultDuration: TimeInterval = 2.0 + static let defaultPosition: ToastPosition = .bottom + } + + private static var operationQueue: OperationQueue = { + let queue = OperationQueue.vc_createSerialOperationQueue(name: "ToastQueue") + queue.qualityOfService = .userInteractive + queue.underlyingQueue = .main + return queue + }() + + /// Show a toast message with the given properties. + /// - Parameters: + /// - message: Message to be displayed + /// - image: Icon to be displayed. Placed left to the message. Will be tinted. + /// - duration: Duration of the toast messsage + /// - position: Vertical position of the toast message in the view. Toast view spans the receiver view horizontally, taking into account the safe area insets. + /// - additionalMargin: By default, a toast placed according to safe area insets, with a margin. + /// For `top` and `bottom` positions, adds toast an additional margin from the top and bottom respectively. + /// Has no effect for `middle` position. + @objc + func vc_toast(message: String?, + image: UIImage? = nil, + duration: TimeInterval = Constants.defaultDuration, + position: ToastPosition = Constants.defaultPosition, + additionalMargin: CGFloat = 0.0) { + let view = ToastView(withMessage: message, image: image) + vc_toast(view: view, duration: duration, position: position, additionalMargin: additionalMargin) + } + + /// Show a toast view with the given properties. + /// - Parameters: + /// - view: View to be displayed as a toast + /// - duration: Duration of the toast messsage + /// - position: Vertical position of the toast message in the view. Toast view spans the receiver view horizontally, taking into account the safe area insets. + /// - additionalMargin: By default, a toast placed according to safe area insets, with a margin. + /// For `top` and `bottom` positions, adds toast an additional margin from the top and bottom respectively. + /// Has no effect for `middle` position. + @objc + func vc_toast(view: UIView, + duration: TimeInterval = Constants.defaultDuration, + position: ToastPosition = Constants.defaultPosition, + additionalMargin: CGFloat = 0.0) { + let operation = ToastOperation(containerView: self, + toastView: view, + duration: duration, + position: position, + additionalMargin: additionalMargin, + completion: nil) + Self.operationQueue.addOperation(operation) + } + +} + +// MARK: - ToastOperation + +/// Async toast UI operation. Will run on the main thread. +private class ToastOperation: AsyncOperation { + + private enum Constants { + static let margin: UIEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + static let animationDuration: TimeInterval = 0.15 + static let timeBetweenToasts: TimeInterval = 0.5 + } + + private var containerView: UIView + private var toastView: UIView + private var duration: TimeInterval + private var position: ToastPosition + private var additionalMargin: CGFloat + private var completion: (() -> Void)? + private var timer: Timer? + + init(containerView: UIView, + toastView: UIView, + duration: TimeInterval, + position: ToastPosition, + additionalMargin: CGFloat, + completion: (() -> Void)? = nil) { + self.containerView = containerView + self.toastView = toastView + self.duration = duration + self.position = position + self.additionalMargin = additionalMargin + self.completion = completion + } + + override func main() { + showToast { + self.invalidateTimer() + let timer = Timer(timeInterval: self.duration, + target: self, + selector: #selector(self.timerFired(_:)), + userInfo: nil, + repeats: false) + RunLoop.main.add(timer, forMode: .common) + self.timer = timer + } + } + + @objc + private func timerFired(_ timer: Timer) { + invalidateTimer() + hideToast() + } + + private func showToast(_ completion: @escaping () -> Void) { + toastView.alpha = 0.0 + containerView.addSubview(toastView) + toastView.translatesAutoresizingMaskIntoConstraints = false + switch position { + case .top: + NSLayoutConstraint.activate([ + toastView.leadingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.leadingAnchor, + constant: Constants.margin.left), + toastView.topAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.topAnchor, + constant: Constants.margin.top + additionalMargin), + toastView.trailingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.trailingAnchor, + constant: -Constants.margin.right), + toastView.bottomAnchor.constraint(lessThanOrEqualTo: containerView.safeAreaLayoutGuide.bottomAnchor, + constant: -Constants.margin.bottom) + ]) + case .middle: + NSLayoutConstraint.activate([ + toastView.leadingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.leadingAnchor, + constant: Constants.margin.left), + toastView.topAnchor.constraint(greaterThanOrEqualTo: containerView.safeAreaLayoutGuide.topAnchor, + constant: Constants.margin.top), + toastView.trailingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.trailingAnchor, + constant: -Constants.margin.right), + toastView.bottomAnchor.constraint(lessThanOrEqualTo: containerView.safeAreaLayoutGuide.bottomAnchor, + constant: -Constants.margin.bottom), + toastView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor) + ]) + case .bottom: + NSLayoutConstraint.activate([ + toastView.leadingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.leadingAnchor, + constant: Constants.margin.left), + toastView.topAnchor.constraint(greaterThanOrEqualTo: containerView.safeAreaLayoutGuide.topAnchor, + constant: Constants.margin.top), + toastView.trailingAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.trailingAnchor, + constant: -Constants.margin.right), + toastView.bottomAnchor.constraint(equalTo: containerView.safeAreaLayoutGuide.bottomAnchor, + constant: -Constants.margin.bottom - additionalMargin) + ]) + } + + UIView.animate(withDuration: Constants.animationDuration, + delay: 0.0, + options: [.curveEaseOut, .allowUserInteraction], + animations: { + self.toastView.alpha = 1.0 + }, completion: { _ in + completion() + }) + } + + private func hideToast() { + UIView.animate(withDuration: Constants.animationDuration, + delay: 0.0, + options: [.curveEaseIn, .beginFromCurrentState], + animations: { + self.toastView.alpha = 0.0 + }, completion: { _ in + self.toastView.removeFromSuperview() + DispatchQueue.main.asyncAfter(deadline: .now() + Constants.timeBetweenToasts) { + self.finish() + self.completion?() + } + }) + } + + private func invalidateTimer() { + timer?.invalidate() + timer = nil + } + +} + +// MARK: - ToastView + +/// Default view for a basic toast. +private class ToastView: UIView, Themable { + + private enum Constants { + static let padding: UIEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) + static let cornerRadius: CGFloat = 8.0 + } + + private lazy var imageView: UIImageView = { + let view = UIImageView() + view.translatesAutoresizingMaskIntoConstraints = false + view.backgroundColor = .clear + return view + }() + + private lazy var messageLabel: UILabel = { + let label = UILabel() + label.font = ThemeService.shared().theme.fonts.body + label.backgroundColor = .clear + label.numberOfLines = 0 + label.textAlignment = .left + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private lazy var stackView: UIStackView = { + let result = UIStackView() + result.axis = .horizontal + result.distribution = .fill + result.alignment = .center + result.spacing = 8.0 + result.backgroundColor = .clear + + addSubview(result) + result.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + result.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Constants.padding.left), + result.topAnchor.constraint(equalTo: topAnchor, constant: Constants.padding.top), + result.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -Constants.padding.right), + result.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Constants.padding.bottom) + ]) + + return result + }() + + init(withMessage message: String?, + image: UIImage? = nil) { + super.init(frame: .zero) + + if let image = image { + imageView.image = image + NSLayoutConstraint.activate([ + imageView.widthAnchor.constraint(equalToConstant: image.size.width), + imageView.heightAnchor.constraint(equalToConstant: image.size.height) + ]) + stackView.addArrangedSubview(imageView) + } + + messageLabel.text = message + stackView.addArrangedSubview(messageLabel) + + stackView.layoutIfNeeded() + layer.cornerRadius = Constants.cornerRadius + layer.masksToBounds = true + registerThemeServiceDidChangeThemeNotification() + themeDidChange() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, + selector: #selector(themeDidChange), + name: .themeServiceDidChangeTheme, + object: nil) + } + + @objc + private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + // MARK: Themable + + func update(theme: Theme) { + backgroundColor = theme.colors.quinaryContent + imageView.tintColor = theme.colors.tertiaryContent + messageLabel.textColor = theme.colors.primaryContent + messageLabel.font = theme.fonts.body + } + +} diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index 7f5c44995..4248aeeaf 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -111,6 +111,14 @@ internal enum Asset { internal static let cameraStop = ImageAsset(name: "camera_stop") internal static let cameraVideoCapture = ImageAsset(name: "camera_video_capture") internal static let videoIcon = ImageAsset(name: "video_icon") + internal static let onboardingSplashScreenPage1 = ImageAsset(name: "OnboardingSplashScreenPage1") + internal static let onboardingSplashScreenPage1Dark = ImageAsset(name: "OnboardingSplashScreenPage1Dark") + internal static let onboardingSplashScreenPage2 = ImageAsset(name: "OnboardingSplashScreenPage2") + internal static let onboardingSplashScreenPage2Dark = ImageAsset(name: "OnboardingSplashScreenPage2Dark") + internal static let onboardingSplashScreenPage3 = ImageAsset(name: "OnboardingSplashScreenPage3") + internal static let onboardingSplashScreenPage3Dark = ImageAsset(name: "OnboardingSplashScreenPage3Dark") + internal static let onboardingSplashScreenPage4 = ImageAsset(name: "OnboardingSplashScreenPage4") + internal static let onboardingSplashScreenPage4Dark = ImageAsset(name: "OnboardingSplashScreenPage4Dark") internal static let peopleEmptyScreenArtwork = ImageAsset(name: "people_empty_screen_artwork") internal static let peopleEmptyScreenArtworkDark = ImageAsset(name: "people_empty_screen_artwork_dark") internal static let peopleFloatingAction = ImageAsset(name: "people_floating_action") @@ -136,6 +144,7 @@ internal enum Asset { internal static let roomContextMenuMore = ImageAsset(name: "room_context_menu_more") internal static let roomContextMenuReply = ImageAsset(name: "room_context_menu_reply") internal static let roomContextMenuRetry = ImageAsset(name: "room_context_menu_retry") + internal static let roomContextMenuThread = ImageAsset(name: "room_context_menu_thread") internal static let inputCloseIcon = ImageAsset(name: "input_close_icon") internal static let inputEditIcon = ImageAsset(name: "input_edit_icon") internal static let inputReplyIcon = ImageAsset(name: "input_reply_icon") @@ -156,7 +165,12 @@ internal enum Asset { internal static let pollDeleteOptionIcon = ImageAsset(name: "poll_delete_option_icon") internal static let pollEditIcon = ImageAsset(name: "poll_edit_icon") internal static let pollEndIcon = ImageAsset(name: "poll_end_icon") + internal static let pollTypeCheckboxDefault = ImageAsset(name: "poll_type_checkbox_default") + internal static let pollTypeCheckboxSelected = ImageAsset(name: "poll_type_checkbox_selected") internal static let pollWinnerIcon = ImageAsset(name: "poll_winner_icon") + internal static let threadsFilter = ImageAsset(name: "threads_filter") + internal static let threadsFilterApplied = ImageAsset(name: "threads_filter_applied") + internal static let threadsIcon = ImageAsset(name: "threads_icon") internal static let urlPreviewClose = ImageAsset(name: "url_preview_close") internal static let urlPreviewCloseDark = ImageAsset(name: "url_preview_close_dark") internal static let voiceMessageCancelGradient = ImageAsset(name: "voice_message_cancel_gradient") @@ -173,7 +187,9 @@ internal enum Asset { internal static let addParticipants = ImageAsset(name: "add_participants") internal static let detailsIcon = ImageAsset(name: "details_icon") internal static let editIcon = ImageAsset(name: "edit_icon") + internal static let fileAttachment = ImageAsset(name: "file_attachment") internal static let integrationsIcon = ImageAsset(name: "integrations_icon") + internal static let linkIcon = ImageAsset(name: "link_icon") internal static let mainAliasIcon = ImageAsset(name: "main_alias_icon") internal static let membersListIcon = ImageAsset(name: "members_list_icon") internal static let modIcon = ImageAsset(name: "mod_icon") diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index 07f71dfb9..ada7e6b05 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -284,6 +284,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: TemplateScreenViewController.self) } + internal enum ThreadListViewController: StoryboardType { + internal static let storyboardName = "ThreadListViewController" + + internal static let initialScene = InitialSceneType(storyboard: ThreadListViewController.self) + } internal enum UserVerificationSessionStatusViewController: StoryboardType { internal static let storyboardName = "UserVerificationSessionStatusViewController" diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 3783b566f..cb5516dca 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1451,6 +1451,10 @@ public class VectorL10n: NSObject { public static func eventFormatterMemberUpdates(_ p1: Int) -> String { return VectorL10n.tr("Vector", "event_formatter_member_updates", p1) } + /// Message deleted + public static var eventFormatterMessageDeleted: String { + return VectorL10n.tr("Vector", "event_formatter_message_deleted") + } /// (edited) public static var eventFormatterMessageEditedMention: String { return VectorL10n.tr("Vector", "event_formatter_message_edited_mention") @@ -1631,6 +1635,46 @@ public class VectorL10n: NSObject { public static var groupSection: String { return VectorL10n.tr("Vector", "group_section") } + /// Favourite + public static var homeContextMenuFavourite: String { + return VectorL10n.tr("Vector", "home_context_menu_favourite") + } + /// Leave + public static var homeContextMenuLeave: String { + return VectorL10n.tr("Vector", "home_context_menu_leave") + } + /// Low priority + public static var homeContextMenuLowPriority: String { + return VectorL10n.tr("Vector", "home_context_menu_low_priority") + } + /// Move to People + public static var homeContextMenuMakeDm: String { + return VectorL10n.tr("Vector", "home_context_menu_make_dm") + } + /// Move to Rooms + public static var homeContextMenuMakeRoom: String { + return VectorL10n.tr("Vector", "home_context_menu_make_room") + } + /// Mute + public static var homeContextMenuMute: String { + return VectorL10n.tr("Vector", "home_context_menu_mute") + } + /// Normal priority + public static var homeContextMenuNormalPriority: String { + return VectorL10n.tr("Vector", "home_context_menu_normal_priority") + } + /// Notifications + public static var homeContextMenuNotifications: String { + return VectorL10n.tr("Vector", "home_context_menu_notifications") + } + /// Remove from Favourites + public static var homeContextMenuUnfavourite: String { + return VectorL10n.tr("Vector", "home_context_menu_unfavourite") + } + /// Unmute + public static var homeContextMenuUnmute: String { + return VectorL10n.tr("Vector", "home_context_menu_unmute") + } /// The all-in-one secure chat app for teams, friends and organisations. Tap the + button below to add people and rooms. public static var homeEmptyViewInformation: String { return VectorL10n.tr("Vector", "home_empty_view_information") @@ -2259,6 +2303,14 @@ public class VectorL10n: NSObject { public static var locationSharingOpenGoogleMaps: String { return VectorL10n.tr("Vector", "location_sharing_open_google_maps") } + /// %@ could not send your location. Please try again later. + public static func locationSharingPostFailureSubtitle(_ p1: String) -> String { + return VectorL10n.tr("Vector", "location_sharing_post_failure_subtitle", p1) + } + /// We couldn’t send your location + public static var locationSharingPostFailureTitle: String { + return VectorL10n.tr("Vector", "location_sharing_post_failure_title") + } /// Location sharing public static var locationSharingSettingsHeader: String { return VectorL10n.tr("Vector", "location_sharing_settings_header") @@ -2351,6 +2403,10 @@ public class VectorL10n: NSObject { public static var mediaTypeAccessibilityVideo: String { return VectorL10n.tr("Vector", "media_type_accessibility_video") } + /// From a thread + public static var messageFromAThread: String { + return VectorL10n.tr("Vector", "message_from_a_thread") + } /// More public static var more: String { return VectorL10n.tr("Vector", "more") @@ -2575,6 +2631,26 @@ public class VectorL10n: NSObject { public static var pollEditFormPollQuestionOrTopic: String { return VectorL10n.tr("Vector", "poll_edit_form_poll_question_or_topic") } + /// Poll type + public static var pollEditFormPollType: String { + return VectorL10n.tr("Vector", "poll_edit_form_poll_type") + } + /// Closed poll + public static var pollEditFormPollTypeClosed: String { + return VectorL10n.tr("Vector", "poll_edit_form_poll_type_closed") + } + /// Results are only revealed when you end the poll + public static var pollEditFormPollTypeClosedDescription: String { + return VectorL10n.tr("Vector", "poll_edit_form_poll_type_closed_description") + } + /// Open poll + public static var pollEditFormPollTypeOpen: String { + return VectorL10n.tr("Vector", "poll_edit_form_poll_type_open") + } + /// Voters see results as soon as they have voted + public static var pollEditFormPollTypeOpenDescription: String { + return VectorL10n.tr("Vector", "poll_edit_form_poll_type_open_description") + } /// Please try again public static var pollEditFormPostFailureSubtitle: String { return VectorL10n.tr("Vector", "poll_edit_form_post_failure_subtitle") @@ -2588,6 +2664,14 @@ public class VectorL10n: NSObject { return VectorL10n.tr("Vector", "poll_edit_form_question_or_topic") } /// Please try again + public static var pollEditFormUpdateFailureSubtitle: String { + return VectorL10n.tr("Vector", "poll_edit_form_update_failure_subtitle") + } + /// Failed to update poll + public static var pollEditFormUpdateFailureTitle: String { + return VectorL10n.tr("Vector", "poll_edit_form_update_failure_title") + } + /// Please try again public static var pollTimelineNotClosedSubtitle: String { return VectorL10n.tr("Vector", "poll_timeline_not_closed_subtitle") } @@ -2707,6 +2791,14 @@ public class VectorL10n: NSObject { public static var roomAccessibilitySearch: String { return VectorL10n.tr("Vector", "room_accessibility_search") } + /// More + public static var roomAccessibilityThreadMore: String { + return VectorL10n.tr("Vector", "room_accessibility_thread_more") + } + /// Threads + public static var roomAccessibilityThreads: String { + return VectorL10n.tr("Vector", "room_accessibility_threads") + } /// Upload public static var roomAccessibilityUpload: String { return VectorL10n.tr("Vector", "room_accessibility_upload") @@ -3187,7 +3279,7 @@ public class VectorL10n: NSObject { public static var roomEventActionMore: String { return VectorL10n.tr("Vector", "room_event_action_more") } - /// Permalink + /// Copy link to message public static var roomEventActionPermalink: String { return VectorL10n.tr("Vector", "room_event_action_permalink") } @@ -3219,6 +3311,10 @@ public class VectorL10n: NSObject { public static var roomEventActionReply: String { return VectorL10n.tr("Vector", "room_event_action_reply") } + /// Thread + public static var roomEventActionReplyInThread: String { + return VectorL10n.tr("Vector", "room_event_action_reply_in_thread") + } /// Report content public static var roomEventActionReport: String { return VectorL10n.tr("Vector", "room_event_action_report") @@ -3251,10 +3347,18 @@ public class VectorL10n: NSObject { public static var roomEventActionViewEncryption: String { return VectorL10n.tr("Vector", "room_event_action_view_encryption") } + /// View in room + public static var roomEventActionViewInRoom: String { + return VectorL10n.tr("Vector", "room_event_action_view_in_room") + } /// View Source public static var roomEventActionViewSource: String { return VectorL10n.tr("Vector", "room_event_action_view_source") } + /// Link copied to clipboard. + public static var roomEventCopyLinkInfo: String { + return VectorL10n.tr("Vector", "room_event_copy_link_info") + } /// Failed to send public static var roomEventFailedToSend: String { return VectorL10n.tr("Vector", "room_event_failed_to_send") @@ -3311,6 +3415,10 @@ public class VectorL10n: NSObject { public static var roomIntroCellInformationRoomWithoutTopicSentence2Part2: String { return VectorL10n.tr("Vector", "room_intro_cell_information_room_without_topic_sentence2_part2") } + /// You do not have permission to invite people to this room + public static var roomInviteNotEnoughPermission: String { + return VectorL10n.tr("Vector", "room_invite_not_enough_permission") + } /// They won’t be a part of %@. public static func roomInviteToRoomOptionDetail(_ p1: String) -> String { return VectorL10n.tr("Vector", "room_invite_to_room_option_detail", p1) @@ -3839,6 +3947,10 @@ public class VectorL10n: NSObject { public static var roomSlideToEndGroupCall: String { return VectorL10n.tr("Vector", "room_slide_to_end_group_call") } + /// Thread + public static var roomThreadTitle: String { + return VectorL10n.tr("Vector", "room_thread_title") + } /// Invite members public static var roomTitleInviteMembers: String { return VectorL10n.tr("Vector", "room_title_invite_members") @@ -4827,6 +4939,10 @@ public class VectorL10n: NSObject { public static var settingsLabsEnableRingingForGroupCalls: String { return VectorL10n.tr("Vector", "settings_labs_enable_ringing_for_group_calls") } + /// Threaded messaging + public static var settingsLabsEnableThreads: String { + return VectorL10n.tr("Vector", "settings_labs_enable_threads") + } /// Polls public static var settingsLabsEnabledPolls: String { return VectorL10n.tr("Vector", "settings_labs_enabled_polls") @@ -5259,6 +5375,10 @@ public class VectorL10n: NSObject { public static var spaceHomeShowAllRooms: String { return VectorL10n.tr("Vector", "space_home_show_all_rooms") } + /// You do not have permission to invite people to this space + public static var spaceInviteNotEnoughPermission: String { + return VectorL10n.tr("Vector", "space_invite_not_enough_permission") + } /// Ban from this space public static var spaceParticipantsActionBan: String { return VectorL10n.tr("Vector", "space_participants_action_ban") @@ -5567,6 +5687,42 @@ public class VectorL10n: NSObject { public static var `switch`: String { return VectorL10n.tr("Vector", "switch") } + /// Copy link to thread + public static var threadCopyLinkToThread: String { + return VectorL10n.tr("Vector", "thread_copy_link_to_thread") + } + /// All threads + public static var threadsActionAllThreads: String { + return VectorL10n.tr("Vector", "threads_action_all_threads") + } + /// My threads + public static var threadsActionMyThreads: String { + return VectorL10n.tr("Vector", "threads_action_my_threads") + } + /// Threads help keep your conversations on-topic and easy to track. + public static var threadsEmptyInfoAll: String { + return VectorL10n.tr("Vector", "threads_empty_info_all") + } + /// Reply to an ongoing thread or tap a message and use “Thread” to start a new one. + public static var threadsEmptyInfoMy: String { + return VectorL10n.tr("Vector", "threads_empty_info_my") + } + /// Show all threads + public static var threadsEmptyShowAllThreads: String { + return VectorL10n.tr("Vector", "threads_empty_show_all_threads") + } + /// Tip: Tap a message and use “Thread” to start one. + public static var threadsEmptyTip: String { + return VectorL10n.tr("Vector", "threads_empty_tip") + } + /// Keep discussions organised with threads + public static var threadsEmptyTitle: String { + return VectorL10n.tr("Vector", "threads_empty_title") + } + /// Threads + public static var threadsTitle: String { + return VectorL10n.tr("Vector", "threads_title") + } /// Favourites public static var titleFavourites: String { return VectorL10n.tr("Vector", "title_favourites") diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 501ba8cfb..450e63497 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -136,6 +136,10 @@ final class RiotSettings: NSObject { @UserDefault(key: "enableRingingForGroupCalls", defaultValue: false, storage: defaults) var enableRingingForGroupCalls + /// Indicates if threads enabled in the timeline. + @UserDefault(key: "enableThreads", defaultValue: false, storage: defaults) + var enableThreads + // MARK: Calls /// Indicate if `allowStunServerFallback` settings has been set once. @@ -184,19 +188,17 @@ final class RiotSettings: NSObject { @UserDefault(key: "roomScreenAllowFilesAction", defaultValue: BuildSettings.roomScreenAllowFilesAction, storage: defaults) var roomScreenAllowFilesAction - - @UserDefault(key: "roomScreenAllowPollsAction", defaultValue: false, storage: defaults) - var roomScreenAllowPollsAction - - @UserDefault(key: "roomScreenAllowLocationAction", defaultValue: false, storage: defaults) - var roomScreenAllowLocationAction @UserDefault(key: "roomScreenShowsURLPreviews", defaultValue: true, storage: defaults) var roomScreenShowsURLPreviews - @UserDefault(key: "roomScreenEnableMessageBubbles", defaultValue: BuildSettings.roomScreenEnableMessageBubblesByDefault, storage: defaults) + @UserDefault(key: "roomScreenEnableMessageBubbles", defaultValue: BuildSettings.isRoomScreenEnableMessageBubblesByDefault, storage: defaults) var roomScreenEnableMessageBubbles + var roomTimelineStyleIdentifier: RoomTimelineStyleIdentifier { + return self.roomScreenEnableMessageBubbles ? .bubble : .plain + } + // MARK: - Room Contextual Menu @UserDefault(key: "roomContextualMenuShowMoreOptionForMessages", defaultValue: BuildSettings.roomContextualMenuShowMoreOptionForMessages, storage: defaults) diff --git a/Riot/Managers/Theme/Theme.swift b/Riot/Managers/Theme/Theme.swift index 8c6f52a66..2418cc967 100644 --- a/Riot/Managers/Theme/Theme.swift +++ b/Riot/Managers/Theme/Theme.swift @@ -32,6 +32,7 @@ import DesignKit var searchBackgroundColor: UIColor { get } var searchPlaceholderColor: UIColor { get } + var searchResultHighlightColor: UIColor { get } var headerBackgroundColor: UIColor { get } var headerBorderColor: UIColor { get } @@ -96,16 +97,22 @@ import DesignKit /// Color to use in shadows. Should be contrast to `backgroundColor`. var shadowColor: UIColor { get } + + // Timeline cells + + var roomCellIncomingBubbleBackgroundColor: UIColor { get } + + var roomCellOutgoingBubbleBackgroundColor: UIColor { get } // MARK: - Customisation methods - /// Apply the theme on a button. + /// Apply the theme on a tab bar. /// - /// - Parameter tabBar: The tabBar to customise. + /// - Parameter tabBar: The tab bar to customise. func applyStyle(onTabBar tabBar: UITabBar) - /// Apply the theme on a navigation bar + /// Apply the theme on a navigation bar. /// /// - Parameter navigationBar: the navigation bar to customise. func applyStyle(onNavigationBar navigationBar: UINavigationBar) diff --git a/Riot/Managers/Theme/ThemeService.m b/Riot/Managers/Theme/ThemeService.m index b205bbb6d..9d544ddce 100644 --- a/Riot/Managers/Theme/ThemeService.m +++ b/Riot/Managers/Theme/ThemeService.m @@ -52,7 +52,7 @@ NSString *const kThemeServiceDidChangeThemeNotification = @"kThemeServiceDidChan [self updateAppearance]; - [[NSNotificationCenter defaultCenter] postNotificationName:kThemeServiceDidChangeThemeNotification object:nil]; + [[NSNotificationCenter defaultCenter] postNotificationName:kThemeServiceDidChangeThemeNotification object:self]; } } @@ -146,6 +146,12 @@ NSString *const kThemeServiceDidChangeThemeNotification = @"kThemeServiceDidChan { [UIScrollView appearance].indicatorStyle = self.theme.scrollBarStyle; + // Remove the extra height added to section headers in iOS 15 + if (@available(iOS 15.0, *)) + { + UITableView.appearance.sectionHeaderTopPadding = 0; + } + // Define the navigation bar text color [[UINavigationBar appearance] setTintColor:self.theme.tintColor]; diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index b6aabc3ff..ebac661c8 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -33,6 +33,7 @@ class DarkTheme: NSObject, Theme { var searchBackgroundColor: UIColor = UIColor(rgb: 0x15191E) var searchPlaceholderColor: UIColor = UIColor(rgb: 0xA9B2BC) + var searchResultHighlightColor: UIColor = UIColor(rgb: 0xFCC639).withAlphaComponent(0.3) var headerBackgroundColor: UIColor = UIColor(rgb: 0x21262C) var headerBorderColor: UIColor = UIColor(rgb: 0x15191E) @@ -91,25 +92,53 @@ class DarkTheme: NSObject, Theme { var shadowColor: UIColor = UIColor(rgb: 0xFFFFFF) var messageTickColor: UIColor = .white + + var roomCellIncomingBubbleBackgroundColor: UIColor { + return self.colors.system + } + + var roomCellOutgoingBubbleBackgroundColor: UIColor = UIColor(rgb: 0x133A34) func applyStyle(onTabBar tabBar: UITabBar) { tabBar.unselectedItemTintColor = self.tabBarUnselectedItemTintColor tabBar.tintColor = self.tintColor tabBar.barTintColor = self.baseColor - tabBar.isTranslucent = false + + // Support standard scrollEdgeAppearance iOS 15 without visual issues. + if #available(iOS 15.0, *) { + tabBar.isTranslucent = true + } else { + tabBar.isTranslucent = false + } } - // Note: We are not using UINavigationBarAppearance on iOS 13+ atm because of UINavigationBar directly include UISearchBar on their titleView that cause crop issues with UINavigationController pop. + // Note: We are not using UINavigationBarAppearance on iOS 13/14 because of UINavigationBar directly including UISearchBar on their titleView that cause crop issues with UINavigationController pop. func applyStyle(onNavigationBar navigationBar: UINavigationBar) { navigationBar.tintColor = self.tintColor - navigationBar.titleTextAttributes = [ - NSAttributedString.Key.foregroundColor: self.textPrimaryColor - ] - navigationBar.barTintColor = self.baseColor - navigationBar.shadowImage = UIImage() // Remove bottom shadow - - // The navigation bar needs to be opaque so that its background color is the expected one - navigationBar.isTranslucent = false + + // On iOS 15 use UINavigationBarAppearance to fix visual issues with the scrollEdgeAppearance style. + if #available(iOS 15.0, *) { + let appearance = UINavigationBarAppearance() + + appearance.configureWithOpaqueBackground() + appearance.backgroundColor = self.baseColor + appearance.shadowColor = nil + appearance.titleTextAttributes = [ + NSAttributedString.Key.foregroundColor: self.textPrimaryColor + ] + + navigationBar.standardAppearance = appearance + navigationBar.scrollEdgeAppearance = appearance + } else { + navigationBar.titleTextAttributes = [ + NSAttributedString.Key.foregroundColor: self.textPrimaryColor + ] + navigationBar.barTintColor = self.baseColor + navigationBar.shadowImage = UIImage() // Remove bottom shadow + + // The navigation bar needs to be opaque so that its background color is the expected one + navigationBar.isTranslucent = false + } } func applyStyle(onSearchBar searchBar: UISearchBar) { diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index 545b801b9..2be798d23 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -33,6 +33,7 @@ class DefaultTheme: NSObject, Theme { var searchBackgroundColor: UIColor = UIColor(rgb: 0xFFFFFF) var searchPlaceholderColor: UIColor = UIColor(rgb: 0x8F97A3) + var searchResultHighlightColor: UIColor = UIColor(rgb: 0xFCC639).withAlphaComponent(0.2) var headerBackgroundColor: UIColor = UIColor(rgb: 0xF5F7FA) var headerBorderColor: UIColor = UIColor(rgb: 0xE9EDF1) @@ -100,24 +101,50 @@ class DefaultTheme: NSObject, Theme { var shadowColor: UIColor = UIColor(rgb: 0x000000) + var roomCellIncomingBubbleBackgroundColor: UIColor = UIColor(rgb: 0xE8EDF4) + + var roomCellOutgoingBubbleBackgroundColor: UIColor = UIColor(rgb: 0xE7F8F3) + func applyStyle(onTabBar tabBar: UITabBar) { tabBar.unselectedItemTintColor = self.tabBarUnselectedItemTintColor tabBar.tintColor = self.tintColor tabBar.barTintColor = self.baseColor - tabBar.isTranslucent = false + + // Support standard scrollEdgeAppearance iOS 15 without visual issues. + if #available(iOS 15.0, *) { + tabBar.isTranslucent = true + } else { + tabBar.isTranslucent = false + } } - // Note: We are not using UINavigationBarAppearance on iOS 13+ atm because of UINavigationBar directly include UISearchBar on their titleView that cause crop issues with UINavigationController pop. + // Note: We are not using UINavigationBarAppearance on iOS 13/14 because of UINavigationBar directly including UISearchBar on their titleView that cause crop issues with UINavigationController pop. func applyStyle(onNavigationBar navigationBar: UINavigationBar) { navigationBar.tintColor = self.tintColor - navigationBar.titleTextAttributes = [ - NSAttributedString.Key.foregroundColor: self.textPrimaryColor - ] - navigationBar.barTintColor = self.baseColor - navigationBar.shadowImage = UIImage() // Remove bottom shadow - - // The navigation bar needs to be opaque so that its background color is the expected one - navigationBar.isTranslucent = false + + // On iOS 15 use UINavigationBarAppearance to fix visual issues with the scrollEdgeAppearance style. + if #available(iOS 15.0, *) { + let appearance = UINavigationBarAppearance() + + appearance.configureWithOpaqueBackground() + appearance.backgroundColor = baseColor + appearance.shadowColor = nil + appearance.titleTextAttributes = [ + NSAttributedString.Key.foregroundColor: textPrimaryColor + ] + + navigationBar.standardAppearance = appearance + navigationBar.scrollEdgeAppearance = appearance + } else { + navigationBar.titleTextAttributes = [ + NSAttributedString.Key.foregroundColor: textPrimaryColor + ] + navigationBar.barTintColor = baseColor + navigationBar.shadowImage = UIImage() // Remove bottom shadow + + // The navigation bar needs to be opaque so that its background color is the expected one + navigationBar.isTranslucent = false + } } func applyStyle(onSearchBar searchBar: UISearchBar) { diff --git a/Riot/Managers/UserSessions/UserSessionsService.swift b/Riot/Managers/UserSessions/UserSessionsService.swift index b8618abb0..7a68d9752 100644 --- a/Riot/Managers/UserSessions/UserSessionsService.swift +++ b/Riot/Managers/UserSessions/UserSessionsService.swift @@ -155,7 +155,7 @@ class UserSessionsService: NSObject { let isSessionStateValid: Bool switch mxSession.state { - case .closed, .unknownToken: + case .closed: isSessionStateValid = false default: isSessionStateValid = true diff --git a/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift b/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift index f09d8608c..581133ce8 100644 --- a/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift +++ b/Riot/Model/HomeserverConfiguration/HomeserverConfiguration.swift @@ -16,19 +16,20 @@ import Foundation -/// Represents the homeserver configuration (usually based on HS Well-Known or hardoced values in the project) +/// Represents the homeserver configuration (usually based on HS Well-Known or hardcoded values in the project) @objcMembers final class HomeserverConfiguration: NSObject { // Note: Use an object per configuration subject when there is multiple properties related let jitsi: HomeserverJitsiConfiguration let isE2EEByDefaultEnabled: Bool + let tileServer: HomeserverTileServerConfiguration init(jitsi: HomeserverJitsiConfiguration, - isE2EEByDefaultEnabled: Bool) { + isE2EEByDefaultEnabled: Bool, + tileServer: HomeserverTileServerConfiguration) { self.jitsi = jitsi self.isE2EEByDefaultEnabled = isE2EEByDefaultEnabled - - super.init() + self.tileServer = tileServer } } diff --git a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift index a0eab4c13..ca87788b6 100644 --- a/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift +++ b/Riot/Model/HomeserverConfiguration/HomeserverConfigurationBuilder.swift @@ -59,12 +59,26 @@ final class HomeserverConfigurationBuilder: NSObject { jitsiServerURL = hardcodedJitsiServerURL } + // Tile server configuration + + let tileServerMapStyleURL: URL + if let mapStyleURLString = wellKnown?.tileServer?.mapStyleURLString, + let mapStyleURL = URL(string: mapStyleURLString) { + tileServerMapStyleURL = mapStyleURL + } else { + tileServerMapStyleURL = BuildSettings.tileServerMapStyleURL + } + + let tileServerConfiguration = HomeserverTileServerConfiguration(mapStyleURL: tileServerMapStyleURL) + // Create HomeserverConfiguration let jitsiConfiguration = HomeserverJitsiConfiguration(serverDomain: jitsiPreferredDomain, serverURL: jitsiServerURL) - return HomeserverConfiguration(jitsi: jitsiConfiguration, isE2EEByDefaultEnabled: isE2EEByDefaultEnabled) + return HomeserverConfiguration(jitsi: jitsiConfiguration, + isE2EEByDefaultEnabled: isE2EEByDefaultEnabled, + tileServer: tileServerConfiguration) } // MARK: - Private diff --git a/Riot/Model/HomeserverConfiguration/HomeserverTileServiceConfiguration.swift b/Riot/Model/HomeserverConfiguration/HomeserverTileServiceConfiguration.swift new file mode 100644 index 000000000..2e3d8cd44 --- /dev/null +++ b/Riot/Model/HomeserverConfiguration/HomeserverTileServiceConfiguration.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 + +/// `HomeserverTileServerConfiguration` defines tile server configuration to be used by the mapping library +@objcMembers +final class HomeserverTileServerConfiguration: NSObject { + let mapStyleURL: URL + + init(mapStyleURL: URL) { + self.mapStyleURL = mapStyleURL + } +} diff --git a/Riot/Modules/Analytics/Analytics.swift b/Riot/Modules/Analytics/Analytics.swift index 6a48f5c6c..d3b9c3ac1 100644 --- a/Riot/Modules/Analytics/Analytics.swift +++ b/Riot/Modules/Analytics/Analytics.swift @@ -23,11 +23,14 @@ import AnalyticsEvents /// ## Creating Analytics Events /// /// Events are managed in a shared repo for all Element clients https://github.com/matrix-org/matrix-analytics-events -/// To add a new event create a PR to that repo with the new/updated schema. +/// To add a new event create a PR to that repo with the new/updated schema. Element's Podfile has +/// a local version of the pod (commented out) for development purposes. /// Once merged into `main`, follow the steps below to integrate the changes into the project: -/// 1. Merge `main` into the `release/swift` branch. -/// 2. Run `bundle exec pod update AnalyticsEvents` to update the pod. -/// 3. Make sure to commit `Podfile.lock` with the new commit hash. +/// 1. Check if `main` contains any source breaking changes to the events. If so, please +/// wait until you are ready to merge your work into element-ios. +/// 2. Merge `main` into the `release/swift` branch. +/// 3. Run `bundle exec pod update AnalyticsEvents` to update the pod. +/// 4. Make sure to commit `Podfile.lock` with the new commit hash. /// @objcMembers class Analytics: NSObject { @@ -93,7 +96,7 @@ import AnalyticsEvents // Catch and log crashes MXLogger.logCrashes(true) - MXLogger.setBuildVersion(AppDelegate.theDelegate().build) + MXLogger.setBuildVersion(AppInfo.current.buildInfo.readableBuildVersion) } /// Use the analytics settings from the supplied session to configure analytics. @@ -210,6 +213,18 @@ extension Analytics { } } + /// Track when a user becomes unauthenticated without pressing the `sign out` button. + /// - Parameters: + /// - softLogout: Wether it was a soft/hard logout that was triggered. + /// - refreshTokenAuth: Wether it was either an access-token-based or refresh-token-based auth mechanism enabled. + /// - errorCode: The error code as returned by the homeserver that triggered the logout. + /// - errorReason: The reason for the error as returned by the homeserver that triggered the logout. + func trackAuthUnauthenticatedError(softLogout: Bool, refreshTokenAuth: Bool, errorCode: String, errorReason: String) { + let errorCode = AnalyticsEvent.UnauthenticatedError.ErrorCode(rawValue: errorCode) ?? .M_UNKNOWN + let event = AnalyticsEvent.UnauthenticatedError(errorCode: errorCode, errorReason: errorReason, refreshTokenAuth: refreshTokenAuth, softLogout: softLogout) + client.capture(event) + } + /// Track whether the user accepted or declined the terms to an identity server. /// **Note** This method isn't currently implemented. /// - Parameter accepted: Whether the terms were accepted. diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index fd6354ad3..3d62fdb16 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -1130,7 +1130,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [[NSNotificationCenter defaultCenter] postNotificationName:AppDelegateUniversalLinkDidChangeNotification object:nil]; } - if ([self handleServerProvionningLink:webURL]) + if ([self handleServerProvisioningLink:webURL]) { return YES; } @@ -1252,8 +1252,8 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni { NSString *fragment = universalLinkParameters.fragment; NSURL *universalLinkURL = universalLinkParameters.universalLinkURL; - ScreenPresentationParameters *screenPresentationParameters = universalLinkParameters.presentationParameters; - BOOL restoreInitialDisplay = screenPresentationParameters.restoreInitialDisplay; + ScreenPresentationParameters *presentationParameters = universalLinkParameters.presentationParameters; + BOOL restoreInitialDisplay = presentationParameters.restoreInitialDisplay; BOOL continueUserActivity = NO; MXKAccountManager *accountManager = [MXKAccountManager sharedManager]; @@ -1355,16 +1355,72 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni if (room.summary.roomType == MXRoomTypeSpace) { - SpaceNavigationParameters *spaceNavigationParameters = [[SpaceNavigationParameters alloc] initWithRoomId:room.roomId mxSession:account.mxSession presentationParameters:screenPresentationParameters]; + SpaceNavigationParameters *spaceNavigationParameters = [[SpaceNavigationParameters alloc] initWithRoomId:room.roomId mxSession:account.mxSession presentationParameters:presentationParameters]; [self showSpaceWithParameters:spaceNavigationParameters]; } else { // Open the room page - RoomNavigationParameters *roomNavigationParameters = [[RoomNavigationParameters alloc] initWithRoomId:roomId eventId:eventId mxSession:account.mxSession presentationParameters: screenPresentationParameters]; - - [self showRoomWithParameters:roomNavigationParameters]; + if (eventId) + { + __block MXEvent *event = [account.mxSession.store eventWithEventId:eventId inRoom:roomId]; + dispatch_group_t eventDispatchGroup = dispatch_group_create(); + + if (event == nil) + { + dispatch_group_enter(eventDispatchGroup); + // event doesn't exist in the store + [account.mxSession eventWithEventId:eventId + inRoom:roomId + success:^(MXEvent *eventFromServer) { + event = eventFromServer; + dispatch_group_leave(eventDispatchGroup); + } failure:^(NSError *error) { + dispatch_group_leave(eventDispatchGroup); + }]; + } + + dispatch_group_notify(eventDispatchGroup, dispatch_get_main_queue(), ^{ + if (event == nil) + { + return; + } + + ThreadParameters *threadParameters = nil; + if (RiotSettings.shared.enableThreads) + { + if (event.threadId) + { + threadParameters = [[ThreadParameters alloc] initWithThreadId:event.threadId + stackRoomScreen:NO]; + } + else if ([account.mxSession.threadingService threadWithId:eventId]) + { + threadParameters = [[ThreadParameters alloc] initWithThreadId:eventId + stackRoomScreen:NO]; + } + } + + RoomNavigationParameters *parameters = [[RoomNavigationParameters alloc] initWithRoomId:roomId + eventId:eventId + mxSession:account.mxSession + threadParameters:threadParameters + presentationParameters:presentationParameters]; + [self showRoomWithParameters:parameters]; + }); + } + else + { + // open the regular room timeline + RoomNavigationParameters *parameters = [[RoomNavigationParameters alloc] initWithRoomId:roomId + eventId:eventId + mxSession:account.mxSession + threadParameters:nil + presentationParameters:presentationParameters]; + + [self showRoomWithParameters:parameters]; + } } continueUserActivity = YES; @@ -1372,9 +1428,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni else { void(^findRoom)(void) = ^{ - if ([_masterTabBarController.selectedViewController isKindOfClass:MXKActivityHandlingViewController.class]) + if ([_masterTabBarController.selectedViewController conformsToProtocol:@protocol(MXKViewControllerActivityHandling)]) { - MXKActivityHandlingViewController *homeViewController = (MXKActivityHandlingViewController*)_masterTabBarController.selectedViewController; + UIViewController *homeViewController = (UIViewController*)_masterTabBarController.selectedViewController; [homeViewController startActivityIndicator]; @@ -1412,7 +1468,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni { universalLinkFragmentPendingRoomAlias = @{roomId: roomIdOrAlias}; - UniversalLinkParameters *newParameters = [[UniversalLinkParameters alloc] initWithFragment:newUniversalLinkFragment universalLinkURL:universalLinkURL presentationParameters:screenPresentationParameters]; + UniversalLinkParameters *newParameters = [[UniversalLinkParameters alloc] initWithFragment:newUniversalLinkFragment universalLinkURL:universalLinkURL presentationParameters:presentationParameters]; [self handleUniversalLinkWithParameters:newParameters]; } @@ -1471,14 +1527,14 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni roomPreviewData.viaServers = queryParams[@"via"]; } - RoomPreviewNavigationParameters *roomPreviewNavigationParameters = [[RoomPreviewNavigationParameters alloc] initWithPreviewData:roomPreviewData presentationParameters:screenPresentationParameters]; + RoomPreviewNavigationParameters *roomPreviewNavigationParameters = [[RoomPreviewNavigationParameters alloc] initWithPreviewData:roomPreviewData presentationParameters:presentationParameters]; [account.mxSession.matrixRestClient roomSummaryWith:roomIdOrAlias via:roomPreviewData.viaServers success:^(MXPublicRoom *room) { if ([room.roomTypeString isEqualToString:MXRoomTypeStringSpace]) { [homeViewController stopActivityIndicator]; - SpacePreviewNavigationParameters *spacePreviewNavigationParameters = [[SpacePreviewNavigationParameters alloc] initWithPublicRoom:room mxSession:account.mxSession presentationParameters:screenPresentationParameters]; + SpacePreviewNavigationParameters *spacePreviewNavigationParameters = [[SpacePreviewNavigationParameters alloc] initWithPublicRoom:room mxSession:account.mxSession presentationParameters:presentationParameters]; [self showSpacePreviewWithParameters:spacePreviewNavigationParameters]; } @@ -1556,7 +1612,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Create the contact related to this member MXKContact *contact = [[MXKContact alloc] initMatrixContactWithDisplayName:displayName andMatrixID:userId]; - [self showContact:contact presentationParameters:screenPresentationParameters]; + [self showContact:contact presentationParameters:presentationParameters]; continueUserActivity = YES; } @@ -1575,7 +1631,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni } // Display the group details - [self showGroup:group withMatrixSession:account.mxSession presentationParamters:screenPresentationParameters]; + [self showGroup:group withMatrixSession:account.mxSession presentationParamters:presentationParameters]; continueUserActivity = YES; } @@ -1592,7 +1648,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Check that 'fragment' has not been cancelled if ([universalLinkFragmentPending isEqualToString:fragment]) { - MXLogDebug(@"[AppDelegate] Universal link: The user is now logged in. Retry the link"); + MXLogDebug(@"[AppDelegate] Universal link: The user is now logged in. Retry the link"); [self handleUniversalLinkWithParameters:universalLinkParameters]; } }]; @@ -1604,7 +1660,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni MXLogDebug(@"[AppDelegate] Universal link with registration parameters"); continueUserActivity = YES; - [_masterTabBarController showAuthenticationScreenWithRegistrationParameters:queryParams]; + [_masterTabBarController showOnboardingFlowWithRegistrationParameters:queryParams]; } else { @@ -1651,11 +1707,13 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Try to get more information about the room before opening its preview [roomPreviewData peekInRoom:^(BOOL succeeded) { MXStrongifyAndReturnIfNil(self); - - MXKViewController *homeViewController = (MXKViewController*)self.masterTabBarController.selectedViewController; + if ([self.masterTabBarController.selectedViewController conformsToProtocol:@protocol(MXKViewControllerActivityHandling)]) + { + UIViewController *homeViewController = (UIViewController*)self.masterTabBarController.selectedViewController; - // Note: the activity indicator will not disappear if the session is not ready - [homeViewController stopActivityIndicator]; + // Note: the activity indicator will not disappear if the session is not ready + [homeViewController stopActivityIndicator]; + } // If no data is available for this room, we name it with the known room alias (if any). if (!succeeded && self->universalLinkFragmentPendingRoomAlias[roomIdOrAlias]) @@ -1742,33 +1800,36 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni *outQueryParams = queryParams; } - -- (BOOL)handleServerProvionningLink:(NSURL*)link +/** + Parse and handle a server provisioning link. Returns `YES` if a provisioning link was detected and handled. + @param link A link such as https://mobile.element.io/?hs_url=matrix.example.com&is_url=identity.example.com + */ +- (BOOL)handleServerProvisioningLink:(NSURL*)link { - MXLogDebug(@"[AppDelegate] handleServerProvionningLink: %@", link); + MXLogDebug(@"[AppDelegate] handleServerProvisioningLink: %@", link); NSString *homeserver, *identityServer; - [self parseServerProvionningLink:link homeserver:&homeserver identityServer:&identityServer]; + [self parseServerProvisioningLink:link homeserver:&homeserver identityServer:&identityServer]; if (homeserver) { if ([MXKAccountManager sharedManager].activeAccounts.count) { - [self displayServerProvionningLinkBuyAlreadyLoggedInAlertWithCompletion:^(BOOL logout) { + [self displayServerProvisioningLinkBuyAlreadyLoggedInAlertWithCompletion:^(BOOL logout) { - MXLogDebug(@"[AppDelegate] handleServerProvionningLink: logoutWithConfirmation: logout: %@", @(logout)); + MXLogDebug(@"[AppDelegate] handleServerProvisioningLink: logoutWithConfirmation: logout: %@", @(logout)); if (logout) { [self logoutWithConfirmation:NO completion:^(BOOL isLoggedOut) { - [self handleServerProvionningLink:link]; + [self handleServerProvisioningLink:link]; }]; } }]; } else { - [_masterTabBarController showAuthenticationScreen]; - [_masterTabBarController.authViewController showCustomHomeserver:homeserver andIdentityServer:identityServer]; + [_masterTabBarController showOnboardingFlow]; + [_masterTabBarController.onboardingCoordinatorBridgePresenter updateHomeserver:homeserver andIdentityServer:identityServer]; } return YES; @@ -1777,7 +1838,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni return NO; } -- (void)parseServerProvionningLink:(NSURL*)link homeserver:(NSString**)homeserver identityServer:(NSString**)identityServer +- (void)parseServerProvisioningLink:(NSURL*)link homeserver:(NSString**)homeserver identityServer:(NSString**)identityServer { if ([link.path isEqualToString:@"/"]) { @@ -1797,14 +1858,14 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni } else { - MXLogDebug(@"[AppDelegate] parseServerProvionningLink: Error: Unknown path: %@", link.path); + MXLogDebug(@"[AppDelegate] parseServerProvisioningLink: Error: Unknown path: %@", link.path); } - MXLogDebug(@"[AppDelegate] parseServerProvionningLink: homeserver: %@ - identityServer: %@", *homeserver, *identityServer); + MXLogDebug(@"[AppDelegate] parseServerProvisioningLink: homeserver: %@ - identityServer: %@", *homeserver, *identityServer); } -- (void)displayServerProvionningLinkBuyAlreadyLoggedInAlertWithCompletion:(void (^)(BOOL logout))completion +- (void)displayServerProvisioningLinkBuyAlreadyLoggedInAlertWithCompletion:(void (^)(BOOL logout))completion { // Ask confirmation self.logoutConfirmation = [UIAlertController alertControllerWithTitle:[VectorL10n errorUserAlreadyLoggedIn] message:nil preferredStyle:UIAlertControllerStyleAlert]; @@ -1963,7 +2024,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni [self removeMatrixSession:account.mxSession]; // Return to authentication screen - [self.masterTabBarController showAuthenticationScreenAfterSoftLogout:account.mxCredentials]; + [self.masterTabBarController showSoftLogoutOnboardingFlowWithCredentials:account.mxCredentials]; }]; [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionIgnoredUsersDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull notif) { @@ -2257,7 +2318,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni } // Return to authentication screen - [_masterTabBarController showAuthenticationScreen]; + [_masterTabBarController showOnboardingFlow]; // Note: Keep App settings // But enforce usage of member lazy loading @@ -2296,7 +2357,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni { BOOL isLaunching = NO; - if (_masterTabBarController.authenticationInProgress) + if (_masterTabBarController.isOnboardingInProgress) { MXLogDebug(@"[AppDelegate] handleAppState: Authentication still in progress"); @@ -2891,7 +2952,10 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni ScreenPresentationParameters *presentationParameters = [[ScreenPresentationParameters alloc] initWithRestoreInitialDisplay:YES]; RoomNavigationParameters *parameters = [[RoomNavigationParameters alloc] initWithRoomId:roomId - eventId:eventId mxSession:mxSession presentationParameters:presentationParameters]; + eventId:eventId + mxSession:mxSession + threadParameters:nil + presentationParameters:presentationParameters]; [self showRoomWithParameters:parameters]; } @@ -3413,7 +3477,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni @"party_id": mxSession.myDeviceId }; - [mxSession.matrixRestClient sendEventToRoom:event.roomId eventType:kMXEventTypeStringCallReject content:content txnId:nil success:nil failure:^(NSError *error) { + [mxSession.matrixRestClient sendEventToRoom:event.roomId threadId:nil eventType:kMXEventTypeStringCallReject content:content txnId:nil success:nil failure:^(NSError *error) { MXLogDebug(@"[AppDelegate] enableNoVoIPOnMatrixSession: ERROR: Cannot send m.call.reject event."); }]; @@ -3852,7 +3916,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni return; } - if (_masterTabBarController.authenticationInProgress) + if (_masterTabBarController.isOnboardingInProgress) { MXLogDebug(@"[AppDelegate][KeyVerification] keyVerificationNewRequestNotification: Postpone requests during the authentication process"); @@ -4494,15 +4558,15 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni - (BOOL)continueSSOLoginWithToken:(NSString*)loginToken txnId:(NSString*)txnId { - AuthenticationViewController *authVC = self.masterTabBarController.authViewController; + OnboardingCoordinatorBridgePresenter *bridgePresenter = self.masterTabBarController.onboardingCoordinatorBridgePresenter; - if (!authVC) + if (!bridgePresenter) { MXLogDebug(@"[AppDelegate] Fail to continue SSO login"); return NO; } - return [authVC continueSSOLoginWithToken:loginToken txnId:txnId]; + return [bridgePresenter continueSSOLoginWithToken:loginToken transactionID:txnId]; } #pragma mark - Private diff --git a/Riot/Modules/Application/ScreenNavigation/RoomNavigationParameters.swift b/Riot/Modules/Application/ScreenNavigation/RoomNavigationParameters.swift index 7308a5701..b44d00356 100644 --- a/Riot/Modules/Application/ScreenNavigation/RoomNavigationParameters.swift +++ b/Riot/Modules/Application/ScreenNavigation/RoomNavigationParameters.swift @@ -16,6 +16,24 @@ import Foundation +@objcMembers +class ThreadParameters: NSObject { + + /// If not nil, the thread will be opened on this room + let threadId: String + + /// If true, related room screen will be stacked in the navigation stack + let stackRoomScreen: Bool + + init(threadId: String, + stackRoomScreen: Bool) { + self.threadId = threadId + self.stackRoomScreen = stackRoomScreen + super.init() + } + +} + /// Navigation parameters to display a room with a provided identifier in a specific matrix session. @objcMembers class RoomNavigationParameters: NSObject { @@ -31,6 +49,9 @@ class RoomNavigationParameters: NSObject { /// The Matrix session in which the room should be available. let mxSession: MXSession + /// Navigation parameters for a thread + let threadParameters: ThreadParameters? + /// Screen presentation parameters. let presentationParameters: ScreenPresentationParameters @@ -39,10 +60,12 @@ class RoomNavigationParameters: NSObject { init(roomId: String, eventId: String?, mxSession: MXSession, + threadParameters: ThreadParameters?, presentationParameters: ScreenPresentationParameters) { self.roomId = roomId self.eventId = eventId self.mxSession = mxSession + self.threadParameters = threadParameters self.presentationParameters = presentationParameters super.init() diff --git a/Riot/Modules/Application/ScreenNavigation/RoomPreviewNavigationParameters.swift b/Riot/Modules/Application/ScreenNavigation/RoomPreviewNavigationParameters.swift index e59d2687f..3a9532c02 100644 --- a/Riot/Modules/Application/ScreenNavigation/RoomPreviewNavigationParameters.swift +++ b/Riot/Modules/Application/ScreenNavigation/RoomPreviewNavigationParameters.swift @@ -34,6 +34,7 @@ class RoomPreviewNavigationParameters: RoomNavigationParameters { super.init(roomId: previewData.roomId, eventId: previewData.eventId, mxSession: previewData.mxSession, + threadParameters: nil, presentationParameters: presentationParameters) } } diff --git a/Riot/Modules/Authentication/AuthenticationCoordinator.swift b/Riot/Modules/Authentication/AuthenticationCoordinator.swift new file mode 100644 index 000000000..a310cdb1a --- /dev/null +++ b/Riot/Modules/Authentication/AuthenticationCoordinator.swift @@ -0,0 +1,87 @@ +// File created from ScreenTemplate +// $ createScreen.sh Onboarding Authentication +/* + 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 + +/// A coordinator that handles authentication, verification and setting a PIN. +final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtocol { + + // MARK: - Properties + + // MARK: Private + + private let authenticationViewController: AuthenticationViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + var completion: (() -> Void)? + + // MARK: - Setup + + override init() { + let authenticationViewController = AuthenticationViewController() + self.authenticationViewController = authenticationViewController + + // Preload the view as this can a second and lock up the UI at presentation. + // The coordinator is initialised early in the onboarding flow to take advantage of this. + authenticationViewController.loadViewIfNeeded() + + super.init() + } + + // MARK: - Public + + func start() { + // Listen to the end of the authentication flow + authenticationViewController.authVCDelegate = self + } + + func toPresentable() -> UIViewController { + return self.authenticationViewController + } + + func update(authenticationType: MXKAuthenticationType) { + authenticationViewController.authType = authenticationType + } + + func update(externalRegistrationParameters: [AnyHashable: Any]) { + authenticationViewController.externalRegistrationParameters = externalRegistrationParameters + } + + func update(softLogoutCredentials: MXCredentials) { + authenticationViewController.softLogoutCredentials = softLogoutCredentials + } + + func updateHomeserver(_ homeserver: String?, andIdentityServer identityServer: String?) { + authenticationViewController.showCustomHomeserver(homeserver, andIdentityServer: identityServer) + } + + func continueSSOLogin(withToken loginToken: String, transactionID: String) -> Bool { + authenticationViewController.continueSSOLogin(withToken: loginToken, txnId: transactionID) + } +} + +// MARK: - AuthenticationViewControllerDelegate +extension AuthenticationCoordinator: AuthenticationViewControllerDelegate { + func authenticationViewControllerDidDismiss(_ authenticationViewController: AuthenticationViewController!) { + completion?() + } +} diff --git a/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift b/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift new file mode 100644 index 000000000..7037f9d1f --- /dev/null +++ b/Riot/Modules/Authentication/AuthenticationCoordinatorProtocol.swift @@ -0,0 +1,40 @@ +// File created from ScreenTemplate +// $ createScreen.sh Onboarding Authentication +/* + 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 + +/// `AuthenticationCoordinatorProtocol` is a protocol describing a Coordinator that handle's the authentication navigation flow. +protocol AuthenticationCoordinatorProtocol: Coordinator, Presentable { + var completion: (() -> Void)? { get set } + + /// Update the screen to display registration or login. + func update(authenticationType: MXKAuthenticationType) + + /// Force a registration process based on a predefined set of parameters from a server provisioning link. + /// For more information see `AuthenticationViewController.externalRegistrationParameters`. + func update(externalRegistrationParameters: [AnyHashable: Any]) + + /// Update the screen to use any credentials to use after a soft logout has taken place. + func update(softLogoutCredentials: MXCredentials) + + /// Set up the authentication screen with the specified homeserver and/or identity server. + func updateHomeserver(_ homeserver: String?, andIdentityServer identityServer: String?) + + /// When SSO login succeeded, when SFSafariViewController is used, continue login with success parameters. + func continueSSOLogin(withToken loginToken: String, transactionID: String) -> Bool +} diff --git a/Riot/Modules/Authentication/AuthenticationViewController.h b/Riot/Modules/Authentication/AuthenticationViewController.h index 133f0a867..894ea8b45 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.h +++ b/Riot/Modules/Authentication/AuthenticationViewController.h @@ -26,13 +26,6 @@ // MXKAuthenticationViewController has already a `delegate` member @property (nonatomic, weak) id authVCDelegate; -@property (weak, nonatomic) IBOutlet UIView *navigationBackView; -@property (weak, nonatomic) IBOutlet UINavigationBar *navigationBar; -@property (weak, nonatomic) IBOutlet UIView *navigationBarSeparatorView; - -@property (weak, nonatomic) IBOutlet UINavigationItem *mainNavigationItem; -@property (weak, nonatomic) IBOutlet UIBarButtonItem *rightBarButtonItem; - @property (weak, nonatomic) IBOutlet UIView *optionsContainer; @property (weak, nonatomic) IBOutlet UIButton *skipButton; diff --git a/Riot/Modules/Authentication/AuthenticationViewController.m b/Riot/Modules/Authentication/AuthenticationViewController.m index 67b2a9e8b..4f76a029b 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.m +++ b/Riot/Modules/Authentication/AuthenticationViewController.m @@ -126,8 +126,11 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; { [super viewDidLoad]; - self.mainNavigationItem.title = nil; - self.rightBarButtonItem.title = [VectorL10n authRegister]; + self.navigationItem.title = nil; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:VectorL10n.authRegister + style:UIBarButtonItemStylePlain + target:self + action:@selector(onButtonPressed:)]; self.defaultHomeServerUrl = RiotSettings.shared.homeserverUrlString; @@ -152,8 +155,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; if (!BuildSettings.authScreenShowRegister) { - self.rightBarButtonItem.enabled = NO; - self.rightBarButtonItem.title = nil; + self.navigationItem.rightBarButtonItem.enabled = NO; + self.navigationItem.rightBarButtonItem.title = nil; } self.serverOptionsContainer.hidden = !BuildSettings.authScreenShowCustomServerOptions; @@ -211,16 +214,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; - (void)userInterfaceThemeDidChange { - self.navigationBackView.backgroundColor = ThemeService.shared.theme.baseColor; - [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationBar]; - self.navigationBarSeparatorView.backgroundColor = ThemeService.shared.theme.lineBreakColor; - - // This view controller is not part of a navigation controller - // so that applyStyleOnNavigationBar does not fully work. - // In order to have the right status bar color, use the expected status bar color - // as the main view background color. - // Hopefully, subviews define their own background color with `theme.backgroundColor`, - // which makes all work together. + [ThemeService.shared.theme applyStyleOnNavigationBar:self.navigationController.navigationBar]; + self.view.backgroundColor = ThemeService.shared.theme.backgroundColor; self.authenticationScrollView.backgroundColor = ThemeService.shared.theme.backgroundColor; @@ -311,6 +306,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [super viewWillAppear:animated]; [_keyboardAvoider startAvoiding]; + [self.navigationController setNavigationBarHidden:NO animated:YES]; } - (void)viewDidAppear:(BOOL)animated @@ -337,7 +333,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; didCheckFalseAuthScreenDisplay = YES; MXLogDebug(@"[AuthenticationVC] viewDidAppear: Checking false logout"); - [[MXKAccountManager sharedManager] forceReloadAccounts]; + [MXKAccountManager sharedManagerWithReload: YES]; if ([MXKAccountManager sharedManager].activeAccounts.count) { // For now, we do not have better solution than forcing the user to restart the app @@ -479,14 +475,18 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; { BOOL hideAuthInputView = NO; - // Hide input view when there is only social login actions to present - if ((self.authType == MXKAuthenticationTypeLogin || self.authType == MXKAuthenticationTypeRegister) + // Hide input view when there is only social login actions to present at login + if ((self.authType == MXKAuthenticationTypeLogin) && self.currentLoginSSOFlow - && !self.isAuthSessionContainsPasswordFlow) + && !self.isAuthSessionContainsPasswordFlow + && BuildSettings.authScreenShowSocialLoginSection) { hideAuthInputView = YES; } + // Note: Registration will hide the input view in onFailureDuringMXOperation + // if registration has been disabled. + self.authInputsView.hidden = hideAuthInputView; } @@ -495,7 +495,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; super.userInteractionEnabled = userInteractionEnabled; // Reset - self.rightBarButtonItem.enabled = YES; + self.navigationItem.rightBarButtonItem.enabled = YES; // Show/Hide server options if (_optionsContainer.hidden == userInteractionEnabled) @@ -509,10 +509,10 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; if (!userInteractionEnabled) { // The right bar button is used to cancel the running request. - self.rightBarButtonItem.title = [VectorL10n cancel]; + self.navigationItem.rightBarButtonItem.title = [VectorL10n cancel]; // Remove the potential back button. - self.mainNavigationItem.leftBarButtonItem = nil; + self.navigationItem.leftBarButtonItem = nil; } else { @@ -529,18 +529,18 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; && !self.softLogoutCredentials && BuildSettings.authScreenShowRegister) { - self.rightBarButtonItem.title = [VectorL10n authRegister]; + self.navigationItem.rightBarButtonItem.title = [VectorL10n authRegister]; } else { // Disable register on SSO - self.rightBarButtonItem.enabled = NO; - self.rightBarButtonItem.title = nil; + self.navigationItem.rightBarButtonItem.enabled = NO; + self.navigationItem.rightBarButtonItem.title = nil; } } else if (self.authType == MXKAuthenticationTypeRegister) { - self.rightBarButtonItem.title = [VectorL10n authLogin]; + self.navigationItem.rightBarButtonItem.title = [VectorL10n authLogin]; // Restore the back button if (authInputsview) @@ -551,7 +551,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; else if (self.authType == MXKAuthenticationTypeForgotPassword) { // The right bar button is used to return to login. - self.rightBarButtonItem.title = [VectorL10n cancel]; + self.navigationItem.rightBarButtonItem.title = [VectorL10n cancel]; } } } @@ -561,14 +561,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; KeyVerificationCoordinatorBridgePresenter *keyVerificationCoordinatorBridgePresenter = [[KeyVerificationCoordinatorBridgePresenter alloc] initWithSession:session]; keyVerificationCoordinatorBridgePresenter.delegate = self; - if (self.navigationController) - { - [keyVerificationCoordinatorBridgePresenter pushCompleteSecurityFrom:self.navigationController isNewSignIn:YES animated:YES]; - } - else - { - [keyVerificationCoordinatorBridgePresenter presentCompleteSecurityFrom:self isNewSignIn:YES animated:YES]; - } + [keyVerificationCoordinatorBridgePresenter presentCompleteSecurityFrom:self isNewSignIn:YES animated:YES]; self.keyVerificationCoordinatorBridgePresenter = keyVerificationCoordinatorBridgePresenter; } @@ -578,17 +571,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; self.userInteractionEnabled = YES; [self.authenticationActivityIndicator stopAnimating]; - // Remove auth view controller on successful login - if (self.navigationController) - { - // Pop the view controller - [self.navigationController popViewControllerAnimated:YES]; - } - else - { - // Dismiss on successful login - [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; - } + // Dismiss (key verification) on successful login + [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; if (self.authVCDelegate) { @@ -606,7 +590,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [self loginWithToken:loginToken]; return YES; } - + MXLogDebug(@"[AuthenticationVC] Fail to continue SSO login"); return NO; } @@ -684,8 +668,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; // Customise the screen for soft logout self.customServersTickButton.hidden = YES; - self.rightBarButtonItem.title = nil; - self.mainNavigationItem.title = [VectorL10n authSoftlogoutSignedOut]; + self.navigationItem.rightBarButtonItem.title = nil; + self.navigationItem.title = [VectorL10n authSoftlogoutSignedOut]; [self showSoftLogoutClearDataContainer]; } @@ -829,14 +813,21 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; } } -- (void)handleAuthenticationSession:(MXAuthenticationSession *)authSession +- (void)refreshAuthenticationSession +{ + // Hide the social login buttons while the session refreshes + [self hideSocialLoginView]; + [super refreshAuthenticationSession]; +} + +- (void)handleAuthenticationSession:(MXAuthenticationSession *)authSession withFallbackSSOFlow:(MXLoginSSOFlow *)fallbackSSOFlow { // Make some cleaning from the server response according to what the app supports authSession = [self handleSupportedFlowsInAuthenticationSession:authSession]; - [super handleAuthenticationSession:authSession]; + [super handleAuthenticationSession:authSession withFallbackSSOFlow:fallbackSSOFlow]; - self.currentLoginSSOFlow = [self logginSSOFlowWithProvidersFromFlows:authSession.flows]; + self.currentLoginSSOFlow = [self loginSSOFlowWithProvidersFromFlows:authSession.flows] ?: fallbackSSOFlow; [self updateAuthInputViewVisibility]; [self updateSocialLoginViewVisibility]; @@ -892,27 +883,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; return NO; } -- (MXLoginSSOFlow*)logginSSOFlowWithProvidersFromFlows:(NSArray*)loginFlows -{ - MXLoginSSOFlow *ssoFlowWithProviders; - - for (MXLoginFlow *loginFlow in loginFlows) - { - if ([loginFlow isKindOfClass:MXLoginSSOFlow.class]) - { - MXLoginSSOFlow *ssoFlow = (MXLoginSSOFlow *)loginFlow; - - if (ssoFlow.identityProviders.count) - { - ssoFlowWithProviders = ssoFlow; - break; - } - } - } - - return ssoFlowWithProviders; -} - - (IBAction)onButtonPressed:(id)sender { if (sender == self.customServersTickButton) @@ -937,7 +907,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; self.authType = MXKAuthenticationTypeForgotPassword; } } - else if (sender == self.rightBarButtonItem) + else if (sender == self.navigationItem.rightBarButtonItem) { // Check whether a request is in progress if (!self.userInteractionEnabled) @@ -948,15 +918,15 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; else if (self.authType == MXKAuthenticationTypeLogin) { self.authType = MXKAuthenticationTypeRegister; - self.rightBarButtonItem.title = [VectorL10n authLogin]; + self.navigationItem.rightBarButtonItem.title = [VectorL10n authLogin]; } else { self.authType = MXKAuthenticationTypeLogin; - self.rightBarButtonItem.title = [VectorL10n authRegister]; + self.navigationItem.rightBarButtonItem.title = [VectorL10n authRegister]; } } - else if (sender == self.mainNavigationItem.leftBarButtonItem) + else if (sender == self.navigationItem.leftBarButtonItem) { if ([self.authInputsView isKindOfClass:AuthInputsView.class]) { @@ -1184,15 +1154,18 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; [self.submitButton setTitle:[VectorL10n authRegister] forState:UIControlStateNormal]; [self.submitButton setTitle:[VectorL10n authRegister] forState:UIControlStateHighlighted]; - self.mainNavigationItem.leftBarButtonItem = nil; + self.navigationItem.leftBarButtonItem = nil; } else { [self.submitButton setTitle:[VectorL10n authSubmit] forState:UIControlStateNormal]; [self.submitButton setTitle:[VectorL10n authSubmit] forState:UIControlStateHighlighted]; - UIBarButtonItem *leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"back_icon"] style:UIBarButtonItemStylePlain target:self action:@selector(onButtonPressed:)]; - self.mainNavigationItem.leftBarButtonItem = leftBarButtonItem; + UIBarButtonItem *leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:VectorL10n.back + style:UIBarButtonItemStylePlain + target:self + action:@selector(onButtonPressed:)]; + self.navigationItem.leftBarButtonItem = leftBarButtonItem; } } @@ -1735,8 +1708,8 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0; - (void)updateSocialLoginViewVisibility { SocialLoginButtonMode socialLoginButtonMode = SocialLoginButtonModeContinue; - - BOOL showSocialLoginView = self.currentLoginSSOFlow ? YES : NO; + + BOOL showSocialLoginView = BuildSettings.authScreenShowSocialLoginSection && (self.currentLoginSSOFlow ? YES : NO); switch (self.authType) { diff --git a/Riot/Modules/Authentication/AuthenticationViewController.xib b/Riot/Modules/Authentication/AuthenticationViewController.xib index 4080c755d..c779ced89 100644 --- a/Riot/Modules/Authentication/AuthenticationViewController.xib +++ b/Riot/Modules/Authentication/AuthenticationViewController.xib @@ -34,14 +34,9 @@ - - - - - @@ -59,35 +54,8 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -129,7 +97,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Threads/ThreadsCoordinator.swift b/Riot/Modules/Threads/ThreadsCoordinator.swift new file mode 100644 index 000000000..063c3a6c1 --- /dev/null +++ b/Riot/Modules/Threads/ThreadsCoordinator.swift @@ -0,0 +1,193 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Threads Threads ThreadList +/* + 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 + +@objcMembers +final class ThreadsCoordinator: NSObject, ThreadsCoordinatorProtocol { + + // MARK: - Properties + + // MARK: Private + + private let parameters: ThreadsCoordinatorParameters + private var selectedThreadCoordinator: RoomCoordinator? + + private var navigationRouter: NavigationRouterType { + return self.parameters.navigationRouter + } + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: ThreadsCoordinatorDelegate? + + // MARK: - Setup + + init(parameters: ThreadsCoordinatorParameters) { + self.parameters = parameters + super.init() + NotificationCenter.default.addObserver(self, + selector: #selector(didPopModule(_:)), + name: NavigationRouter.didPopModule, + object: nil) + } + + // MARK: - Public + + func start() { + + let rootCoordinator: Coordinator & Presentable + if let threadId = parameters.threadId { + rootCoordinator = createThreadCoordinator(forThreadId: threadId) + } else { + rootCoordinator = createThreadListCoordinator() + } + + rootCoordinator.start() + + self.add(childCoordinator: rootCoordinator) + + // Detect when view controller has been dismissed by gesture when presented modally (not in full screen). + self.navigationRouter.toPresentable().presentationController?.delegate = self + + if self.navigationRouter.modules.isEmpty == false { + self.navigationRouter.push(rootCoordinator, animated: true, popCompletion: { [weak self] in + self?.remove(childCoordinator: rootCoordinator) + }) + } else { + self.navigationRouter.setRootModule(rootCoordinator) { [weak self] in + self?.remove(childCoordinator: rootCoordinator) + } + } + } + + func stop() { + if selectedThreadCoordinator != nil { + let modules = self.navigationRouter.modules + // if a thread is selected from the thread list coordinator, then navigation stack will look like: + // ... -> Screen A -> Thread List Screen -> Thread Screen + // we'll try to pop to Screen A here + // sanity check: navigation stack contains at least 3 items + guard modules.count >= 3 else { + return + } + let moduleToGoBack = modules[modules.count - 3] + self.navigationRouter.popToModule(moduleToGoBack, animated: true) + } else { + self.navigationRouter.popModule(animated: true) + } + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } + + // MARK: - Private + + @objc + private func didPopModule(_ notification: Notification) { + guard let userInfo = notification.userInfo, + let module = userInfo[NavigationRouter.NotificationUserInfoKey.module] as? Presentable, + let selectedThreadCoordinator = selectedThreadCoordinator else { + return + } + + if module.toPresentable() == selectedThreadCoordinator.toPresentable() { + selectedThreadCoordinator.delegate = nil + remove(childCoordinator: selectedThreadCoordinator) + self.selectedThreadCoordinator = nil + } + } + + private func createThreadListCoordinator() -> ThreadListCoordinator { + let coordinatorParameters = ThreadListCoordinatorParameters(session: self.parameters.session, + roomId: self.parameters.roomId) + let coordinator = ThreadListCoordinator(parameters: coordinatorParameters) + coordinator.delegate = self + return coordinator + } + + private func createThreadCoordinator(forThreadId threadId: String) -> RoomCoordinator { + let parameters = RoomCoordinatorParameters(navigationRouter: navigationRouter, + navigationRouterStore: nil, + session: parameters.session, + parentSpaceId: nil, + roomId: parameters.roomId, + eventId: nil, + threadId: threadId, + displayConfiguration: .forThreads) + let coordinator = RoomCoordinator(parameters: parameters) + coordinator.delegate = self + return coordinator + } +} + +// MARK: - UIAdaptivePresentationControllerDelegate +extension ThreadsCoordinator: UIAdaptivePresentationControllerDelegate { + + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.delegate?.threadsCoordinatorDidDismissInteractively(self) + } +} + +// MARK: - ThreadListCoordinatorDelegate +extension ThreadsCoordinator: ThreadListCoordinatorDelegate { + func threadListCoordinatorDidLoadThreads(_ coordinator: ThreadListCoordinatorProtocol) { + + } + + func threadListCoordinatorDidSelectThread(_ coordinator: ThreadListCoordinatorProtocol, thread: MXThread) { + let roomCoordinator = createThreadCoordinator(forThreadId: thread.id) + selectedThreadCoordinator = roomCoordinator + roomCoordinator.start() + self.add(childCoordinator: roomCoordinator) + } + + func threadListCoordinatorDidSelectRoom(_ coordinator: ThreadListCoordinatorProtocol, roomId: String, eventId: String) { + self.delegate?.threadsCoordinatorDidSelect(self, roomId: roomId, eventId: eventId) + } + + func threadListCoordinatorDidCancel(_ coordinator: ThreadListCoordinatorProtocol) { + self.delegate?.threadsCoordinatorDidComplete(self) + } +} + +// MARK: - RoomCoordinatorDelegate + +extension ThreadsCoordinator: RoomCoordinatorDelegate { + + func roomCoordinatorDidLeaveRoom(_ coordinator: RoomCoordinatorProtocol) { + + } + + func roomCoordinatorDidCancelRoomPreview(_ coordinator: RoomCoordinatorProtocol) { + + } + + func roomCoordinator(_ coordinator: RoomCoordinatorProtocol, didSelectRoomWithId roomId: String, eventId: String?) { + self.delegate?.threadsCoordinatorDidSelect(self, roomId: roomId, eventId: eventId) + } + + func roomCoordinatorDidDismissInteractively(_ coordinator: RoomCoordinatorProtocol) { + + } + +} diff --git a/Riot/Modules/Threads/ThreadsCoordinatorBridgePresenter.swift b/Riot/Modules/Threads/ThreadsCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..43879b292 --- /dev/null +++ b/Riot/Modules/Threads/ThreadsCoordinatorBridgePresenter.swift @@ -0,0 +1,149 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Threads Threads ThreadList +/* + 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 ThreadsCoordinatorBridgePresenterDelegate { + func threadsCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: ThreadsCoordinatorBridgePresenter) + func threadsCoordinatorBridgePresenterDelegateDidSelect(_ coordinatorBridgePresenter: ThreadsCoordinatorBridgePresenter, + roomId: String, + eventId: String?) + func threadsCoordinatorBridgePresenterDidDismissInteractively(_ coordinatorBridgePresenter: ThreadsCoordinatorBridgePresenter) +} + +/// ThreadsCoordinatorBridgePresenter enables to start ThreadsCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (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 ThreadsCoordinatorBridgePresenter: NSObject { + + // MARK: - Constants + + private enum NavigationType { + case present + case push + } + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let roomId: String + private let threadId: String? + private var navigationType: NavigationType = .present + private var coordinator: ThreadsCoordinator? + + // MARK: Public + + weak var delegate: ThreadsCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + /// Initializer + /// - Parameters: + /// - session: Session instance + /// - roomId: Room identifier + /// - threadId: Thread identifier. Specified thread will be opened if provided, the thread list otherwise + init(session: MXSession, + roomId: String, + threadId: String?) { + self.session = session + self.roomId = roomId + self.threadId = threadId + 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 threadsCoordinatorParameters = ThreadsCoordinatorParameters(session: self.session, + roomId: self.roomId, + threadId: self.threadId) + + let threadsCoordinator = ThreadsCoordinator(parameters: threadsCoordinatorParameters) + threadsCoordinator.delegate = self + let presentable = threadsCoordinator.toPresentable() + viewController.present(presentable, animated: animated, completion: nil) + threadsCoordinator.start() + + self.coordinator = threadsCoordinator + self.navigationType = .present + } + + func push(from navigationController: UINavigationController, animated: Bool) { + + let navigationRouter = NavigationRouterStore.shared.navigationRouter(for: navigationController) + + let threadsCoordinatorParameters = ThreadsCoordinatorParameters(session: self.session, + roomId: self.roomId, + threadId: self.threadId, + navigationRouter: navigationRouter) + + let threadsCoordinator = ThreadsCoordinator(parameters: threadsCoordinatorParameters) + threadsCoordinator.delegate = self + threadsCoordinator.start() // Will trigger the view controller push + + self.coordinator = threadsCoordinator + self.navigationType = .push + } + + func dismiss(animated: Bool, completion: (() -> Void)?) { + guard let coordinator = self.coordinator else { + return + } + + switch navigationType { + case .present: + // Dismiss modal + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + + completion?() + } + case .push: + // stop coordinator to pop modules as needed + coordinator.stop() + self.coordinator = nil + + completion?() + } + } +} + +// MARK: - ThreadsCoordinatorDelegate +extension ThreadsCoordinatorBridgePresenter: ThreadsCoordinatorDelegate { + + func threadsCoordinatorDidComplete(_ coordinator: ThreadsCoordinatorProtocol) { + self.delegate?.threadsCoordinatorBridgePresenterDelegateDidComplete(self) + } + + func threadsCoordinatorDidSelect(_ coordinator: ThreadsCoordinatorProtocol, roomId: String, eventId: String?) { + self.delegate?.threadsCoordinatorBridgePresenterDelegateDidSelect(self, roomId: roomId, eventId: eventId) + } + + func threadsCoordinatorDidDismissInteractively(_ coordinator: ThreadsCoordinatorProtocol) { + self.delegate?.threadsCoordinatorBridgePresenterDidDismissInteractively(self) + } +} diff --git a/Riot/Modules/Threads/ThreadsCoordinatorParameters.swift b/Riot/Modules/Threads/ThreadsCoordinatorParameters.swift new file mode 100644 index 000000000..83a2d47be --- /dev/null +++ b/Riot/Modules/Threads/ThreadsCoordinatorParameters.swift @@ -0,0 +1,45 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Threads Threads ThreadList +/* + 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 + +/// ThreadsCoordinator input parameters +struct ThreadsCoordinatorParameters { + + /// The Matrix session + let session: MXSession + + /// Room identifier + let roomId: String + + /// Thread identifier. Specified thread will be opened if provided, the thread list otherwise + let threadId: String? + + /// The navigation router that manage physical navigation + let navigationRouter: NavigationRouterType + + init(session: MXSession, + roomId: String, + threadId: String?, + navigationRouter: NavigationRouterType? = nil) { + self.session = session + self.roomId = roomId + self.threadId = threadId + self.navigationRouter = navigationRouter ?? NavigationRouter(navigationController: RiotNavigationController()) + } +} diff --git a/Riot/Modules/Threads/ThreadsCoordinatorProtocol.swift b/Riot/Modules/Threads/ThreadsCoordinatorProtocol.swift new file mode 100644 index 000000000..69d6a90f2 --- /dev/null +++ b/Riot/Modules/Threads/ThreadsCoordinatorProtocol.swift @@ -0,0 +1,33 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Threads Threads ThreadList +/* + 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 ThreadsCoordinatorDelegate: AnyObject { + func threadsCoordinatorDidComplete(_ coordinator: ThreadsCoordinatorProtocol) + + func threadsCoordinatorDidSelect(_ coordinator: ThreadsCoordinatorProtocol, roomId: String, eventId: String?) + + /// Called when the view has been dismissed by gesture when presented modally (not in full screen). + func threadsCoordinatorDidDismissInteractively(_ coordinator: ThreadsCoordinatorProtocol) +} + +/// `ThreadsCoordinatorProtocol` is a protocol describing a Coordinator that handle xxxxxxx navigation flow. +protocol ThreadsCoordinatorProtocol: Coordinator, Presentable { + var delegate: ThreadsCoordinatorDelegate? { get } +} diff --git a/Riot/Routers/NavigationModule.swift b/Riot/Routers/NavigationModule.swift new file mode 100644 index 000000000..01c54bb98 --- /dev/null +++ b/Riot/Routers/NavigationModule.swift @@ -0,0 +1,36 @@ +// +// 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 + +/// Structure used to pass modules to routers with pop completion blocks. +struct NavigationModule { + /// Actual presentable of the module + let presentable: Presentable + + /// Block to be called when the module is popped + let popCompletion: (() -> Void)? +} + +// MARK: - CustomStringConvertible + +extension NavigationModule: CustomStringConvertible { + + var description: String { + return "NavigationModule: \(presentable), pop completion: \(String(describing: popCompletion))" + } + +} diff --git a/Riot/Routers/NavigationRouter.swift b/Riot/Routers/NavigationRouter.swift index 25a0aa333..715e1aa3c 100755 --- a/Riot/Routers/NavigationRouter.swift +++ b/Riot/Routers/NavigationRouter.swift @@ -117,13 +117,13 @@ final class NavigationRouter: NSObject, NavigationRouterType { self.didPushViewController(controller) } - func setModules(_ modules: [Presentable], hideNavigationBar: Bool, animated: Bool) { + func setModules(_ modules: [NavigationModule], hideNavigationBar: Bool, animated: Bool) { MXLog.debug("[NavigationRouter] Set modules \(modules)") - let controllers = modules.map { (presentable) -> UIViewController in - let controller = presentable.toPresentable() - self.addModule(presentable, for: controller) + let controllers = modules.map { (module) -> UIViewController in + let controller = module.presentable.toPresentable() + self.addModule(module.presentable, for: controller) return controller } @@ -147,8 +147,8 @@ final class NavigationRouter: NSObject, NavigationRouterType { } // Add again controller to module association, in case same modules instance are added back - modules.forEach { (presentable) in - self.addModule(presentable, for: presentable.toPresentable()) + modules.forEach { (module) in + self.addModule(module.presentable, for: module.presentable.toPresentable()) } controllers.forEach { @@ -221,6 +221,36 @@ final class NavigationRouter: NSObject, NavigationRouterType { self.didPushViewController(controller) } + func push(_ modules: [NavigationModule], animated: Bool) { + MXLog.debug("[NavigationRouter] Push modules \(modules)") + + // Avoid pushing any UINavigationController onto stack + guard modules.first(where: { $0.presentable.toPresentable() is UINavigationController }) == nil else { + MXLog.error("Cannot push a UINavigationController to NavigationRouter") + return + } + + for module in modules { + let controller = module.presentable.toPresentable() + self.addModule(module.presentable, for: controller) + + if let completion = module.popCompletion { + completions[controller] = completion + } + + self.willPushViewController(controller) + } + + var viewControllers = navigationController.viewControllers + viewControllers.append(contentsOf: modules.map({ $0.presentable.toPresentable() })) + navigationController.setViewControllers(viewControllers, animated: animated) + + for module in modules { + let controller = module.presentable.toPresentable() + self.didPushViewController(controller) + } + } + func popModule(animated: Bool = true) { MXLog.debug("[NavigationRouter] Pop module") @@ -338,7 +368,7 @@ extension NavigationRouter: UINavigationControllerDelegate { return } - MXLog.debug("[NavigationRouter] Poppped module: \(poppedViewController)") + MXLog.debug("[NavigationRouter] Popped module: \(poppedViewController)") self.didPopViewController(poppedViewController) } diff --git a/Riot/Routers/NavigationRouterStore.swift b/Riot/Routers/NavigationRouterStore.swift index 9aaca74c6..072798f55 100644 --- a/Riot/Routers/NavigationRouterStore.swift +++ b/Riot/Routers/NavigationRouterStore.swift @@ -49,12 +49,12 @@ class NavigationRouterStore: NavigationRouterStoreProtocol { return navigationRouter } - func findNavigationRouter(for navigationController: UINavigationController) -> NavigationRouterType? { + // MARK: - Private + + private func findNavigationRouter(for navigationController: UINavigationController) -> NavigationRouterType? { return self.navigationRouters[navigationController] } - // MARK: - Private - private func removeNavigationRouter(for navigationController: UINavigationController) { self.navigationRouters[navigationController] = nil } diff --git a/Riot/Routers/NavigationRouterType.swift b/Riot/Routers/NavigationRouterType.swift index c7c9aa2ec..f1275efa0 100755 --- a/Riot/Routers/NavigationRouterType.swift +++ b/Riot/Routers/NavigationRouterType.swift @@ -42,10 +42,10 @@ protocol NavigationRouterType: AnyObject, Presentable { /// Set view controllers stack of navigation controller /// - Parameters: - /// - modules: The presentables stack to set. + /// - modules: The modules stack to set. /// - hideNavigationBar: Specify true to hide the UINavigationBar. /// - animated: Specify true to animate the transition. - func setModules(_ modules: [Presentable], hideNavigationBar: Bool, animated: Bool) + func setModules(_ modules: [NavigationModule], hideNavigationBar: Bool, animated: Bool) /// Pop to root view controller of navigation controller and remove all others /// @@ -64,6 +64,12 @@ protocol NavigationRouterType: AnyObject, Presentable { /// - Parameter popCompletion: Completion called when `module` is removed from the navigation stack. func push(_ module: Presentable, animated: Bool, popCompletion: (() -> Void)?) + /// Push some view controllers on navigation controller stack + /// + /// - Parameter modules: Modules to push + /// - Parameter animated: Specify true to animate the transition. + func push(_ modules: [NavigationModule], animated: Bool) + /// Pop last view controller from navigation controller stack /// /// - Parameter animated: Specify true to animate the transition. @@ -93,7 +99,37 @@ extension NavigationRouterType { setRootModule(module, hideNavigationBar: false, animated: false, popCompletion: popCompletion) } + func setModules(_ modules: [NavigationModule], animated: Bool) { + setModules(modules, hideNavigationBar: false, animated: animated) + } + func setModules(_ modules: [Presentable], animated: Bool) { setModules(modules, hideNavigationBar: false, animated: animated) } + +} + +// MARK: - Presentable <--> NavigationModule Transitive Methods + +extension NavigationRouterType { + + func setRootModule(_ module: NavigationModule) { + setRootModule(module.presentable, popCompletion: module.popCompletion) + } + + func push(_ module: NavigationModule, animated: Bool) { + push(module.presentable, animated: animated, popCompletion: module.popCompletion) + } + + func setModules(_ modules: [Presentable], hideNavigationBar: Bool, animated: Bool) { + setModules(modules.map { $0.toModule() }, + hideNavigationBar: hideNavigationBar, + animated: animated) + } + + func push(_ modules: [Presentable], animated: Bool) { + push(modules.map { $0.toModule() }, + animated: animated) + } + } diff --git a/Riot/Routers/Presentable.swift b/Riot/Routers/Presentable.swift index fd42d3059..e9f760393 100755 --- a/Riot/Routers/Presentable.swift +++ b/Riot/Routers/Presentable.swift @@ -26,3 +26,13 @@ extension UIViewController: Presentable { return self } } + +extension Presentable { + + /// Returns a new module from the presentable without a pop completion block + /// - Returns: Module + func toModule() -> NavigationModule { + return NavigationModule(presentable: self, popCompletion: nil) + } + +} diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index df0f42f40..de80c7d17 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -47,6 +47,10 @@ #import "ShareItemSender.h" #import "Contact.h" #import "HTMLFormatter.h" +#import "RoomTimelineCellProvider.h" +#import "PlainRoomTimelineCellProvider.h" +#import "BubbleRoomTimelineCellProvider.h" +#import "RoomSelectedStickerBubbleCell.h" // MatrixKit common imports, shared with all targets #import "MatrixKit-Bridging-Header.h" diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index d97386aaa..efb79d40e 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -65,6 +65,38 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; - (NSAttributedString *)attributedStringFromEvent:(MXEvent *)event withRoomState:(MXRoomState *)roomState error:(MXKEventFormatterError *)error { + if (event.isRedactedEvent) + { + // Check whether the event is a thread root or redacted information is required + if ((RiotSettings.shared.enableThreads && [mxSession.threadingService isEventThreadRoot:event]) + || self.settings.showRedactionsInRoomHistory) + { + UIFont *font = self.defaultTextFont; + UIColor *color = ThemeService.shared.theme.colors.secondaryContent; + NSString *string = [NSString stringWithFormat:@" %@", VectorL10n.eventFormatterMessageDeleted]; + NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:string + attributes:@{ + NSFontAttributeName: font, + NSForegroundColorAttributeName: color + }]; + + CGSize imageSize = CGSizeMake(20, 20); + NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; + attachment.image = [[[UIImage imageNamed:@"room_context_menu_delete"] vc_resizedWith:imageSize] vc_tintedImageUsingColor:color]; + attachment.bounds = CGRectMake(0, font.descender, imageSize.width, imageSize.height); + NSAttributedString *imageString = [NSAttributedString attributedStringWithAttachment:attachment]; + + NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithAttributedString:imageString]; + [result appendAttributedString:attrString]; + + if (error) + { + *error = MXKEventFormatterErrorNone; + } + + return result; + } + } BOOL isEventSenderMyUser = [event.sender isEqualToString:mxSession.myUserId]; // Build strings for widget events diff --git a/Riot/target.yml b/Riot/target.yml index 81a912fbc..f8f52a7da 100644 --- a/Riot/target.yml +++ b/Riot/target.yml @@ -107,6 +107,9 @@ targets: - path: Assets/hu.lproj/InfoPlist.strings - path: Assets/hu.lproj/Localizable.strings - path: Assets/hu.lproj/Vector.strings + - path: Assets/id.lproj/InfoPlist.strings + - path: Assets/id.lproj/Localizable.strings + - path: Assets/id.lproj/Vector.strings - path: Assets/is.lproj/InfoPlist.strings - path: Assets/is.lproj/Localizable.strings - path: Assets/is.lproj/Vector.strings @@ -134,6 +137,9 @@ targets: - path: Assets/ru.lproj/InfoPlist.strings - path: Assets/ru.lproj/Localizable.strings - path: Assets/ru.lproj/Vector.strings + - path: Assets/sk.lproj/InfoPlist.strings + - path: Assets/sk.lproj/Localizable.strings + - path: Assets/sk.lproj/Vector.strings - path: Assets/sq.lproj/InfoPlist.strings - path: Assets/sq.lproj/Localizable.strings - path: Assets/sq.lproj/Vector.strings diff --git a/RiotNSE/NotificationService.swift b/RiotNSE/NotificationService.swift index c77a9dd86..0cba64e18 100644 --- a/RiotNSE/NotificationService.swift +++ b/RiotNSE/NotificationService.swift @@ -56,8 +56,14 @@ class NotificationService: UNNotificationServiceExtension { guard let userAccount = userAccount else { return nil } - return MXRestClient(credentials: userAccount.mxCredentials, unrecognizedCertificateHandler: nil) + let restClient = MXRestClient(credentials: userAccount.mxCredentials, unrecognizedCertificateHandler: nil, persistentTokenDataHandler: { persistTokenDataHandler in + MXKAccountManager.shared().readAndWriteCredentials(persistTokenDataHandler) + }, unauthenticatedHandler: { error, softLogout, refreshTokenAuth, completion in + userAccount.handleUnauthenticatedWithError(error, isSoftLogout: softLogout, isRefreshTokenAuth: refreshTokenAuth, andCompletion: completion) + }) + return restClient }() + private static var isLoggerInitialized: Bool = false private lazy var pushGatewayRestClient: MXPushGatewayRestClient = { let url = URL(string: BuildSettings.serverConfigSygnalAPIUrlString)! @@ -91,6 +97,8 @@ class NotificationService: UNNotificationServiceExtension { // log memory at the beginning of the process logMemory() + setupAnalytics() + UNUserNotificationCenter.current().removeUnwantedNotifications() // check if this is a Matrix notification @@ -164,15 +172,26 @@ class NotificationService: UNNotificationServiceExtension { } } + private func setupAnalytics(){ + // Configure our analytics. It will start if the option is enabled + let analytics = Analytics.shared + MXSDKOptions.sharedInstance().analyticsDelegate = analytics + analytics.startIfEnabled() + } + private func setup(withRoomId roomId: String, eventId: String, completion: @escaping () -> Void) { - MXKAccountManager.shared()?.forceReloadAccounts() + MXKAccountManager.sharedManager(withReload: true) self.userAccount = MXKAccountManager.shared()?.activeAccounts.first if let userAccount = userAccount { Self.backgroundServiceInitQueue.sync { if NotificationService.backgroundSyncService?.credentials != userAccount.mxCredentials { MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: BEFORE") self.logMemory() - NotificationService.backgroundSyncService = MXBackgroundSyncService(withCredentials: userAccount.mxCredentials) + NotificationService.backgroundSyncService = MXBackgroundSyncService(withCredentials: userAccount.mxCredentials, persistTokenDataHandler: { persistTokenDataHandler in + MXKAccountManager.shared().readAndWriteCredentials(persistTokenDataHandler) + }, unauthenticatedHandler: { error, softLogout, refreshTokenAuth, completion in + userAccount.handleUnauthenticatedWithError(error, isSoftLogout: softLogout, isRefreshTokenAuth: refreshTokenAuth, andCompletion: completion) + }) MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: AFTER") self.logMemory() } @@ -427,7 +446,7 @@ class NotificationService: UNNotificationServiceExtension { if event.isReply() { let parser = MXReplyEventParser() let replyParts = parser.parse(event) - notificationBody = replyParts.bodyParts.replyText + notificationBody = replyParts?.bodyParts.replyText } else { notificationBody = messageContent } diff --git a/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h b/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h index 41b0ba485..6409af92f 100644 --- a/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h +++ b/RiotNSE/SupportingFiles/RiotNSE-Bridging-Header.h @@ -21,4 +21,6 @@ #import "MatrixKit-Bridging-Header.h" +#import "BuildInfo.h" + #endif /* RiotNSE_Bridging_Header_h */ diff --git a/RiotNSE/target.yml b/RiotNSE/target.yml index de0dd69c1..68a86ca51 100644 --- a/RiotNSE/target.yml +++ b/RiotNSE/target.yml @@ -62,6 +62,8 @@ targets: - path: ../Riot/Managers/Widgets/WidgetConstants.m - path: ../Riot/PropertyWrappers/UserDefaultsBackedPropertyWrapper.swift - path: ../Riot/Modules/MatrixKit + - path: ../Riot/Modules/Analytics + - path: ../Riot/Managers/AppInfo/ excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Generated/MatrixKitStrings.swift diff --git a/RiotShareExtension/Shared/ForwardingShareItemSender.swift b/RiotShareExtension/Shared/ForwardingShareItemSender.swift index 84b866b27..3909812db 100644 --- a/RiotShareExtension/Shared/ForwardingShareItemSender.swift +++ b/RiotShareExtension/Shared/ForwardingShareItemSender.swift @@ -51,7 +51,7 @@ class ForwardingShareItemSender: NSObject, ShareItemSenderProtocol { dispatchGroup.enter() var localEcho: MXEvent? - room.sendMessage(withContent: event.content, localEcho: &localEcho) { result in + room.sendMessage(withContent: event.content, threadId: nil, localEcho: &localEcho) { result in switch result { case .failure(let innerError): errors.append(innerError) diff --git a/RiotShareExtension/Shared/ShareDataSource.m b/RiotShareExtension/Shared/ShareDataSource.m index 3c3ae631b..83889c333 100644 --- a/RiotShareExtension/Shared/ShareDataSource.m +++ b/RiotShareExtension/Shared/ShareDataSource.m @@ -81,8 +81,11 @@ NSMutableArray *cellData = [NSMutableArray array]; + MXRestClient *mxRestClient = [[MXRestClient alloc] initWithCredentials:self.credentials andOnUnrecognizedCertificateBlock:nil andPersistentTokenDataHandler:^(void (^handler)(NSArray *credentials, void (^completion)(BOOL didUpdateCredentials))) { + [[MXKAccountManager sharedManager] readAndWriteCredentials:handler]; + } andUnauthenticatedHandler:nil]; // Add a fake matrix session to each room summary to provide it a REST client (used to handle correctly the room avatar). - MXSession *session = [[MXSession alloc] initWithMatrixRestClient:[[MXRestClient alloc] initWithCredentials:self.credentials andOnUnrecognizedCertificateBlock:nil]]; + MXSession *session = [[MXSession alloc] initWithMatrixRestClient:mxRestClient]; for (id summary in summaries) { diff --git a/RiotShareExtension/Shared/ShareManager.m b/RiotShareExtension/Shared/ShareManager.m index d955f9da1..d24289188 100644 --- a/RiotShareExtension/Shared/ShareManager.m +++ b/RiotShareExtension/Shared/ShareManager.m @@ -78,7 +78,14 @@ - (void)shareViewController:(ShareViewController *)shareViewController didRequestShareForRoomIdentifiers:(NSSet *)roomIdentifiers { - MXSession *session = [[MXSession alloc] initWithMatrixRestClient:[[MXRestClient alloc] initWithCredentials:self.userAccount.mxCredentials andOnUnrecognizedCertificateBlock:nil]]; + MXWeakify(self); + MXRestClient *restClient = [[MXRestClient alloc] initWithCredentials:self.userAccount.mxCredentials andOnUnrecognizedCertificateBlock:nil andPersistentTokenDataHandler:^(void (^handler)(NSArray *credentials, void (^completion)(BOOL didUpdateCredentials))) { + [[MXKAccountManager sharedManager] readAndWriteCredentials:handler]; + } andUnauthenticatedHandler:^(MXError *error, BOOL isSoftLogout, BOOL isRefreshTokenAuth, void (^completion)(void)) { + MXStrongifyAndReturnIfNil(self); + [self.userAccount handleUnauthenticatedWithError:error isSoftLogout:isSoftLogout isRefreshTokenAuth:isRefreshTokenAuth andCompletion:completion]; + }]; + MXSession *session = [[MXSession alloc] initWithMatrixRestClient:restClient]; [MXFileStore setPreloadOptions:0]; MXWeakify(session); @@ -147,7 +154,7 @@ - (void)checkUserAccount { // Force account manager to reload account from the local storage. - [[MXKAccountManager sharedManager] forceReloadAccounts]; + [MXKAccountManager sharedManagerWithReload:YES]; if (self.userAccount) { diff --git a/RiotShareExtension/Sources/ShareExtensionRootViewController.m b/RiotShareExtension/Sources/ShareExtensionRootViewController.m index 90b5006e8..c43eef81e 100644 --- a/RiotShareExtension/Sources/ShareExtensionRootViewController.m +++ b/RiotShareExtension/Sources/ShareExtensionRootViewController.m @@ -52,6 +52,10 @@ [MXLog configure:configuration]; + // Configure our analytics. It will start if the option is enabled + Analytics *analytics = Analytics.shared; + [MXSDKOptions sharedInstance].analyticsDelegate = analytics; + [analytics startIfEnabled]; [ThemeService.shared setThemeId:RiotSettings.shared.userInterfaceTheme]; diff --git a/RiotShareExtension/Sources/ShareItemSender.m b/RiotShareExtension/Sources/ShareItemSender.m index e1cd1add6..fdc213033 100644 --- a/RiotShareExtension/Sources/ShareItemSender.m +++ b/RiotShareExtension/Sources/ShareItemSender.m @@ -525,7 +525,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) dispatch_group_t dispatchGroup = dispatch_group_create(); for (MXRoom *room in rooms) { dispatch_group_enter(dispatchGroup); - [room sendTextMessage:text success:^(NSString *eventId) { + [room sendTextMessage:text threadId:nil success:^(NSString *eventId) { dispatch_group_leave(dispatchGroup); } failure:^(NSError *innerError) { MXLogError(@"[ShareItemSender] sendTextMessage failed with error %@", error); @@ -565,7 +565,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) dispatch_group_t dispatchGroup = dispatch_group_create(); for (MXRoom *room in rooms) { dispatch_group_enter(dispatchGroup); - [room sendFile:fileUrl mimeType:mimeType localEcho:nil success:^(NSString *eventId) { + [room sendFile:fileUrl mimeType:mimeType threadId:nil localEcho:nil success:^(NSString *eventId) { dispatch_group_leave(dispatchGroup); } failure:^(NSError *innerError) { MXLogError(@"[ShareItemSender] sendFile failed with error %@", innerError); @@ -616,7 +616,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) dispatch_group_t dispatchGroup = dispatch_group_create(); for (MXRoom *room in rooms) { dispatch_group_enter(dispatchGroup); - [room sendVideoAsset:videoAsset withThumbnail:videoThumbnail localEcho:nil success:^(NSString *eventId) { + [room sendVideoAsset:videoAsset withThumbnail:videoThumbnail threadId:nil localEcho:nil success:^(NSString *eventId) { dispatch_group_leave(dispatchGroup); } failure:^(NSError *innerError) { MXLogError(@"[ShareManager] Failed sending video with error %@", innerError); @@ -702,7 +702,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) dispatch_group_t dispatchGroup = dispatch_group_create(); for (MXRoom *room in rooms) { dispatch_group_enter(dispatchGroup); - [room sendVoiceMessage:fileUrl mimeType:nil duration:0.0 samples:nil localEcho:nil success:^(NSString *eventId) { + [room sendVoiceMessage:fileUrl mimeType:nil duration:0.0 samples:nil threadId:nil localEcho:nil success:^(NSString *eventId) { dispatch_group_leave(dispatchGroup); } failure:^(NSError *innerError) { MXLogError(@"[ShareItemSender] sendVoiceMessage failed with error %@", error); @@ -867,7 +867,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) } dispatch_group_enter(dispatchGroup); - [room sendImage:finalImageData withImageSize:imageSize mimeType:mimeType andThumbnail:thumbnail localEcho:nil success:^(NSString *eventId) { + [room sendImage:finalImageData withImageSize:imageSize mimeType:mimeType andThumbnail:thumbnail threadId:nil localEcho:nil success:^(NSString *eventId) { dispatch_group_leave(dispatchGroup); } failure:^(NSError *innerError) { MXLogError(@"[ShareManager] sendImage failed with error %@", error); diff --git a/RiotShareExtension/target.yml b/RiotShareExtension/target.yml index 2c8f71d64..494407323 100644 --- a/RiotShareExtension/target.yml +++ b/RiotShareExtension/target.yml @@ -69,6 +69,7 @@ targets: - path: ../Riot/Assets/SharedImages.xcassets buildPhase: resources - path: ../Riot/Modules/MatrixKit + - path: ../Riot/Modules/Analytics excludes: - "**/*.md" # excludes all files with the .md extension - path: ../Riot/Generated/MatrixKitStrings.swift diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift index b9678fdbd..2cfdb6e0f 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptModels.swift @@ -1,5 +1,3 @@ -// File created from SimpleUserProfileExample -// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt // // Copyright 2021 New Vector Ltd // @@ -18,9 +16,6 @@ import Foundation -// The state is never modified so this is unnecessary. -enum AnalyticsPromptStateAction { } - enum AnalyticsPromptViewAction { /// Enable analytics. case enable diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptViewModel.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptViewModel.swift index 999e2a95f..7cd852f71 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptViewModel.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/AnalyticsPromptViewModel.swift @@ -1,5 +1,3 @@ -// File created from SimpleUserProfileExample -// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt // // Copyright 2021 New Vector Ltd // @@ -21,7 +19,7 @@ import Combine @available(iOS 14, *) typealias AnalyticsPromptViewModelType = StateStoreViewModel @available(iOS 14, *) class AnalyticsPromptViewModel: AnalyticsPromptViewModelType { @@ -56,10 +54,6 @@ class AnalyticsPromptViewModel: AnalyticsPromptViewModelType { openTermsURL() } } - - override class func reducer(state: inout AnalyticsPromptViewState, action: AnalyticsPromptStateAction) { - // There is no mutable state to reduce :) - } /// Enable analytics. The call to the Analytics class is made in the completion. private func enable() { diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift index c9e5f44b4..6e107b62b 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/Coordinator/AnalyticsPromptCoordinator.swift @@ -1,20 +1,18 @@ -// File created from SimpleUserProfileExample -// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt -/* - 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. - */ +// +// 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 SwiftUI diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptScreenState.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptScreenState.swift index 9c303bbbe..ed947e303 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptScreenState.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/MockAnalyticsPromptScreenState.swift @@ -1,5 +1,3 @@ -// File created from SimpleUserProfileExample -// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt // // Copyright 2021 New Vector Ltd // diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/Test/UI/AnalyticsPromptUITests.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/Test/UI/AnalyticsPromptUITests.swift index b8a38a117..c24e1fa63 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/Test/UI/AnalyticsPromptUITests.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/Test/UI/AnalyticsPromptUITests.swift @@ -1,5 +1,3 @@ -// File created from SimpleUserProfileExample -// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt // // Copyright 2021 New Vector Ltd // diff --git a/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift b/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift index 8f7acf49d..5e622ab5d 100644 --- a/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift +++ b/RiotSwiftUI/Modules/AnalyticsPrompt/View/AnalyticsPrompt.swift @@ -1,5 +1,3 @@ -// File created from SimpleUserProfileExample -// $ createScreen.sh AnalyticsPrompt AnalyticsPrompt // // Copyright 2021 New Vector Ltd // diff --git a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift index e584288e7..a53c6d67d 100644 --- a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift +++ b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift @@ -20,17 +20,19 @@ import Foundation @available(iOS 14.0, *) enum MockAppScreens { static let appScreens: [MockScreenState.Type] = [ + MockOnboardingSplashScreenScreenState.self, MockLocationSharingScreenState.self, MockAnalyticsPromptScreenState.self, MockUserSuggestionScreenState.self, MockPollEditFormScreenState.self, - MockPollTimelineScreenState.self, MockSpaceCreationEmailInvitesScreenState.self, MockMatrixItemChooserScreenState.self, MockSpaceCreationMenuScreenState.self, MockSpaceCreationRoomsScreenState.self, MockSpaceCreationSettingsScreenState.self, MockSpaceCreationPostProcessScreenState.self, + MockTimelinePollScreenState.self, + MockTemplateSimpleScreenScreenState.self, MockTemplateUserProfileScreenState.self, MockTemplateRoomListScreenState.self, MockTemplateRoomChatScreenState.self diff --git a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift index 3f633a5e8..6a389c86f 100644 --- a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift @@ -20,4 +20,5 @@ import DesignKit @available(iOS 14.0, *) protocol ThemeSwiftUI: ThemeSwiftUIType { var identifier: ThemeIdentifier { get } + var isDark: Bool { get } } diff --git a/RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift index 778809553..f21ef8b4f 100644 --- a/RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift @@ -20,6 +20,7 @@ import DesignKit @available(iOS 14.0, *) struct DarkThemeSwiftUI: ThemeSwiftUI { var identifier: ThemeIdentifier = .dark + let isDark: Bool = true var colors: ColorSwiftUI = DarkColors.swiftUI var fonts: FontSwiftUI = FontSwiftUI(values: ElementFonts()) } diff --git a/RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift index a7e5e9909..0d2a7d9c6 100644 --- a/RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift @@ -20,6 +20,7 @@ import DesignKit @available(iOS 14.0, *) struct DefaultThemeSwiftUI: ThemeSwiftUI { var identifier: ThemeIdentifier = .light + let isDark: Bool = false var colors: ColorSwiftUI = LightColors.swiftUI var fonts: FontSwiftUI = FontSwiftUI(values: ElementFonts()) } diff --git a/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift b/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift index edf478856..116c5d610 100644 --- a/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift +++ b/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift @@ -16,6 +16,7 @@ import SwiftUI +/// `ClearViewModifier` aims to add a clear button (e.g. `x` button) on the right side of any text editing view @available(iOS 14.0, *) struct ClearViewModifier: ViewModifier { diff --git a/RiotSwiftUI/Modules/Common/Util/NextViewModifier.swift b/RiotSwiftUI/Modules/Common/Util/NextViewModifier.swift deleted file mode 100644 index 80f4914ce..000000000 --- a/RiotSwiftUI/Modules/Common/Util/NextViewModifier.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// 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 SwiftUI - -@available(iOS 14.0, *) -struct NextViewModifier: ViewModifier -{ - // MARK: - Properties - - let alignment: Alignment - - // MARK: - Bindings - - @Binding var isEditing: Bool - - // MARK: - Private - - @Environment(\.theme) private var theme: ThemeSwiftUI - - // MARK: - Public - - public func body(content: Content) -> some View - { - ZStack(alignment: alignment) { - content - if isEditing { - Button(action: { - if !ResponderManager.makeActiveNextResponder() { - ResponderManager.resignFirstResponder() - } - }) { - Image(systemName: "arrow.right.circle.fill") - .renderingMode(.template) - .foregroundColor(theme.colors.quarterlyContent) - } - .padding(EdgeInsets(top: alignment.vertical == .top ? 8 : 0, leading: 0, bottom: alignment.vertical == .bottom ? 8 : 0, trailing: 8)) - } - } - } -} - diff --git a/RiotSwiftUI/Modules/Common/Util/ResponderManager.swift b/RiotSwiftUI/Modules/Common/Util/ResponderManager.swift index 87ce626a9..21738bae9 100644 --- a/RiotSwiftUI/Modules/Common/Util/ResponderManager.swift +++ b/RiotSwiftUI/Modules/Common/Util/ResponderManager.swift @@ -16,6 +16,7 @@ import UIKit +/// `ResponderManager` is used to chain `SwiftUI` text editing views that embed `UIKit` text editing views using `UIViewRepresentable` class ResponderManager { private static var tagIndex: Int = 1000 @@ -40,6 +41,10 @@ class ResponderManager { return nil } + /// register the `UIKit` view as a responder + /// + /// - Parameters: + /// - view: view to be registered static func register(view: UIView) { if registeredResponders.object(forKey: NSNumber(value: view.tag)) == nil { view.tag = nextIndex @@ -47,10 +52,16 @@ class ResponderManager { } } + /// Unregister the `UIKit` view from this manager. The view won't be considered as potential next responder anymore + /// + /// - Parameters: + /// - view: view to be unregistered static func unregister(view: UIView) { registeredResponders.removeObject(forKey: NSNumber(value: view.tag)) } + /// Tries to get the focused registered responder and give the focus to it's next responder + /// - Returns: `True` if the next responder has been found and is successfully focused. `False` otherwise. static func makeActiveNextResponder() -> Bool { guard let firstResponder = self.firstResponder else { return false @@ -59,6 +70,12 @@ class ResponderManager { return makeActiveNextResponder(of: firstResponder) } + /// Give the focus to the next responder f the given `UIKit` view + /// + /// - Parameters: + /// - view: base view + /// + /// - Returns: `True` if the next responder has been found and is successfully focused. `False` otherwise. static func makeActiveNextResponder(of view: UIView) -> Bool { let nextTag = view.tag + 1 guard let nextResponder = registeredResponders.object(forKey: NSNumber(value: nextTag)) else { @@ -69,6 +86,7 @@ class ResponderManager { return true } + /// Unfocus any focused registered view. static func resignFirstResponder() { firstResponder?.resignFirstResponder() } diff --git a/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift b/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift index c63cba339..b5d341e63 100644 --- a/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift +++ b/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift @@ -16,6 +16,7 @@ import SwiftUI +/// `WaitOverlay` allows to easily add an overlay that covers the entire with an `ActivityIndicator` at the center @available(iOS 14.0, *) struct WaitOverlay: ViewModifier { // MARK: - Properties @@ -36,12 +37,7 @@ struct WaitOverlay: ViewModifier { if isLoading { ZStack { Color.clear - RoundedRectangle(cornerRadius: 8, style: .continuous) - .fill(theme.colors.tertiaryContent.opacity(0.6)) - .frame(width: 50, height: 50) - ProgressView() - .scaleEffect(1.3, anchor: .center) - .progressViewStyle(CircularProgressViewStyle(tint: theme.colors.background)) + ActivityIndicator() } .frame(width: .infinity, height: .infinity) .transition(.opacity) diff --git a/RiotSwiftUI/Modules/Common/ViewModel/StateStoreViewModel.swift b/RiotSwiftUI/Modules/Common/ViewModel/StateStoreViewModel.swift index 915859165..efde322fb 100644 --- a/RiotSwiftUI/Modules/Common/ViewModel/StateStoreViewModel.swift +++ b/RiotSwiftUI/Modules/Common/ViewModel/StateStoreViewModel.swift @@ -92,9 +92,9 @@ class StateStoreViewModel { /// Constrained interface for passing to Views. var context: Context - /// State can be read within the 'ViewModel' but not modified outside of the reducer. var state: State { - context.viewState + get { context.viewState } + set { context.viewState = newValue } } // MARK: Setup @@ -110,12 +110,14 @@ class StateStoreViewModel { /// Send state actions to modify the state within the reducer. /// - Parameter action: The state action to send to the reducer. + @available(*, deprecated, message: "Mutate state directly instead") func dispatch(action: StateAction) { Self.reducer(state: &context.viewState, action: action) } /// Send state actions from a publisher to modify the state within the reducer. /// - Parameter actionPublisher: The publisher that produces actions to be sent to the reducer + @available(*, deprecated, message: "Mutate state directly instead") func dispatch(actionPublisher: AnyPublisher) { actionPublisher.sink { [weak self] action in guard let self = self else { return } diff --git a/RiotSwiftUI/Modules/Onboarding/Common/OnboardingConstants.swift b/RiotSwiftUI/Modules/Onboarding/Common/OnboardingConstants.swift new file mode 100644 index 000000000..eed2cb042 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/Common/OnboardingConstants.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 CoreGraphics + +/// Constants used across the entire onboarding flow. +struct OnboardingConstants { + static let maxContentWidth: CGFloat = 600 + static let maxContentHeight: CGFloat = 750 +} diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/Coordinator/OnboardingSplashScreenCoordinator.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/Coordinator/OnboardingSplashScreenCoordinator.swift new file mode 100644 index 000000000..d031d5826 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/Coordinator/OnboardingSplashScreenCoordinator.swift @@ -0,0 +1,66 @@ +// +// 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 SwiftUI + +protocol OnboardingSplashScreenCoordinatorProtocol: Coordinator, Presentable { + var completion: ((OnboardingSplashScreenViewModelResult) -> Void)? { get set } +} + +final class OnboardingSplashScreenCoordinator: OnboardingSplashScreenCoordinatorProtocol { + + // MARK: - Properties + + // MARK: Private + + private let onboardingSplashScreenHostingController: UIViewController + private var onboardingSplashScreenViewModel: OnboardingSplashScreenViewModelProtocol + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + var completion: ((OnboardingSplashScreenViewModelResult) -> Void)? + + // MARK: - Setup + + @available(iOS 14.0, *) + init() { + let viewModel = OnboardingSplashScreenViewModel() + let view = OnboardingSplashScreen(viewModel: viewModel.context) + onboardingSplashScreenViewModel = viewModel + let hostingController = VectorHostingController(rootView: view) + hostingController.vc_removeBackTitle() + onboardingSplashScreenHostingController = hostingController + } + + // MARK: - Public + func start() { + MXLog.debug("[OnboardingSplashScreenCoordinator] did start.") + onboardingSplashScreenViewModel.completion = { [weak self] result in + MXLog.debug("[OnboardingSplashScreenCoordinator] OnboardingSplashScreenViewModel did complete with result: \(result).") + guard let self = self else { return } + switch result { + case .login, .register: + self.completion?(result) + } + } + } + + func toPresentable() -> UIViewController { + return self.onboardingSplashScreenHostingController + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/MockOnboardingSplashScreenScreenState.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/MockOnboardingSplashScreenScreenState.swift new file mode 100644 index 000000000..541ad8c7c --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/MockOnboardingSplashScreenScreenState.swift @@ -0,0 +1,50 @@ +// +// 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 SwiftUI + +/// Using an enum for the screen allows you define the different state cases with +/// the relevant associated data for each case. +@available(iOS 14.0, *) +enum MockOnboardingSplashScreenScreenState: MockScreenState, CaseIterable { + // A case for each state you want to represent + // with specific, minimal associated data that will allow you + // mock that screen. + case animated + + /// The associated screen + var screenType: Any.Type { + OnboardingSplashScreen.self + } + + /// A list of screen state definitions + static var allCases: [MockOnboardingSplashScreenScreenState] { + [.animated] + } + + /// Generate the view struct for the screen state. + var screenView: ([Any], AnyView) { + let viewModel = OnboardingSplashScreenViewModel() + + // can simulate service and viewModel actions here if needs be. + + return ( + [false, viewModel], + AnyView(OnboardingSplashScreen(viewModel: viewModel.context)) + ) + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/OnboardingSplashScreenModels.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/OnboardingSplashScreenModels.swift new file mode 100644 index 000000000..d24b7679d --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/OnboardingSplashScreenModels.swift @@ -0,0 +1,107 @@ +// +// 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 SwiftUI + +// MARK: - Coordinator + +@available(iOS 13.0, *) +/// The content displayed in a single splash screen page. +struct OnboardingSplashScreenPageContent { + let title: String + let message: String + let image: ImageAsset + let darkImage: ImageAsset + let gradient: Gradient +} + +// MARK: View model + +enum OnboardingSplashScreenViewModelResult { + case register + case login +} + +// MARK: View + +@available(iOS 13.0, *) +struct OnboardingSplashScreenViewState: BindableState, CustomDebugStringConvertible { + + // MARK: - Constants + + private enum Constants { + static let gradientColors = [ + Color(red: 0.95, green: 0.98, blue: 0.96), + Color(red: 0.89, green: 0.96, blue: 0.97), + Color(red: 0.95, green: 0.89, blue: 0.97), + Color(red: 0.81, green: 0.95, blue: 0.91), + Color(red: 0.95, green: 0.98, blue: 0.96) + ] + } + + // MARK: - Properties + + /// An array containing all content of the carousel pages + let content: [OnboardingSplashScreenPageContent] + var bindings: OnboardingSplashScreenBindings + + /// Custom debug description to reduce noise in the logs. + var debugDescription: String { + "OnboardingSplashScreenViewState at page \(bindings.pageIndex)." + } + + init() { + // The pun doesn't translate, so we only use it for English. + let locale = Locale.current + let page4Title = locale.identifier.hasPrefix("en") ? "Cut the slack from teams." : VectorL10n.onboardingSplashPage4TitleNoPun + + self.content = [ + OnboardingSplashScreenPageContent(title: VectorL10n.onboardingSplashPage1Title, + message: VectorL10n.onboardingSplashPage1Message, + image: Asset.Images.onboardingSplashScreenPage1, + darkImage: Asset.Images.onboardingSplashScreenPage1Dark, + gradient: Gradient(colors: [Constants.gradientColors[0], Constants.gradientColors[1]])), + OnboardingSplashScreenPageContent(title: VectorL10n.onboardingSplashPage2Title, + message: VectorL10n.onboardingSplashPage2Message, + image: Asset.Images.onboardingSplashScreenPage2, + darkImage: Asset.Images.onboardingSplashScreenPage2Dark, + gradient: Gradient(colors: [Constants.gradientColors[1], Constants.gradientColors[2]])), + OnboardingSplashScreenPageContent(title: VectorL10n.onboardingSplashPage3Title, + message: VectorL10n.onboardingSplashPage3Message, + image: Asset.Images.onboardingSplashScreenPage3, + darkImage: Asset.Images.onboardingSplashScreenPage3Dark, + gradient: Gradient(colors: [Constants.gradientColors[2], Constants.gradientColors[3]])), + OnboardingSplashScreenPageContent(title: page4Title, + message: VectorL10n.onboardingSplashPage4Message, + image: Asset.Images.onboardingSplashScreenPage4, + darkImage: Asset.Images.onboardingSplashScreenPage4Dark, + gradient: Gradient(colors: [Constants.gradientColors[3], Constants.gradientColors[4]])), + ] + self.bindings = OnboardingSplashScreenBindings() + } +} + +struct OnboardingSplashScreenBindings { + var pageIndex = 0 +} + +enum OnboardingSplashScreenViewAction { + case register + case login + case nextPage + case previousPage + case hiddenPage +} diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/OnboardingSplashScreenViewModel.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/OnboardingSplashScreenViewModel.swift new file mode 100644 index 000000000..c51b843dc --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/OnboardingSplashScreenViewModel.swift @@ -0,0 +1,76 @@ +// +// 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 SwiftUI +import Combine + +@available(iOS 14, *) +typealias OnboardingSplashScreenViewModelType = StateStoreViewModel + +protocol OnboardingSplashScreenViewModelProtocol { + var completion: ((OnboardingSplashScreenViewModelResult) -> Void)? { get set } + @available(iOS 14, *) + var context: OnboardingSplashScreenViewModelType.Context { get } +} + + +@available(iOS 14, *) +class OnboardingSplashScreenViewModel: OnboardingSplashScreenViewModelType, OnboardingSplashScreenViewModelProtocol { + + // MARK: - Properties + + // MARK: Private + + // MARK: Public + + var completion: ((OnboardingSplashScreenViewModelResult) -> Void)? + + // MARK: - Setup + + init() { + super.init(initialViewState: OnboardingSplashScreenViewState()) + } + + // MARK: - Public + + override func process(viewAction: OnboardingSplashScreenViewAction) { + switch viewAction { + case .register: + register() + case .login: + login() + case .nextPage: + // Wrap back round to the first page index when reaching the end. + state.bindings.pageIndex = (state.bindings.pageIndex + 1) % state.content.count + case .previousPage: + // Prevent the hidden page at index -1 from being shown. + state.bindings.pageIndex = max(0, (state.bindings.pageIndex - 1)) + case .hiddenPage: + // Hidden page for a nicer animation when looping back to the start. + state.bindings.pageIndex = -1 + } + } + + private func register() { + completion?(.register) + } + + private func login() { + completion?(.login) + } +} diff --git a/RiotSwiftUI/Modules/Room/UserSuggestion/Model/UserSuggestionStateAction.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/Test/Unit/OnboardingSplashScreenViewModelTests.swift similarity index 74% rename from RiotSwiftUI/Modules/Room/UserSuggestion/Model/UserSuggestionStateAction.swift rename to RiotSwiftUI/Modules/Onboarding/SplashScreen/Test/Unit/OnboardingSplashScreenViewModelTests.swift index 153848943..c247b4027 100644 --- a/RiotSwiftUI/Modules/Room/UserSuggestion/Model/UserSuggestionStateAction.swift +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/Test/Unit/OnboardingSplashScreenViewModelTests.swift @@ -1,5 +1,3 @@ -// File created from SimpleUserProfileExample -// $ createScreen.sh Room/UserSuggestion UserSuggestion // // Copyright 2021 New Vector Ltd // @@ -16,9 +14,12 @@ // limitations under the License. // -import Foundation +import XCTest +import Combine + +@testable import RiotSwiftUI @available(iOS 14.0, *) -enum UserSuggestionStateAction { - case updateWithItems([UserSuggestionItemProtocol]) +class OnboardingSplashScreenViewModelTests: XCTestCase { + } diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreen.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreen.swift new file mode 100644 index 000000000..d8c13982a --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreen.swift @@ -0,0 +1,205 @@ +// +// 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 SwiftUI + +@available(iOS 14.0, *) +/// The splash screen shown at the beginning of the onboarding flow. +struct OnboardingSplashScreen: View { + + // MARK: - Properties + + // MARK: Private + + @Environment(\.theme) private var theme + @Environment(\.layoutDirection) private var layoutDirection + + private var isLeftToRight: Bool { layoutDirection == .leftToRight } + private var pageCount: Int { viewModel.viewState.content.count } + + /// The dimensions of the stack with the action buttons and page indicator. + @State private var overlayFrame: CGRect = .zero + /// A timer to automatically animate the pages. + @State private var pageTimer: Timer? + /// The amount of offset to apply when a drag gesture is in progress. + @State private var dragOffset: CGFloat = .zero + + // MARK: Public + + @ObservedObject var viewModel: OnboardingSplashScreenViewModel.Context + + /// The main action buttons. + var buttons: some View { + VStack(spacing: 12) { + Button { viewModel.send(viewAction: .register) } label: { + Text(VectorL10n.onboardingSplashRegisterButtonTitle) + } + .buttonStyle(PrimaryActionButtonStyle()) + + Button { viewModel.send(viewAction: .login) } label: { + Text(VectorL10n.onboardingSplashLoginButtonTitle) + .font(theme.fonts.body) + .padding(12) + } + } + } + + /// The only part of the UI that isn't inside of the carousel. + var overlay: some View { + VStack(spacing: 50) { + Color.clear + Color.clear + + VStack { + OnboardingSplashScreenPageIndicator(pageCount: pageCount, + pageIndex: viewModel.pageIndex) + Spacer() + + buttons + .padding(.horizontal, 16) + .frame(maxWidth: OnboardingConstants.maxContentWidth) + Spacer() + } + .background(ViewFrameReader(frame: $overlayFrame)) + } + } + + var body: some View { + GeometryReader { geometry in + ZStack(alignment: .leading) { + + // The main content of the carousel + HStack(spacing: 0) { + + // Add a hidden page at the start of the carousel duplicating the content of the last page + OnboardingSplashScreenPage(content: viewModel.viewState.content[pageCount - 1], + overlayHeight: overlayFrame.height + geometry.safeAreaInsets.bottom) + .frame(width: geometry.size.width) + .tag(-1) + + ForEach(0.. Bool { + if viewModel.pageIndex == 0 { + return isLeftToRight ? width < 0 : width > 0 + } else if viewModel.pageIndex == pageCount - 1 { + return isLeftToRight ? width > 0 : width < 0 + } + + return true + } + + /// Updates the `dragOffset` based on the gesture's value. + /// - Parameter drag: The drag gesture value to handle. + private func handleDragGestureChange(_ drag: DragGesture.Value) { + guard shouldSwipeForTranslation(drag.translation.width) else { return } + + stopTimer() + + // Animate the change over a few frames to smooth out any stuttering. + withAnimation(.linear(duration: 0.05)) { + dragOffset = isLeftToRight ? drag.translation.width : -drag.translation.width + } + } + + /// Clears the drag offset and informs the view model to switch to another page if necessary. + /// - Parameter viewSize: The size of the view in which the gesture took place. + private func handleDragGestureEnded(_ drag: DragGesture.Value, viewSize: CGSize) { + guard shouldSwipeForTranslation(drag.predictedEndTranslation.width) else { + // Reset the offset just in case. + withAnimation { dragOffset = 0 } + return + } + + withAnimation(.easeInOut(duration: 0.2)) { + if drag.predictedEndTranslation.width < -viewSize.width / 2 { + viewModel.send(viewAction: .nextPage) + } else if drag.predictedEndTranslation.width > viewSize.width / 2 { + viewModel.send(viewAction: .previousPage) + } + + dragOffset = 0 + } + } +} + +// MARK: - Previews + +@available(iOS 14.0, *) +struct OnboardingSplashScreen_Previews: PreviewProvider { + static let stateRenderer = MockOnboardingSplashScreenScreenState.stateRenderer + static var previews: some View { + stateRenderer.screenGroup() + } +} diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPage.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPage.swift new file mode 100644 index 000000000..53a71d301 --- /dev/null +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPage.swift @@ -0,0 +1,85 @@ +// +// 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 SwiftUI + +@available(iOS 14.0, *) +struct OnboardingSplashScreenPage: View { + + // MARK: - Properties + + // MARK: Private + @Environment(\.theme) private var theme + + // MARK: Public + /// The content that this page should display. + let content: OnboardingSplashScreenPageContent + /// The height of the non-scrollable content in the splash screen. + let overlayHeight: CGFloat + + // MARK: - Views + + @ViewBuilder + var backgroundGradient: some View { + if !theme.isDark { + LinearGradient(gradient: content.gradient, startPoint: .leading, endPoint: .trailing) + .flipsForRightToLeftLayoutDirection(true) + } + } + + var body: some View { + VStack { + VStack { + Image(theme.isDark ? content.darkImage.name : content.image.name) + .resizable() + .scaledToFit() + .frame(maxWidth: 300) + .padding(20) + + VStack(spacing: 8) { + OnboardingSplashScreenTitleText(content.title) + .font(theme.fonts.title2B) + .foregroundColor(theme.colors.primaryContent) + Text(content.message) + .font(theme.fonts.body) + .foregroundColor(theme.colors.secondaryContent) + .multilineTextAlignment(.center) + } + .padding(.bottom) + + Spacer() + + // Prevent the content from clashing with the overlay content. + Spacer().frame(maxHeight: overlayHeight) + } + .padding(.horizontal, 16) + .frame(maxWidth: OnboardingConstants.maxContentWidth, + maxHeight: OnboardingConstants.maxContentHeight) + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(backgroundGradient.ignoresSafeArea()) + } +} + +@available(iOS 14.0, *) +struct OnboardingSplashScreenPage_Previews: PreviewProvider { + static let content = OnboardingSplashScreenViewState().content + static var previews: some View { + ForEach(0..