Merge branch 'release/1.8.0/master'

This commit is contained in:
Doug 2022-02-09 18:41:39 +00:00
commit 905a1acf8f
385 changed files with 18663 additions and 2684 deletions

View file

@ -13,7 +13,21 @@ env:
MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }}
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

View file

@ -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 }}

View file

@ -1,3 +1,52 @@
## Changes in 1.8.0 (2022-02-09)
✨ Features
- Threads: Add `View in room` action to the thread root event. ([#5117](https://github.com/vector-im/element-ios/issues/5117))
- Add a splash screen before authentication is shown. ([#5159](https://github.com/vector-im/element-ios/issues/5159))
- Remove location sharing settings entry and enable it by default. Add .well-known configuration support for tile server and map styles. ([#5298](https://github.com/vector-im/element-ios/issues/5298))
🙌 Improvements
- Show target user avatars for collapsed membership changes ([#4148](https://github.com/vector-im/element-ios/pull/4148))
- Updated available emojis with data from https://github.com/missive/emoji-mart/blob/master/data/apple.json ([#5517](https://github.com/vector-im/element-ios/pull/5517))
- Upgrade MatrixSDK version ([v0.22.0](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.22.0)).
- Permalinks: Create for thread events & handle navigations. ([#5094](https://github.com/vector-im/element-ios/issues/5094))
- Search: Navigate to thread view for search results in threads. ([#5095](https://github.com/vector-im/element-ios/issues/5095))
- Search: display matching pattern with a highlight color ([#5252](https://github.com/vector-im/element-ios/issues/5252))
🐛 Bugfixes
- Share: Handle jpeg and png UTType properly ([#3636](https://github.com/vector-im/element-ios/issues/3636))
- Timeline: automatically scroll timeline to the bottom when opening a room or rotating device. ([#4524](https://github.com/vector-im/element-ios/issues/4524))
- Fix bugs when building with Xcode 13: bar appearance / header padding / space avatar content size. Additionally, use UIKit context menus on the home screen. ([#4883](https://github.com/vector-im/element-ios/issues/4883))
- joining a space seemed to noop ([#5171](https://github.com/vector-im/element-ios/issues/5171))
- Accepting a Space Invite has shouty button labels ([#5175](https://github.com/vector-im/element-ios/issues/5175))
- RoomDataSource: Avoid reloading of data source on thread screen itself. ([#5263](https://github.com/vector-im/element-ios/issues/5263))
- MXKAccount: Gracefully pause the session. ([#5426](https://github.com/vector-im/element-ios/issues/5426))
- HomeViewController: Reload section if total number of rooms changed. ([#5448](https://github.com/vector-im/element-ios/issues/5448))
- Selecting Transfer in a call should immediately put the the other person on hold until the call connects or the Transfer is cancelled. ([#5451](https://github.com/vector-im/element-ios/issues/5451))
- Avatar view prevents to select space in space list ([#5454](https://github.com/vector-im/element-ios/issues/5454))
- Fixes media library freezing under iOS 15.2. ([#5465](https://github.com/vector-im/element-ios/issues/5465))
- Room Settings: Fix incorrect header title. ([#5525](https://github.com/vector-im/element-ios/issues/5525))
🗣 Translations
- Localisation: Add Indonesian and Slovak languages. ([#5048](https://github.com/vector-im/element-ios/issues/5048))
🧱 Build
- Fix CI builds for external contributors using forked repos. ([#5496](https://github.com/vector-im/element-ios/pull/5496), [#5491](https://github.com/vector-im/element-ios/issues/5491))
- Use Xcode 13.2 to build the project. ([#4883](https://github.com/vector-im/element-ios/issues/4883))
Others
- Add WIP to towncrier. ([#5446](https://github.com/vector-im/element-ios/pull/5446))
- Add a simple screen SwiftUI template. ([#5349](https://github.com/vector-im/element-ios/issues/5349))
- Added a new automation for FTUE and WTF Project boards ([#5493](https://github.com/vector-im/element-ios/issues/5493))
- Fix the indentation in the project board automation file on FTU and WTF labels ([#5504](https://github.com/vector-im/element-ios/issues/5504))
## Changes in 1.7.0 (2022-01-25)
✨ Features

View file

@ -15,5 +15,5 @@
//
// Version
MARKETING_VERSION = 1.7.0
CURRENT_PROJECT_VERSION = 1.7.0
MARKETING_VERSION = 1.8.0
CURRENT_PROJECT_VERSION = 1.8.0

View file

@ -293,9 +293,9 @@ final class BuildSettings: NSObject {
static let roomScreenAllowFilesAction: Bool = true
// Timeline style
static let roomScreenAllowTimelineStyleConfiguration: Bool = false
static let roomScreenAllowTimelineStyleConfiguration: Bool = true
static let roomScreenTimelineDefaultStyleIdentifier: RoomTimelineStyleIdentifier = .plain
static var roomScreenEnableMessageBubblesByDefault: Bool {
static var isRoomScreenEnableMessageBubblesByDefault: Bool {
return self.roomScreenTimelineDefaultStyleIdentifier == .bubble
}
@ -351,6 +351,9 @@ final class BuildSettings: NSObject {
static let authScreenShowCustomServerOptions = true
static let authScreenShowSocialLoginSection = true
// MARK: - Authentication Options
static let authEnableRefreshTokens = false
// MARK: - Unified Search
static let unifiedSearchScreenShowPublicDirectory = true
@ -369,7 +372,7 @@ final class BuildSettings: NSObject {
// MARK: - Location Sharing
static let tileServerMapURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx")!
static let tileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx")!
static var locationSharingEnabled: Bool {
guard #available(iOS 14, *) else {

View file

@ -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
}

View file

@ -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.21.0'
$matrixSDKVersion = '= 0.22.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'
@ -150,4 +151,4 @@ post_install do |installer|
config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
end
end
end
end

View file

@ -57,16 +57,16 @@ PODS:
- LoggerAPI (1.9.200):
- Logging (~> 1.1)
- Logging (1.4.0)
- MatrixSDK (0.21.0):
- MatrixSDK/Core (= 0.21.0)
- MatrixSDK/Core (0.21.0):
- MatrixSDK (0.22.0):
- MatrixSDK/Core (= 0.22.0)
- MatrixSDK/Core (0.22.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.21.0):
- MatrixSDK/JingleCallStack (0.22.0):
- JitsiMeetSDK (= 3.10.2)
- MatrixSDK/Core
- OLMKit (3.2.5):
@ -95,6 +95,7 @@ PODS:
- LoggerAPI (~> 1.7)
- SwiftLint (0.44.0)
- SwiftyBeaver (1.9.5)
- UICollectionViewRightAlignedLayout (0.0.3)
- WeakDictionary (2.0.2)
- zxcvbn-ios (1.0.4)
- ZXingObjC (3.6.5):
@ -116,8 +117,8 @@ DEPENDENCIES:
- KeychainAccess (~> 4.2.2)
- KTCenterFlowLayout (~> 1.3.1)
- libPhoneNumber-iOS (~> 0.9.13)
- MatrixSDK (= 0.21.0)
- MatrixSDK/JingleCallStack (= 0.21.0)
- MatrixSDK (= 0.22.0)
- MatrixSDK/JingleCallStack (= 0.22.0)
- OLMKit
- PostHog (~> 1.4.4)
- ReadMoreTextView (~> 3.0.1)
@ -127,6 +128,7 @@ DEPENDENCIES:
- SwiftGen (~> 6.3)
- SwiftJWT (~> 3.6.200)
- SwiftLint (~> 0.44.0)
- UICollectionViewRightAlignedLayout (~> 0.0.3)
- WeakDictionary (~> 2.0)
- zxcvbn-ios
- ZXingObjC (~> 3.6.5)
@ -169,6 +171,7 @@ SPEC REPOS:
- SwiftJWT
- SwiftLint
- SwiftyBeaver
- UICollectionViewRightAlignedLayout
- WeakDictionary
- zxcvbn-ios
- ZXingObjC
@ -209,7 +212,7 @@ SPEC CHECKSUMS:
libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
Logging: beeb016c9c80cf77042d62e83495816847ef108b
MatrixSDK: cd98e3e4287b8a4f3a5bb47642beae80e8f62534
MatrixSDK: 21201cd007145d96beff24cc7f9727ced497c3fd
OLMKit: 9fb4799c4a044dd2c06bda31ec31a12191ad30b5
PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
@ -221,10 +224,11 @@ SPEC CHECKSUMS:
SwiftJWT: 88c412708f58c169d431d344c87bc79a87c830ae
SwiftLint: e96c0a8c770c7ebbc4d36c55baf9096bb65c4584
SwiftyBeaver: 84069991dd5dca07d7069100985badaca7f0ce82
UICollectionViewRightAlignedLayout: 823eef8c567eba4a44c21bc2ffcb0d0d5f361e2d
WeakDictionary: 8cd038acd77e5d54ca4ebaec3d20853d732b45e0
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: 39329dd448eb0ad10c396f84f854a18962ab1fc4
PODFILE CHECKSUM: 2a13c9d4f4894152af066c746582fde2e731cbff
COCOAPODS: 1.11.2

View file

@ -64,27 +64,6 @@
</objects>
<point key="canvasLocation" x="5047" y="-1437"/>
</scene>
<!--Room Context Timeline-->
<scene sceneID="Htr-h8-baq">
<objects>
<viewController title="Room Context Timeline" hidesBottomBarWhenPushed="YES" id="Too-LV-OLW" customClass="RoomViewController" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="yLe-Hk-Sol">
<nil key="title"/>
<view key="titleView" contentMode="scaleToFill" id="djN-zB-Vni" userLabel="Room title view container">
<rect key="frame" x="8" y="2" width="312" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</navigationItem>
<connections>
<outlet property="roomTitleViewContainer" destination="djN-zB-Vni" id="VQG-Mp-hSa"/>
<segue destination="gkO-rP-nGK" kind="show" identifier="showContactDetails" id="ziz-Xl-QVg"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Yjg-uP-Hcy" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3326" y="-1299"/>
</scene>
<!--Room Search View Controller-->
<scene sceneID="rUg-1s-vHX">
<objects>
@ -98,9 +77,6 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<connections>
<segue destination="Too-LV-OLW" kind="show" identifier="showTimeline" id="P1V-0d-mYL"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="bK5-DX-KSF" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
@ -543,10 +519,29 @@
</objects>
<point key="canvasLocation" x="374" y="449"/>
</scene>
<!--Thread-->
<scene sceneID="Opl-gU-pwm">
<objects>
<viewController storyboardIdentifier="ThreadViewControllerStoryboardId" title="Room" hidesBottomBarWhenPushed="YES" id="R2h-H9-hdJ" userLabel="Thread" customClass="ThreadViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<navigationItem key="navigationItem" id="TFF-nx-BSb">
<nil key="title"/>
<view key="titleView" contentMode="scaleToFill" id="e4J-vI-jzo" userLabel="Room title view container">
<rect key="frame" x="8" y="2" width="312" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</navigationItem>
<connections>
<outlet property="roomTitleViewContainer" destination="e4J-vI-jzo" id="b1C-TY-2R6"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="kHX-sJ-uYE" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-153" y="-419"/>
</scene>
</scenes>
<inferredMetricsTieBreakers>
<segue reference="Tfl-tq-LQp"/>
<segue reference="f5u-Y1-7nt"/>
</inferredMetricsTieBreakers>
<resources>
<image name="launch_screen_logo" width="240" height="240"/>

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "OnboardingSplashScreenPage1.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "OnboardingSplashScreenPage1-Dark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -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

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "OnboardingSplashScreenPage2.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "OnboardingSplashScreenPage2-Dark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "OnboardingSplashScreenPage3.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "OnboardingSplashScreenPage3-Dark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "OnboardingSplashScreenPage4.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "OnboardingSplashScreenPage4-Dark.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}

View file

@ -19,5 +19,8 @@
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View file

@ -19,5 +19,8 @@
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View file

@ -1,6 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View file

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

View file

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 B

View file

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "filter-on.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View file

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13 6H4C3.45 6 3 6.45 3 7C3 7.55 3.45 8 4 8H13.3414C13.1203 7.37444 13 6.70127 13 6Z" fill="#17191C"/>
<path d="M15.6822 11C16.3676 11.4557 17.1521 11.7743 17.9965 11.9165C17.9988 11.944 18 11.9719 18 12C18 12.55 17.55 13 17 13H7C6.45 13 6 12.55 6 12C6 11.45 6.45 11 7 11H15.6822Z" fill="#17191C"/>
<path d="M11 18H13C13.55 18 14 17.55 14 17C14 16.45 13.55 16 13 16H11C10.45 16 10 16.45 10 17C10 17.55 10.45 18 11 18Z" fill="#17191C"/>
<path d="M19 9C20.6569 9 22 7.65685 22 6C22 4.34315 20.6569 3 19 3C17.3431 3 16 4.34315 16 6C16 7.65685 17.3431 9 19 9Z" fill="#17191C"/>
</svg>

After

Width:  |  Height:  |  Size: 686 B

View file

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 870 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long

View file

@ -1269,7 +1269,7 @@
// Social login
"social_login_list_title_continue" = "Weiter mit";
"room_intro_cell_information_multiple_dm_sentence2" = "Nur ihr seid in diesem Gespräch, außer ihr lädt jemanden ein.";
"room_intro_cell_information_multiple_dm_sentence2" = "Nur ihr seid in diesem Gespräch, außer ihr ladet jemanden ein.";
"room_intro_cell_information_dm_sentence2" = "Nur zwei von euch sind in diesem Gespräch. Keine anderer kann beitreten.";
"room_intro_cell_information_dm_sentence1_part3" = ". ";
"room_intro_cell_information_dm_sentence1_part1" = "Dies ist der Beginn deiner Direktnachricht mit ";
@ -1596,3 +1596,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Konto erstellen";
"settings_enable_room_message_bubbles" = "Nachrichtenblasen";
"poll_edit_form_update_failure_subtitle" = "Bitte erneut versuchen";
"poll_edit_form_poll_type" = "Umfragetyp";
"poll_edit_form_poll_type_closed_description" = "Ergebnisse werden erst angezeigt, wenn du die Umfrage beendest";
"poll_edit_form_poll_type_closed" = "Geschlossene Umfrage";
"poll_edit_form_poll_type_open_description" = "Ergebnisse werden direkt nach Stimmabgabe angezeigt";
"poll_edit_form_poll_type_open" = "Offene Umfrage";
"poll_edit_form_update_failure_title" = "Aktualisierung der Umfrage fehlgeschlagen";

View file

@ -110,7 +110,7 @@
"auth_optional_phone_placeholder" = "Phone number (optional)";
"auth_phone_placeholder" = "Phone number";
"auth_repeat_password_placeholder" = "Repeat password";
"auth_repeat_new_password_placeholder" = "Confirm your new password";
"auth_repeat_new_password_placeholder" = "Confirm your new Matrix account password";
"auth_home_server_placeholder" = "URL (e.g. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (e.g. https://vector.im)";
"auth_invalid_login_param" = "Incorrect username and/or password";
@ -127,13 +127,13 @@
"auth_missing_email_or_phone" = "Missing email address or phone number";
"auth_email_in_use" = "This email address is already in use";
"auth_phone_in_use" = "This phone number is already in use";
"auth_email_is_required" = "No identity server is configured so you cannot add an email address in order to reset your password in the future.";
"auth_phone_is_required" = "No identity server is configured so you cannot add a phone number in order to reset your password in the future.";
"auth_email_is_required" = "No identity server is configured so you cannot add an email address in order to reset your Matrix account password in the future.";
"auth_phone_is_required" = "No identity server is configured so you cannot add a phone number in order to reset your Matrix account password in the future.";
"auth_untrusted_id_server" = "The identity server is not trusted";
"auth_password_dont_match" = "Passwords don't match";
"auth_username_in_use" = "Username in use";
"auth_forgot_password" = "Forgot password?";
"auth_forgot_password_error_no_configured_identity_server" = "No identity server is configured: add one to reset your password.";
"auth_forgot_password" = "Forgot Matrix account password?";
"auth_forgot_password_error_no_configured_identity_server" = "No identity server is configured: add one to reset your Matrix account password.";
"auth_email_not_found" = "Failed to send email: This email address was not found";
"auth_use_server_options" = "Use custom server options (advanced)";
"auth_email_validation_message" = "Please check your email to continue registration";
@ -141,15 +141,15 @@
"auth_msisdn_validation_message" = "We\'ve sent an SMS with an activation code. Please enter this code below.";
"auth_msisdn_validation_error" = "Unable to verify phone number.";
"auth_recaptcha_message" = "This homeserver would like to make sure you are not a robot";
"auth_reset_password_message" = "To reset your password, enter the email address linked to your account:";
"auth_reset_password_message" = "To reset your Matrix account password, enter the email address linked to your account:";
"auth_reset_password_missing_email" = "The email address linked to your account must be entered.";
"auth_reset_password_missing_password" = "A new password must be entered.";
"auth_reset_password_email_validation_message" = "An email has been sent to %@. Once you've followed the link it contains, click below.";
"auth_reset_password_next_step_button" = "I have verified my email address";
"auth_reset_password_error_unauthorized" = "Failed to verify email address: make sure you clicked the link in the email";
"auth_reset_password_error_not_found" = "Your email address does not appear to be associated with a Matrix ID on this homeserver.";
"auth_reset_password_error_is_required" = "No identity server is configured: add one in server options to reset your password.";
"auth_reset_password_success_message" = "Your password has been reset.\n\nYou have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, re-log in on each device.";
"auth_reset_password_error_is_required" = "No identity server is configured: add one in server options to reset your Matrix account password.";
"auth_reset_password_success_message" = "Your Matrix account password has been reset.\n\nYou have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, re-log in on each device.";
"auth_add_email_and_phone_warning" = "Registration with email and phone number at once is not supported yet until the api exists. Only the phone number will be taken into account. You may add your email to your profile in settings.";
"auth_accept_policies" = "Please review and accept the policies of this homeserver:";
"auth_autodiscover_invalid_response" = "Invalid homeserver discovery response";
@ -338,7 +338,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";
@ -378,7 +379,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";
@ -395,10 +397,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";
@ -423,12 +427,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";
@ -512,7 +531,7 @@ Tap the + to start adding people.";
"settings_add_email_address" = "Add email address";
"settings_phone_number" = "Phone";
"settings_add_phone_number" = "Add phone number";
"settings_change_password" = "Change password";
"settings_change_password" = "Change Matrix account password";
"settings_night_mode" = "Night Mode";
"settings_fail_to_update_profile" = "Fail to update profile";
"settings_three_pids_management_information_part1" = "Manage which email addresses or phone numbers you can use to log in or recover your account here. Control who can find you in ";
@ -585,6 +604,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 %@";
@ -596,16 +616,16 @@ Tap the + to start adding people.";
"settings_enable_rageshake" = "Rage shake to report bug";
"settings_clear_cache" = "Clear cache";
"settings_change_password" = "Change password";
"settings_change_password" = "Change Matrix account password";
"settings_old_password" = "old password";
"settings_new_password" = "new password";
"settings_confirm_password" = "confirm password";
"settings_fail_to_update_password" = "Fail to update password";
"settings_password_updated" = "Your password has been updated";
"settings_fail_to_update_password" = "Fail to update Matrix account password";
"settings_password_updated" = "Your Matrix account password has been updated";
"settings_add_3pid_password_title_email" = "Add email address";
"settings_add_3pid_password_title_msidsn" = "Add phone number";
"settings_add_3pid_password_message" = "To continue, please enter your password";
"settings_add_3pid_password_message" = "To continue, please enter your Matrix account password";
"settings_add_3pid_invalid_password_message" = "Invalid credentials";
"settings_crypto_device_name" = "Session name: ";
@ -673,7 +693,7 @@ Tap the + to start adding people.";
"security_settings_title" = "Security";
"security_settings_crypto_sessions" = "MY SESSIONS";
"security_settings_crypto_sessions_loading" = "Loading sessions…";
"security_settings_crypto_sessions_description_2" = "If you dont recognise a login, change your password and reset Secure Backup.";
"security_settings_crypto_sessions_description_2" = "If you dont recognise a login, change your Matrix account password and reset Secure Backup.";
"security_settings_secure_backup" = "SECURE BACKUP";
"security_settings_secure_backup_description" = "Back up your encryption keys with your account data in case you lose access to your sessions. Your keys will be secured with a unique Security Key.";
@ -706,7 +726,7 @@ Tap the + to start adding people.";
"security_settings_complete_security_alert_message" = "You should complete security on your current session first.";
"security_settings_coming_soon" = "Sorry. This action is not available on %@ iOS yet. Please use another Matrix client to set it up. %@ iOS will use it.";
"security_settings_user_password_description" = "Confirm your identity by entering your account password";
"security_settings_user_password_description" = "Confirm your identity by entering your Matrix account password";
// Manage session
"manage_session_title" = "Manage session";
@ -919,6 +939,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: %@";
@ -1103,7 +1124,7 @@ Tap the + to start adding people.";
"deactivate_account_validate_action" = "Deactivate account";
"deactivate_account_password_alert_title" = "Deactivate Account";
"deactivate_account_password_alert_message" = "To continue, please enter your password";
"deactivate_account_password_alert_message" = "To continue, please enter your Matrix account password";
// Re-request confirmation dialog
"rerequest_keys_alert_title" = "Request Sent";
@ -1159,7 +1180,7 @@ Tap the + to start adding people.";
// Passphrase
"key_backup_setup_passphrase_title" = "Secure your backup with a Security Phrase";
"key_backup_setup_passphrase_info" = "We'll store an encrypted copy of your keys on our server. Protect your backup with a phrase to keep it secure.\n\nFor maximum security, this should be different from your account password.";
"key_backup_setup_passphrase_info" = "We'll store an encrypted copy of your keys on our server. Protect your backup with a phrase to keep it secure.\n\nFor maximum security, this should be different from your Matrix account password.";
"key_backup_setup_passphrase_passphrase_title" = "Enter";
"key_backup_setup_passphrase_passphrase_placeholder" = "Enter phrase";
"key_backup_setup_passphrase_passphrase_valid" = "Great!";
@ -1578,7 +1599,7 @@ Tap the + to start adding people.";
"secrets_setup_recovery_passphrase_title" = "Set a Security Phrase";
"secrets_setup_recovery_passphrase_information" = "Enter a security phrase only you know, used to secure secrets on your server.";
"secrets_setup_recovery_passphrase_additional_information" = "Don't use your account password.";
"secrets_setup_recovery_passphrase_additional_information" = "Don't use your Matrix account password.";
"secrets_setup_recovery_passphrase_validate_action" = "Done";
"secrets_setup_recovery_passphrase_confirm_information" = "Enter your Security Phrase again to confirm it.";
@ -1598,7 +1619,7 @@ Tap the + to start adding people.";
"secrets_reset_warning_title" = "If you reset everything";
"secrets_reset_warning_message" = "You will restart with no history, no messages, trusted devices or trusted users.";
"secrets_reset_reset_action" = "Reset";
"secrets_reset_authentication_message" = "Enter your account password to confirm";
"secrets_reset_authentication_message" = "Enter your Matrix account password to confirm";
// MARK: - Cross-signing
@ -1704,6 +1725,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
@ -1884,6 +1915,10 @@ Tap the + to start adding people.";
"location_sharing_share_action" = "Share";
"location_sharing_post_failure_title" = "We couldnt 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.";

View file

@ -1148,7 +1148,7 @@
"create_room_title" = "Uus jututuba";
"create_room_section_header_name" = "Jututoa nimi";
"create_room_placeholder_name" = "Nimi";
"create_room_section_header_topic" = "Jututoa teema (valikuline)";
"create_room_section_header_topic" = "Jututoa teema (kui soovid lisada)";
"create_room_placeholder_topic" = "Jututoa teema";
"create_room_section_header_encryption" = "Krüptimine jututoas";
"create_room_enable_encryption" = "Võta krüptimine kasutusele";
@ -1431,7 +1431,7 @@
"spaces_empty_space_title" = "Selles kogukonnakeskuses pole veel jututube";
"space_tag" = "kogukonnakeskus";
"spaces_suggested_room" = "Soovitatud";
"spaces_explore_rooms" = "Uuri jututubasid";
"spaces_explore_rooms" = "Tutvu jututubadega";
"leave_space_and_all_rooms_action" = "Lahku kõikidest jututubadest ja kogukondadest";
"leave_space_only_action" = "Ära lahku ühestki jututoast";
"leave_space_message_admin_warning" = "Sa oled selle kogukonnakeskuse haldaja. Enne oma lahkumist palun lisa siia veel vähemalt üks uus haldaja.";
@ -1556,3 +1556,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Loo kasutajakonto";
"poll_edit_form_poll_type_closed_description" = "Tulemusi kuvame vaid siis, kui küsitlus on lõppenud";
"poll_edit_form_poll_type_closed" = "Küsitlus on lõppenud";
"poll_edit_form_poll_type_open_description" = "Osalejad näevad tulemusi peale oma valiku salvestamist";
"poll_edit_form_poll_type_open" = "Ava küsitlus";
"poll_edit_form_update_failure_subtitle" = "Palun proovi uuesti";
"poll_edit_form_update_failure_title" = "Küsitluse muutmine ei õnnestunud";
"poll_edit_form_poll_type" = "Küsitluse tüüp";

View file

@ -1356,7 +1356,7 @@
"less" = "Moins";
"more" = "Plus";
"switch" = "Basculer";
"joined" = "Rejoint";
"joined" = "A rejoint";
"store_promotional_text" = "Application de messagerie et collaboration respectueuse de la vie privée, sur un réseau ouvert. Décentralisée pour vous donner le contrôle. Pas dextraction de vos données, pas de porte dérobée, pas daccès réservé à des tiers.";
"room_details_search" = "Rechercher dans le salon";
"room_details_integrations" = "Intégrations";
@ -1633,3 +1633,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Créer un compte";
"ok" = "Ok";
"poll_edit_form_poll_type_closed_description" = "Les résultats ne sont dévoilés que lorsque vous terminez le sondage";
"poll_edit_form_poll_type_closed" = "Sondage fermé";
"poll_edit_form_poll_type_open_description" = "Les votants voient les résultats dès qu'ils ont voté";
"poll_edit_form_poll_type_open" = "Ouvrir le sondage";
"poll_edit_form_update_failure_subtitle" = "Veuillez réessayer";
"poll_edit_form_update_failure_title" = "Échec lors de la mise à jour du sondage";
"poll_edit_form_poll_type" = "Type de sondage";

View file

@ -1619,3 +1619,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Fiók létrehozása";
"poll_edit_form_poll_type_closed_description" = "Az eredmény csak a szavazás végeztével válik láthatóvá";
"poll_edit_form_poll_type_closed" = "Lezárt szavazás";
"poll_edit_form_poll_type_open_description" = "A szavazók a szavazásuk után látják a szavazatokat";
"poll_edit_form_poll_type_open" = "Szavazás megnyitása";
"poll_edit_form_update_failure_subtitle" = "Kérlek próbáld újra";
"poll_edit_form_update_failure_title" = "A szavazást nem sikerült frissíteni";
"poll_edit_form_poll_type" = "Szavazás típusa";

View file

@ -1735,3 +1735,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Buat akun";
"poll_edit_form_poll_type_closed_description" = "Hasil akan ditampilkan ketika Anda mengakhiri poll-nya";
"poll_edit_form_poll_type_closed" = "Poll tertutup";
"poll_edit_form_poll_type_open_description" = "Pemberi suara akan melihat hasilnya ketika telah memberikan suara";
"poll_edit_form_poll_type_open" = "Buka poll";
"poll_edit_form_update_failure_subtitle" = "Silakan coba lagi";
"poll_edit_form_update_failure_title" = "Gagal untuk memperbarui poll";
"poll_edit_form_poll_type" = "Tipe poll";

View file

@ -1590,3 +1590,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Crea account";
"poll_edit_form_poll_type_closed_description" = "I risultati verranno rivelati solo quando termini il sondaggio";
"poll_edit_form_poll_type_closed" = "Sondaggio chiuso";
"poll_edit_form_poll_type_open_description" = "I votanti vedono i risultati appena avranno votato";
"poll_edit_form_poll_type_open" = "Apri sondaggio";
"poll_edit_form_update_failure_subtitle" = "Riprova";
"poll_edit_form_update_failure_title" = "Aggiornamento del sondaggio fallito";
"poll_edit_form_poll_type" = "Tipo sondaggio";

View file

@ -1719,3 +1719,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Account aanmaken";
"poll_edit_form_poll_type_closed_description" = "Resultaten worden pas onthuld als u de poll beëindigt";
"poll_edit_form_poll_type_closed" = "Gesloten poll";
"poll_edit_form_poll_type_open_description" = "Kiezers zien resultaten zodra ze hebben gestemd";
"poll_edit_form_poll_type_open" = "Poll openen";
"poll_edit_form_update_failure_subtitle" = "Probeer het opnieuw";
"poll_edit_form_update_failure_title" = "Kan poll niet bijwerken";
"poll_edit_form_poll_type" = "Poll type";

View file

@ -845,7 +845,7 @@
"room_accessibility_upload" = "Fazer upload";
"room_accessibility_hangup" = "Desligar";
"media_type_accessibility_image" = "Imagem";
"media_type_accessibility_location" = "Local";
"media_type_accessibility_location" = "Localização";
"external_link_confirmation_title" = "Cheque duplamente este link";
"external_link_confirmation_message" = "O link %@ vai levar você para um outro site: %@↵\n↵\nVocê tem certeza que você quer continuar?";
"settings_discovery_settings" = "DESCOBERTA";
@ -1558,14 +1558,14 @@
"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.";
"location_sharing_settings_toggle_title" = "Habilitar compartilhamento de localização";
"location_sharing_settings_header" = "Compartilhamento de localização";
"location_sharing_invalid_authorization_error_title" = "%@ não tem permissão para acessar sua localização. Você pode habilitar acesso em Ajustes > Localização";
"location_sharing_locating_user_error_title" = "%@ não pôde acessar sua localização. Por favor tente de novo mais tarde.";
// MARK: - Location sharing
"location_sharing_title" = "Local";
"location_sharing_title" = "Localização";
"location_sharing_open_google_maps" = "Abrir em Google Maps";
"location_sharing_open_apple_maps" = "Abrir em Apple Mapas";
"location_sharing_invalid_authorization_settings" = "Ajustes";
@ -1576,7 +1576,7 @@
"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_message" = "Element 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.";
@ -1587,3 +1587,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Criar conta";
"poll_edit_form_poll_type_closed_description" = "Resultados são somente revelados quando você termina a sondagem";
"poll_edit_form_poll_type_open" = "Sondagem aberta";
"poll_edit_form_poll_type_closed" = "Sondagem fechada";
"poll_edit_form_poll_type_open_description" = "Votantes veem resultados assim que elas(es) têm votado";
"poll_edit_form_update_failure_subtitle" = "Por favor tente de novo";
"poll_edit_form_update_failure_title" = "Falha para atualizar sondagem";
"poll_edit_form_poll_type" = "Tipo de sondagem";

View file

@ -5,3 +5,4 @@
"NSContactsUsageDescription" = "Element покажет ваши контакты, чтобы вы могли пригласить их в чат.";
"NSCalendarsUsageDescription" = "Просматривайте запланированные встречи в приложении.";
"NSFaceIDUsageDescription" = "Face ID используется для доступа к вашему приложению.";
"NSLocationWhenInUseUsageDescription" = "Когда вы делитесь с людьми своим местоположением, Element необходим доступ, чтобы показать им карту.";

View file

@ -116,3 +116,6 @@
/** General **/
"NOTIFICATION" = "Уведомление";
/* New file message from a specific person, not referencing a room. */
"LOCATION_FROM_USER" = "%@ поделились своим местоположением";

View file

@ -1341,7 +1341,7 @@
"side_menu_app_version" = "Версия %@";
"side_menu_action_feedback" = "Отзыв";
"side_menu_action_help" = "Помощь";
"side_menu_action_settings" = "Настойки";
"side_menu_action_settings" = "Настройки";
"side_menu_action_invite_friends" = "Пригласить друзей";
// Mark: - Side menu

View file

@ -1388,7 +1388,7 @@
// Unverified sessions
"key_verification_self_verify_unverified_sessions_alert_title" = "Zobraziť, kde ste prihlásený";
"key_verification_self_verify_unverified_sessions_alert_title" = "Skontrolujte, kde ste prihlásení";
"key_verification_self_verify_current_session_alert_message" = "Ostatní používatelia jej nemusia dôverovať.";
// Current session
@ -1664,3 +1664,48 @@
"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";
"share_extension_failed_to_encrypt" = "Nepodarilo sa odoslať. Skontrolujte v hlavnej aplikácii nastavenia šifrovania pre túto miestnosť";
"service_terms_modal_policy_checkbox_accessibility_hint" = "Označte, či chcete prijať %@";
// Intro
"key_backup_setup_intro_title" = "Nikdy neprídete o šifrované správy";
"key_backup_setup_passphrase_setup_recovery_key_info" = "Alebo zabezpečte zálohu bezpečnostným kľúčom a uložte ju na bezpečné miesto.";
"key_verification_user_title" = "Overte ich";
"poll_edit_form_poll_question_or_topic" = "Otázka alebo téma ankety";
"poll_timeline_total_no_votes" = "Žiadne odovzdané hlasy";
"poll_timeline_total_one_vote_not_voted" = "1 odovzdaný hlas. Hlasujte a pozrite si výsledky";
"poll_timeline_total_final_results_one_vote" = "Konečný výsledok na základe 1 hlasu";
"poll_timeline_total_final_results" = "Konečný výsledok na základe %lu hlasov";
"poll_edit_form_poll_type" = "Typ ankety";
"poll_edit_form_update_failure_title" = "Nepodarilo sa aktualizovať anketu";
"poll_edit_form_update_failure_subtitle" = "Prosím, skúste to znova";
"poll_edit_form_poll_type_open" = "Otvoriť anketu";
"poll_edit_form_poll_type_open_description" = "Hlasujúci uvidia výsledky hneď po hlasovaní";
"poll_edit_form_poll_type_closed_description" = "Výsledky sa zobrazia až po ukončení ankety";
"poll_timeline_total_votes_not_voted" = "%lu odovzdaných hlasov. Hlasujte a pozrite si výsledky";
"poll_timeline_total_votes" = "%lu odovzdaných hlasov";
"poll_timeline_total_one_vote" = "1 odovzdaný hlas";
"poll_edit_form_poll_type_closed" = "Uzavretá anketa";
"poll_edit_form_post_failure_title" = "Nepodarilo sa odoslať anketu";
"call_transfer_error_message" = "Prenos hovoru zlyhal";
"user_verification_session_details_information_untrusted_other_user" = " sa prihlásil pomocou novej relácie:";
"key_verification_bootstrap_not_setup_message" = "Najprv musíte zaviesť krížové podpisovanie.";
"device_verification_emoji_pin" = "Špendlík";
"device_verification_emoji_folder" = "Fascikel";
"secure_key_backup_setup_existing_backup_error_info" = "Odomknite to a znovu to použite v zabezpečenej zálohe alebo to vymažte a vytvorte novú zálohu správ v zabezpečenej zálohe.";
// Share extension
"share_extension_auth_prompt" = "Prihláste sa v hlavnej aplikácii na zdieľanie obsahu";
// Widget Integration Manager
"widget_integration_need_to_be_able_to_invite" = "Musíte mať možnosť pozvať používateľov, aby ste to mohli urobiť.";
"bug_report_logs_description" = "Za účelom diagnostiky problémov budú spolu s týmto hlásením o chybe odoslané protokoly z tohto klienta. Ak chcete radšej posielať len vyššie uvedený text, zrušte prosím označené:";
"call_no_stun_server_error_message_1" = "Požiadajte prosím správcu vášho domovského servera %@ o nastavenie servera TURN, aby hovory fungovali spoľahlivo.";
"call_no_stun_server_error_title" = "Hovor zlyhal z dôvodu nesprávne nastaveného servera";
"room_details_addresses_disable_main_address_prompt_title" = "Upozornenie na hlavnú adresu";
"manage_session_trusted" = "Dôveryhodný pre vás";
"settings_contacts_phonebook_country" = "Krajina telefónneho zoznamu";
"settings_ui_theme_picker_message_invert_colours" = "\"Auto\" používa nastavenia \"Invertovať farby\" vášho zariadenia";
"external_link_confirmation_title" = "Prekontrolujte tento odkaz";
"callbar_only_single_active_group" = "Ťuknutím sa pripojíte k skupinovému hovoru (%@)";

View file

@ -1608,3 +1608,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Krijoni llogari";
"poll_edit_form_poll_type_open" = "Anketim i hapur";
"poll_edit_form_poll_type_closed_description" = "Përfundimet shfaqen vetëm kur përfundoni anketimin";
"poll_edit_form_poll_type_closed" = "Anketim i mbyllur";
"poll_edit_form_poll_type_open_description" = "Votuesit do të shohin përfundime sapo të kenë votuar";
"poll_edit_form_update_failure_subtitle" = "Ju lutemi, riprovoni";
"poll_edit_form_update_failure_title" = "Su arrit të përditësohet anketimi";
"poll_edit_form_poll_type" = "Lloj anketimi";

View file

@ -276,7 +276,7 @@
"settings_ignored_users" = "IGNORERADE ANVÄNDARE";
"settings_contacts" = "ENHETSKONTAKTER";
"settings_advanced" = "AVANCERAT";
"settings_other" = "ANNAT";
"settings_other" = "Annat";
"settings_labs" = "EXPERIMENT";
"settings_devices" = "SESSIONER";
"settings_cryptography" = "KRYPTOGRAFI";
@ -430,7 +430,7 @@
"room_participants_action_mention" = "Nämn";
"room_participants_security_information_room_encrypted" = "Meddelanden i det här rummet är totalsträckskrypterade.\n\nDina meddelanden är säkrade med lås och bara du och mottagaren har de unika nycklarna för att låsa upp dem.";
"room_accessiblity_scroll_to_bottom" = "Scrolla till botten";
"room_do_not_have_permission_to_post" = "Du har inte behörighet att posta till det här rummet";
"room_do_not_have_permission_to_post" = "Du är inte behörig att posta till det här rummet";
"room_unsent_messages_notification" = "Meddelanden misslyckades att skickas.";
"room_conference_call_no_power" = "Du behöver behörighet för att hantera gruppsamtal i det här rummet";
"room_prompt_resend" = "Skicka alla igen";
@ -704,7 +704,7 @@
"event_formatter_jitsi_widget_removed_by_you" = "Du tog bort VoIP-konferens";
"public_room_section_title" = "Offentliga rum (på %@):";
"bug_report_prompt" = "Appen har kraschat förra gången. Vill du skicka en kraschrapport?";
"camera_access_not_granted" = "%@ verkar inte ha behörighet att använda kameran, vänligen ändra integritetsinställningarna";
"camera_access_not_granted" = "%@ verkar inte vara behörig att använda kameran, vänligen ändra integritetsinställningarna";
"photo_library_access_not_granted" = "%@ verkar inte ha åtkomst till bildbiblioteket, vänligen ändra integritetsinställningarna";
"large_badge_value_k_format" = "%.1fK";
"call_incoming_voice" = "Inkommande samtal…";
@ -838,7 +838,7 @@
// Widget Integration Manager
"widget_integration_need_to_be_able_to_invite" = "Du behöver kunna bjuda in användare för att göra det där.";
"widget_integration_positive_power_level" = "Behörighetsnivå måste vara ett positivt heltal.";
"widget_integration_no_permission_in_room" = "Du har inte behörighet att göra det där i det här rummet.";
"widget_integration_no_permission_in_room" = "Du är inte behörig att göra det där i det här rummet.";
"widget_integration_manager_disabled" = "Du behöver aktivera integrationshanteraren i inställningarna";
"room_widget_permission_webview_information_title" = "Att använda den kan sätta kakor och dela data med %@:\n";
"room_widget_permission_information_title" = "Att använda den kan dela data med %@:\n";
@ -1452,7 +1452,7 @@
"space_home_show_all_rooms" = "Visa alla rum";
"space_participants_action_ban" = "Banna från det här utrymmet";
"space_participants_action_remove" = "Ta bort från det här utrymmet";
"spaces_coming_soon_detail" = "Den här funktionen har inte bjudits in än, men den är på väg. För tillfället så kan du göra det med Element på din dator.";
"spaces_coming_soon_detail" = "Den här funktionen har inte implementerats än, men den är på väg. För tillfället så kan du göra det med %@ på din dator.";
"spaces_invites_coming_soon_title" = "Inbjudningar kommer snart";
"spaces_add_rooms_coming_soon_title" = "Tilläggning av rum kommer snart";
"spaces_coming_soon_title" = "Kommer snart";

View file

@ -1925,6 +1925,31 @@ Library.
SOFTWARE.
<br/><br/>
</li>
<li>
<b>UICollectionViewRightAlignedLayout</b> (<a href="https://github.com/mokagio/UICollectionViewRightAlignedLayout">https://github.com/mokagio/UICollectionViewRightAlignedLayout</a>)
<br/><br/>
The MIT License (MIT)
<br/><br/>
Copyright (c) 2014 Giovanni Lodi
<br/><br/>
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:
<br/><br/>
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
<br/><br/>
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.
<br/><br/>
</li>
</ul>
</body>
</html>

View file

@ -1731,3 +1731,10 @@
// Onboarding
"onboarding_splash_register_button_title" = "Створити обліковий запис";
"poll_edit_form_poll_type_open_description" = "Усі, хто проголосує, побачать результати одразу після голосування";
"poll_edit_form_poll_type_closed_description" = "Результати можна переглянути лише після завершення опитування";
"poll_edit_form_poll_type_closed" = "Закрите опитування";
"poll_edit_form_poll_type_open" = "Відкрити опитування";
"poll_edit_form_update_failure_subtitle" = "Повторіть спробу";
"poll_edit_form_update_failure_title" = "Не вдалося оновити опитування";
"poll_edit_form_poll_type" = "Тип опитування";

View file

@ -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)
}
}

View file

@ -18,16 +18,13 @@ import Foundation
extension MXKRoomBubbleCellData {
/// Indicate true if the sender is the session user
var isSenderCurrentUser: Bool {
if let senderId = self.senderId, let currentUserId = self.mxSession.myUserId, senderId == currentUserId {
return true
}
return false
}
// 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
}
}

View file

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

View file

@ -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

View file

@ -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 {

View file

@ -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
}
}

View file

@ -110,6 +110,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")
@ -135,6 +143,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")
@ -158,6 +167,9 @@ internal enum Asset {
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")
@ -174,7 +186,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")

View file

@ -279,6 +279,11 @@ internal enum StoryboardScene {
internal static let initialScene = InitialSceneType<Riot.TemplateScreenViewController>(storyboard: TemplateScreenViewController.self)
}
internal enum ThreadListViewController: StoryboardType {
internal static let storyboardName = "ThreadListViewController"
internal static let initialScene = InitialSceneType<Riot.ThreadListViewController>(storyboard: ThreadListViewController.self)
}
internal enum UserVerificationSessionStatusViewController: StoryboardType {
internal static let storyboardName = "UserVerificationSessionStatusViewController"

View file

@ -115,7 +115,7 @@ public class VectorL10n: NSObject {
public static var authEmailInUse: String {
return VectorL10n.tr("Vector", "auth_email_in_use")
}
/// No identity server is configured so you cannot add an email address in order to reset your password in the future.
/// No identity server is configured so you cannot add an email address in order to reset your Matrix account password in the future.
public static var authEmailIsRequired: String {
return VectorL10n.tr("Vector", "auth_email_is_required")
}
@ -131,11 +131,11 @@ public class VectorL10n: NSObject {
public static var authEmailValidationMessage: String {
return VectorL10n.tr("Vector", "auth_email_validation_message")
}
/// Forgot password?
/// Forgot Matrix account password?
public static var authForgotPassword: String {
return VectorL10n.tr("Vector", "auth_forgot_password")
}
/// No identity server is configured: add one to reset your password.
/// No identity server is configured: add one to reset your Matrix account password.
public static var authForgotPasswordErrorNoConfiguredIdentityServer: String {
return VectorL10n.tr("Vector", "auth_forgot_password_error_no_configured_identity_server")
}
@ -227,7 +227,7 @@ public class VectorL10n: NSObject {
public static var authPhoneInUse: String {
return VectorL10n.tr("Vector", "auth_phone_in_use")
}
/// No identity server is configured so you cannot add a phone number in order to reset your password in the future.
/// No identity server is configured so you cannot add a phone number in order to reset your Matrix account password in the future.
public static var authPhoneIsRequired: String {
return VectorL10n.tr("Vector", "auth_phone_is_required")
}
@ -243,7 +243,7 @@ public class VectorL10n: NSObject {
public static var authRegister: String {
return VectorL10n.tr("Vector", "auth_register")
}
/// Confirm your new password
/// Confirm your new Matrix account password
public static var authRepeatNewPasswordPlaceholder: String {
return VectorL10n.tr("Vector", "auth_repeat_new_password_placeholder")
}
@ -255,7 +255,7 @@ public class VectorL10n: NSObject {
public static func authResetPasswordEmailValidationMessage(_ p1: String) -> String {
return VectorL10n.tr("Vector", "auth_reset_password_email_validation_message", p1)
}
/// No identity server is configured: add one in server options to reset your password.
/// No identity server is configured: add one in server options to reset your Matrix account password.
public static var authResetPasswordErrorIsRequired: String {
return VectorL10n.tr("Vector", "auth_reset_password_error_is_required")
}
@ -267,7 +267,7 @@ public class VectorL10n: NSObject {
public static var authResetPasswordErrorUnauthorized: String {
return VectorL10n.tr("Vector", "auth_reset_password_error_unauthorized")
}
/// To reset your password, enter the email address linked to your account:
/// To reset your Matrix account password, enter the email address linked to your account:
public static var authResetPasswordMessage: String {
return VectorL10n.tr("Vector", "auth_reset_password_message")
}
@ -283,7 +283,7 @@ public class VectorL10n: NSObject {
public static var authResetPasswordNextStepButton: String {
return VectorL10n.tr("Vector", "auth_reset_password_next_step_button")
}
/// Your password has been reset.\n\nYou have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, re-log in on each device.
/// Your Matrix account password has been reset.\n\nYou have been logged out of all sessions and will no longer receive push notifications. To re-enable notifications, re-log in on each device.
public static var authResetPasswordSuccessMessage: String {
return VectorL10n.tr("Vector", "auth_reset_password_success_message")
}
@ -747,7 +747,7 @@ public class VectorL10n: NSObject {
public static var deactivateAccountInformationsPart5: String {
return VectorL10n.tr("Vector", "deactivate_account_informations_part5")
}
/// To continue, please enter your password
/// To continue, please enter your Matrix account password
public static var deactivateAccountPasswordAlertMessage: String {
return VectorL10n.tr("Vector", "deactivate_account_password_alert_message")
}
@ -1419,6 +1419,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")
@ -1595,6 +1599,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")
@ -1831,7 +1875,7 @@ public class VectorL10n: NSObject {
public static var keyBackupSetupPassphraseConfirmPassphraseValid: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_confirm_passphrase_valid")
}
/// We'll store an encrypted copy of your keys on our server. Protect your backup with a phrase to keep it secure.\n\nFor maximum security, this should be different from your account password.
/// We'll store an encrypted copy of your keys on our server. Protect your backup with a phrase to keep it secure.\n\nFor maximum security, this should be different from your Matrix account password.
public static var keyBackupSetupPassphraseInfo: String {
return VectorL10n.tr("Vector", "key_backup_setup_passphrase_info")
}
@ -2223,6 +2267,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 couldnt 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")
@ -2315,6 +2367,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")
@ -2687,6 +2743,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")
@ -3167,7 +3231,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")
}
@ -3199,6 +3263,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")
@ -3231,10 +3299,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")
@ -3799,6 +3875,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")
@ -4035,7 +4115,7 @@ public class VectorL10n: NSObject {
public static var secretsRecoveryWithPassphraseTitle: String {
return VectorL10n.tr("Vector", "secrets_recovery_with_passphrase_title")
}
/// Enter your account password to confirm
/// Enter your Matrix account password to confirm
public static var secretsResetAuthenticationMessage: String {
return VectorL10n.tr("Vector", "secrets_reset_authentication_message")
}
@ -4087,7 +4167,7 @@ public class VectorL10n: NSObject {
public static var secretsSetupRecoveryKeyTitle: String {
return VectorL10n.tr("Vector", "secrets_setup_recovery_key_title")
}
/// Don't use your account password.
/// Don't use your Matrix account password.
public static var secretsSetupRecoveryPassphraseAdditionalInformation: String {
return VectorL10n.tr("Vector", "secrets_setup_recovery_passphrase_additional_information")
}
@ -4243,7 +4323,7 @@ public class VectorL10n: NSObject {
public static var securitySettingsCryptoSessions: String {
return VectorL10n.tr("Vector", "security_settings_crypto_sessions")
}
/// If you dont recognise a login, change your password and reset Secure Backup.
/// If you dont recognise a login, change your Matrix account password and reset Secure Backup.
public static var securitySettingsCryptoSessionsDescription2: String {
return VectorL10n.tr("Vector", "security_settings_crypto_sessions_description_2")
}
@ -4295,7 +4375,7 @@ public class VectorL10n: NSObject {
public static var securitySettingsTitle: String {
return VectorL10n.tr("Vector", "security_settings_title")
}
/// Confirm your identity by entering your account password
/// Confirm your identity by entering your Matrix account password
public static var securitySettingsUserPasswordDescription: String {
return VectorL10n.tr("Vector", "security_settings_user_password_description")
}
@ -4367,7 +4447,7 @@ public class VectorL10n: NSObject {
public static var settingsAdd3pidInvalidPasswordMessage: String {
return VectorL10n.tr("Vector", "settings_add_3pid_invalid_password_message")
}
/// To continue, please enter your password
/// To continue, please enter your Matrix account password
public static var settingsAdd3pidPasswordMessage: String {
return VectorL10n.tr("Vector", "settings_add_3pid_password_message")
}
@ -4415,7 +4495,7 @@ public class VectorL10n: NSObject {
public static func settingsCallsStunServerFallbackDescription(_ p1: String) -> String {
return VectorL10n.tr("Vector", "settings_calls_stun_server_fallback_description", p1)
}
/// Change password
/// Change Matrix account password
public static var settingsChangePassword: String {
return VectorL10n.tr("Vector", "settings_change_password")
}
@ -4619,7 +4699,7 @@ public class VectorL10n: NSObject {
public static var settingsEncryptedGroupMessages: String {
return VectorL10n.tr("Vector", "settings_encrypted_group_messages")
}
/// Fail to update password
/// Fail to update Matrix account password
public static var settingsFailToUpdatePassword: String {
return VectorL10n.tr("Vector", "settings_fail_to_update_password")
}
@ -4787,6 +4867,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")
@ -4871,7 +4955,7 @@ public class VectorL10n: NSObject {
public static var settingsOther: String {
return VectorL10n.tr("Vector", "settings_other")
}
/// Your password has been updated
/// Your Matrix account password has been updated
public static var settingsPasswordUpdated: String {
return VectorL10n.tr("Vector", "settings_password_updated")
}
@ -5295,6 +5379,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")

View file

@ -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.
@ -185,13 +189,10 @@ final class RiotSettings: NSObject {
@UserDefault(key: "roomScreenAllowFilesAction", defaultValue: BuildSettings.roomScreenAllowFilesAction, storage: defaults)
var roomScreenAllowFilesAction
@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 {

View file

@ -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)

View file

@ -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];

View file

@ -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) {

View file

@ -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) {

View file

@ -155,7 +155,7 @@ class UserSessionsService: NSObject {
let isSessionStateValid: Bool
switch mxSession.state {
case .closed, .unknownToken:
case .closed:
isSessionStateValid = false
default:
isSessionStateValid = true

View file

@ -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
}
}

View file

@ -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

View file

@ -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
}
}

View file

@ -96,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.
@ -213,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.

View file

@ -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,73 @@ 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) {
MXLogError(@"[LegacyAppDelegate] handleUniversalLinkWithParameters: event fetch failed: %@", 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;
@ -1412,7 +1469,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 +1528,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 +1613,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 +1632,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 +1649,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 +1661,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
MXLogDebug(@"[AppDelegate] Universal link with registration parameters");
continueUserActivity = YES;
[_masterTabBarController showAuthenticationScreenWithRegistrationParameters:queryParams];
[_masterTabBarController showOnboardingFlowWithRegistrationParameters:queryParams];
}
else
{
@ -1744,33 +1801,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;
@ -1779,7 +1839,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:@"/"])
{
@ -1799,14 +1859,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];
@ -1965,7 +2025,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) {
@ -2259,7 +2319,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
}
// Return to authentication screen
[_masterTabBarController showAuthenticationScreen];
[_masterTabBarController showOnboardingFlow];
// Note: Keep App settings
// But enforce usage of member lazy loading
@ -2298,7 +2358,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
{
BOOL isLaunching = NO;
if (_masterTabBarController.authenticationInProgress)
if (_masterTabBarController.isOnboardingInProgress)
{
MXLogDebug(@"[AppDelegate] handleAppState: Authentication still in progress");
@ -2893,7 +2953,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];
}
@ -3415,7 +3478,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.");
}];
@ -3854,7 +3917,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
return;
}
if (_masterTabBarController.authenticationInProgress)
if (_masterTabBarController.isOnboardingInProgress)
{
MXLogDebug(@"[AppDelegate][KeyVerification] keyVerificationNewRequestNotification: Postpone requests during the authentication process");
@ -4496,15 +4559,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

View file

@ -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()

View file

@ -34,6 +34,7 @@ class RoomPreviewNavigationParameters: RoomNavigationParameters {
super.init(roomId: previewData.roomId,
eventId: previewData.eventId,
mxSession: previewData.mxSession,
threadParameters: nil,
presentationParameters: presentationParameters)
}
}

View file

@ -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?()
}
}

View file

@ -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
}

View file

@ -26,13 +26,6 @@
// MXKAuthenticationViewController has already a `delegate` member
@property (nonatomic, weak) id<AuthenticationViewControllerDelegate> 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;

View file

@ -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,8 +475,8 @@ 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
&& BuildSettings.authScreenShowSocialLoginSection)
@ -488,6 +484,9 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
hideAuthInputView = YES;
}
// Note: Registration will hide the input view in onFailureDuringMXOperation
// if registration has been disabled.
self.authInputsView.hidden = hideAuthInputView;
}
@ -496,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)
@ -510,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
{
@ -530,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)
@ -552,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];
}
}
}
@ -562,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;
}
@ -579,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)
{
@ -607,7 +590,7 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
[self loginWithToken:loginToken];
return YES;
}
MXLogDebug(@"[AuthenticationVC] Fail to continue SSO login");
return NO;
}
@ -685,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];
}
@ -830,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];
@ -893,27 +883,6 @@ static const CGFloat kAuthInputContainerViewMinHeightConstraintConstant = 150.0;
return NO;
}
- (MXLoginSSOFlow*)logginSSOFlowWithProvidersFromFlows:(NSArray<MXLoginFlow*>*)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)
@ -938,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)
@ -949,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])
{
@ -1185,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;
}
}

View file

@ -34,14 +34,9 @@
<outlet property="identityServerLabel" destination="5CT-Ht-Z3v" id="9h4-6J-n3X"/>
<outlet property="identityServerSeparator" destination="t08-ua-0mJ" id="Sbf-ps-zeK"/>
<outlet property="identityServerTextField" destination="PZC-Hd-Q6a" id="vKg-sd-dzJ"/>
<outlet property="mainNavigationItem" destination="rj9-wP-8QS" id="yNN-Dg-4Yw"/>
<outlet property="navigationBackView" destination="6q5-bI-9WO" id="edE-wy-DYw"/>
<outlet property="navigationBar" destination="k7D-Gy-yBR" id="6Uc-3Q-3dO"/>
<outlet property="navigationBarSeparatorView" destination="izX-ya-hXh" id="gY5-Oj-nSl"/>
<outlet property="noFlowLabel" destination="54b-4O-ip9" id="f18-H1-cQm"/>
<outlet property="optionsContainer" destination="Gg0-TE-OGb" id="RWa-Pl-eaL"/>
<outlet property="retryButton" destination="wIH-Kd-r7q" id="42j-Ad-zVS"/>
<outlet property="rightBarButtonItem" destination="Kwt-KN-aVL" id="Y3F-wA-tf8"/>
<outlet property="serverOptionsContainer" destination="FIn-2w-e6H" id="Z0e-NN-3LY"/>
<outlet property="skipButton" destination="wEJ-AF-rdH" id="smu-MS-2IY"/>
<outlet property="socialLoginContainerView" destination="TjK-XL-dQS" id="Rte-h1-blp"/>
@ -59,35 +54,8 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6q5-bI-9WO">
<rect key="frame" x="0.0" y="0.0" width="375" height="88"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
<navigationBar contentMode="scaleToFill" translucent="NO" translatesAutoresizingMaskIntoConstraints="NO" id="k7D-Gy-yBR">
<rect key="frame" x="0.0" y="44" width="375" height="44"/>
<color key="barTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<items>
<navigationItem title="Riot" id="rj9-wP-8QS">
<barButtonItem key="rightBarButtonItem" title="Register" id="Kwt-KN-aVL">
<connections>
<action selector="onButtonPressed:" destination="-1" id="nwk-aV-eBO"/>
</connections>
</barButtonItem>
</navigationItem>
</items>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="AuthenticationVCNavigationBar"/>
</userDefinedRuntimeAttributes>
</navigationBar>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="izX-ya-hXh">
<rect key="frame" x="0.0" y="88" width="375" height="1"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="ZiW-uP-cgo"/>
</constraints>
</view>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" keyboardDismissMode="interactive" translatesAutoresizingMaskIntoConstraints="NO" id="OHV-KQ-Ww0">
<rect key="frame" x="0.0" y="89" width="375" height="723"/>
<rect key="frame" x="0.0" y="44" width="375" height="768"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rhx-dD-4EJ" userLabel="Content View">
<rect key="frame" x="0.0" y="0.0" width="375" height="485"/>
@ -129,7 +97,7 @@
<nil key="highlightedColor"/>
</label>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wIH-Kd-r7q" userLabel="retryButton">
<rect key="frame" x="165" y="46.666666666666686" width="45" height="30"/>
<rect key="frame" x="165" y="46.666666666666657" width="45" height="30"/>
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCRetryButton"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="WtO-NT-ei8"/>
@ -513,24 +481,14 @@ Clear it if you're finished using this device, or want to sign in to another acc
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="AuthenticationVCView"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="k7D-Gy-yBR" secondAttribute="trailing" id="5VB-NY-mpo"/>
<constraint firstAttribute="trailing" secondItem="izX-ya-hXh" secondAttribute="trailing" id="8Ae-F5-jr2"/>
<constraint firstItem="k7D-Gy-yBR" firstAttribute="top" secondItem="3h8-75-kcp" secondAttribute="top" id="8Fw-By-vab"/>
<constraint firstItem="OHV-KQ-Ww0" firstAttribute="top" secondItem="izX-ya-hXh" secondAttribute="bottom" id="BJv-Y7-VKe"/>
<constraint firstItem="OHV-KQ-Ww0" firstAttribute="top" secondItem="3h8-75-kcp" secondAttribute="top" id="1ep-Co-Ook"/>
<constraint firstItem="q1e-Wg-6t7" firstAttribute="trailing" secondItem="3h8-75-kcp" secondAttribute="trailing" id="Dcr-ff-OGs"/>
<constraint firstItem="q1e-Wg-6t7" firstAttribute="leading" secondItem="3h8-75-kcp" secondAttribute="leading" id="HTS-LJ-eiv"/>
<constraint firstItem="OHV-KQ-Ww0" firstAttribute="leading" secondItem="3h8-75-kcp" secondAttribute="leading" id="Lat-3d-ufd"/>
<constraint firstItem="izX-ya-hXh" firstAttribute="leading" secondItem="5rn-KE-plm" secondAttribute="leading" id="MrU-gL-vGG"/>
<constraint firstAttribute="bottom" secondItem="OHV-KQ-Ww0" secondAttribute="bottom" id="Q6p-jM-1nj"/>
<constraint firstItem="6q5-bI-9WO" firstAttribute="leading" secondItem="5rn-KE-plm" secondAttribute="leading" id="SQT-Gz-hp5"/>
<constraint firstItem="6q5-bI-9WO" firstAttribute="top" secondItem="5rn-KE-plm" secondAttribute="top" id="TeU-yo-Lbi"/>
<constraint firstItem="OHV-KQ-Ww0" firstAttribute="trailing" secondItem="3h8-75-kcp" secondAttribute="trailing" id="cIg-kU-obq"/>
<constraint firstAttribute="trailing" secondItem="6q5-bI-9WO" secondAttribute="trailing" id="eZp-eW-PG2"/>
<constraint firstItem="k7D-Gy-yBR" firstAttribute="bottom" secondItem="6q5-bI-9WO" secondAttribute="bottom" id="pmI-Og-NJA"/>
<constraint firstItem="q1e-Wg-6t7" firstAttribute="bottom" secondItem="3h8-75-kcp" secondAttribute="bottom" id="qqV-Kd-Qs1"/>
<constraint firstItem="q1e-Wg-6t7" firstAttribute="top" secondItem="3h8-75-kcp" secondAttribute="top" id="tSe-Uf-qYh"/>
<constraint firstItem="izX-ya-hXh" firstAttribute="top" secondItem="k7D-Gy-yBR" secondAttribute="bottom" id="y2h-1T-TZ4"/>
<constraint firstItem="k7D-Gy-yBR" firstAttribute="leading" secondItem="5rn-KE-plm" secondAttribute="leading" id="z6D-6L-5Ud"/>
</constraints>
<point key="canvasLocation" x="138.40000000000001" y="153.69458128078819"/>
</view>

View file

@ -645,8 +645,8 @@ CallAudioRouteMenuViewDelegate>
{
CallTransferMainViewController *controller = [CallTransferMainViewController instantiateWithSession:self.mainSession ignoredUserIds:@[self.peer.userId]];
controller.delegate = self;
UINavigationController *navController = [[RiotNavigationController alloc] initWithRootViewController:controller];
[self.mxCall hold:YES];
[self presentViewController:navController animated:YES completion:nil];
}
@ -732,6 +732,7 @@ CallAudioRouteMenuViewDelegate>
- (void)callTransferMainViewControllerDidCancel:(CallTransferMainViewController *)viewController
{
[self.mxCall hold:NO];
[viewController dismissViewControllerAnimated:YES completion:nil];
}

View file

@ -128,9 +128,11 @@ class AvatarView: UIView, Themable {
previewImage: defaultAvatarImage,
mediaManager: viewData.mediaManager)
avatarImageView.contentMode = .scaleAspectFill
avatarImageView.imageView?.contentMode = .scaleAspectFill
} else {
avatarImageView.image = defaultAvatarImage
avatarImageView.contentMode = defaultAvatarImageContentMode
avatarImageView.imageView?.contentMode = defaultAvatarImageContentMode
}
}

View file

@ -1,29 +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 UIKit
/// UICollectionViewFlowLayout with right align behavior
class CollectionViewRightAlignFlowLayout: UICollectionViewFlowLayout {
override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}
override var developmentLayoutDirection: UIUserInterfaceLayoutDirection {
return UIUserInterfaceLayoutDirection.rightToLeft
}
}

Some files were not shown because too many files have changed in this diff Show more