mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-11 02:17:43 +02:00
Merge pull request #7414 from vector-im/release/1.10.4/release
Release 1.10.4
This commit is contained in:
@@ -13,3 +13,25 @@ jobs:
|
||||
project: Issue triage
|
||||
column: Incoming
|
||||
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
add_to_triage:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.repository == 'vector-im/element-x-ios'
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
headers: '{"GraphQL-Features": "projects_next_graphql"}'
|
||||
query: |
|
||||
mutation add_to_project($projectid:ID!,$contentid:ID!) {
|
||||
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
|
||||
item {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
projectid: ${{ env.PROJECT_ID }}
|
||||
contentid: ${{ github.event.issue.node_id }}
|
||||
env:
|
||||
PROJECT_ID: "PVT_kwDOAM0swc4AMlHr"
|
||||
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
+19
@@ -1,3 +1,22 @@
|
||||
## Changes in 1.10.4 (2023-03-07)
|
||||
|
||||
🙌 Improvements
|
||||
|
||||
- CryptoV2: CryptoSDK phased rollout feature ([#7374](https://github.com/vector-im/element-ios/pull/7374))
|
||||
- Analytics: Use SwiftPM for AnalyticsEvents instead of CocoaPods ([#7401](https://github.com/vector-im/element-ios/pull/7401))
|
||||
- Upgrade MatrixSDK version ([v0.26.0](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.26.0)).
|
||||
- Session verification: automatically starts scanning for a QR code if we do not have a QR code to display. ([#3115](https://github.com/vector-im/element-ios/issues/3115))
|
||||
- Direct Message: manage encrypted DM in case of invite by email ([#6612](https://github.com/vector-im/element-ios/issues/6612))
|
||||
|
||||
🐛 Bugfixes
|
||||
|
||||
- fix issue on timeline's bubbles not showing proper content after decrypt ([#7397](https://github.com/vector-im/element-ios/pull/7397))
|
||||
- Fixes bug about centring user in live location sharing ([#7398](https://github.com/vector-im/element-ios/pull/7398))
|
||||
- Polls: improve rendering of poll ended events. ([#7402](https://github.com/vector-im/element-ios/pull/7402))
|
||||
- Fix an issue where SAS verification would fail between two iOS devices ([#3946](https://github.com/vector-im/element-ios/issues/3946))
|
||||
- Pin SwiftOGG to a release rather than main branch to avoid breaking changes in SwiftOGG causing bugs in element-ios. ([#7388](https://github.com/vector-im/element-ios/issues/7388))
|
||||
|
||||
|
||||
## Changes in 1.10.3 (2023-02-21)
|
||||
|
||||
🙌 Improvements
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
//
|
||||
|
||||
// Version
|
||||
MARKETING_VERSION = 1.10.3
|
||||
CURRENT_PROJECT_VERSION = 1.10.3
|
||||
MARKETING_VERSION = 1.10.4
|
||||
CURRENT_PROJECT_VERSION = 1.10.4
|
||||
|
||||
@@ -92,14 +92,8 @@ class CommonConfiguration: NSObject, Configurable {
|
||||
|
||||
sdkOptions.enableNewClientInformationFeature = RiotSettings.shared.enableClientInformationFeature
|
||||
|
||||
if sdkOptions.isCryptoSDKAvailable {
|
||||
let isEnabled = RiotSettings.shared.enableCryptoSDK
|
||||
MXLog.debug("[CommonConfiguration] Crypto SDK is \(isEnabled ? "enabled" : "disabled")")
|
||||
sdkOptions.enableCryptoSDK = isEnabled
|
||||
sdkOptions.enableStartupProgress = isEnabled
|
||||
} else {
|
||||
MXLog.debug("[CommonConfiguration] Crypto SDK is not available)")
|
||||
}
|
||||
// Configure Crypto SDK feature deciding which crypto module to use
|
||||
sdkOptions.cryptoSDKFeature = CryptoSDKFeature.shared
|
||||
}
|
||||
|
||||
private func makeASCIIUserAgent() -> String? {
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
//
|
||||
// Copyright 2023 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
|
||||
|
||||
/// Configuration for enabling / disabling Matrix Crypto SDK
|
||||
@objcMembers class CryptoSDKConfiguration: NSObject {
|
||||
static let shared = CryptoSDKConfiguration()
|
||||
|
||||
func enable() {
|
||||
guard MXSDKOptions.sharedInstance().isCryptoSDKAvailable else {
|
||||
return
|
||||
}
|
||||
|
||||
RiotSettings.shared.enableCryptoSDK = true
|
||||
MXSDKOptions.sharedInstance().enableCryptoSDK = true
|
||||
MXSDKOptions.sharedInstance().enableStartupProgress = true
|
||||
|
||||
MXLog.debug("[CryptoSDKConfiguration] enabling Crypto SDK")
|
||||
}
|
||||
|
||||
func disable() {
|
||||
RiotSettings.shared.enableCryptoSDK = false
|
||||
MXSDKOptions.sharedInstance().enableCryptoSDK = false
|
||||
MXSDKOptions.sharedInstance().enableStartupProgress = false
|
||||
|
||||
MXLog.debug("[CryptoSDKConfiguration] disabling Crypto SDK")
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ use_frameworks!
|
||||
# - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI
|
||||
#
|
||||
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
|
||||
$matrixSDKVersion = '= 0.25.2'
|
||||
$matrixSDKVersion = '= 0.26.0'
|
||||
# $matrixSDKVersion = :local
|
||||
# $matrixSDKVersion = { :branch => 'develop'}
|
||||
# $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } }
|
||||
@@ -70,10 +70,8 @@ abstract_target 'RiotPods' do
|
||||
pod 'WeakDictionary', '~> 2.0'
|
||||
|
||||
# PostHog for analytics
|
||||
pod 'PostHog', '~> 1.4.4'
|
||||
pod 'PostHog', '~> 2.0.0'
|
||||
pod 'Sentry', '~> 7.15.0'
|
||||
pod 'AnalyticsEvents', :git => 'https://github.com/matrix-org/matrix-analytics-events.git', :branch => 'release/swift', :inhibit_warnings => false
|
||||
# pod 'AnalyticsEvents', :path => '../matrix-analytics-events/AnalyticsEvents.podspec'
|
||||
|
||||
pod 'OLMKit'
|
||||
pod 'zxcvbn-ios'
|
||||
|
||||
+4
-17
@@ -14,7 +14,6 @@ PODS:
|
||||
- AFNetworking/Serialization (4.0.1)
|
||||
- AFNetworking/UIKit (4.0.1):
|
||||
- AFNetworking/NSURLSession
|
||||
- AnalyticsEvents (0.1.0)
|
||||
- BlueCryptor (1.0.32)
|
||||
- BlueECC (1.2.5)
|
||||
- BlueRSA (1.0.200)
|
||||
@@ -57,7 +56,7 @@ PODS:
|
||||
- OLMKit/olmcpp (= 3.2.12)
|
||||
- OLMKit/olmc (3.2.12)
|
||||
- OLMKit/olmcpp (3.2.12)
|
||||
- PostHog (1.4.4)
|
||||
- PostHog (2.0.0)
|
||||
- ReadMoreTextView (3.0.1)
|
||||
- Realm (10.27.0):
|
||||
- Realm/Headers (= 10.27.0)
|
||||
@@ -91,7 +90,6 @@ PODS:
|
||||
- ZXingObjC/All (3.6.5)
|
||||
|
||||
DEPENDENCIES:
|
||||
- AnalyticsEvents (from `https://github.com/matrix-org/matrix-analytics-events.git`, branch `release/swift`)
|
||||
- Down (~> 0.11.0)
|
||||
- DSBottomSheet (~> 0.3)
|
||||
- DSWaveformImage (~> 6.1.1)
|
||||
@@ -105,7 +103,7 @@ DEPENDENCIES:
|
||||
- MatrixSDK (= 0.25.2)
|
||||
- MatrixSDK/JingleCallStack (= 0.25.2)
|
||||
- OLMKit
|
||||
- PostHog (~> 1.4.4)
|
||||
- PostHog (~> 2.0.0)
|
||||
- ReadMoreTextView (~> 3.0.1)
|
||||
- Reusable (~> 4.1)
|
||||
- Sentry (~> 7.15.0)
|
||||
@@ -164,19 +162,8 @@ SPEC REPOS:
|
||||
- zxcvbn-ios
|
||||
- ZXingObjC
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
AnalyticsEvents:
|
||||
:branch: release/swift
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
AnalyticsEvents:
|
||||
:commit: 53ad46ba1ea1ee8f21139dda3c351890846a202f
|
||||
:git: https://github.com/matrix-org/matrix-analytics-events.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AFNetworking: 3bd23d814e976cd148d7d44c3ab78017b744cd58
|
||||
AnalyticsEvents: 0cc8cf52da2fd464a2f39b788a295988151116ce
|
||||
BlueCryptor: b0aee3d9b8f367b49b30de11cda90e1735571c24
|
||||
BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc
|
||||
BlueRSA: dfeef51db96bcc4edec654956c1581adbda4e6a3
|
||||
@@ -199,7 +186,7 @@ SPEC CHECKSUMS:
|
||||
MatrixSDK: 354274127d163af37bdc55093ab96deea1be6a40
|
||||
MatrixSDKCrypto: e1ef22aae76b5a6f030ace21a47be83864f4ff44
|
||||
OLMKit: da115f16582e47626616874e20f7bb92222c7a51
|
||||
PostHog: 4b6321b521569092d4ef3a02238d9435dbaeb99f
|
||||
PostHog: 660ec6c9d80cec17b685e148f17f6785a88b597d
|
||||
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
|
||||
Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2
|
||||
Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136
|
||||
@@ -217,6 +204,6 @@ SPEC CHECKSUMS:
|
||||
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: 20544e99d9acfdfbc4bf98b21d20c1496b9d6fe9
|
||||
PODFILE CHECKSUM: b95e7964fe3b77759768daa7d9e4988f80210056
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
|
||||
@@ -36,6 +36,15 @@
|
||||
"version" : "5.12.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "matrix-analytics-events",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/matrix-org/matrix-analytics-events",
|
||||
"state" : {
|
||||
"revision" : "2f5fa5f1e2f6c6ae1a47c33d953a3ce289167eb0",
|
||||
"version" : "0.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "matrix-wysiwyg-composer-swift",
|
||||
"kind" : "remoteSourceControl",
|
||||
@@ -77,7 +86,7 @@
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/vector-im/swift-ogg",
|
||||
"state" : {
|
||||
"branch" : "main",
|
||||
"branch" : "0.0.1",
|
||||
"revision" : "e9a9e7601da662fd8b97d93781ff5c60b4becf88"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,6 +365,7 @@
|
||||
"room_creation_invite_another_user" = "User ID, name or email";
|
||||
"room_creation_error_invite_user_by_email_without_identity_server" = "No identity server is configured so you cannot add a participant with an email.";
|
||||
"room_creation_dm_error" = "We couldn't create your DM. Please check the users you want to invite and try again.";
|
||||
"room_creation_only_one_email_invite" = "You can only invite one email at a time";
|
||||
|
||||
// Room recents
|
||||
"room_recents_directory_section" = "ROOM DIRECTORY";
|
||||
@@ -1570,9 +1571,11 @@ Tap the + to start adding people.";
|
||||
"device_verification_self_verify_wait_new_sign_in_title" = "Verify this login";
|
||||
"device_verification_self_verify_wait_information" = "Verify this session from one of your other sessions, granting it access to encrypted messages.\n\nUse the latest %@ on your other devices:";
|
||||
"device_verification_self_verify_wait_additional_information" = "This works with %@ and other cross-signing capable Matrix clients.";
|
||||
"device_verification_self_verify_open_on_other_device_title" = "Open %@ on your other device";
|
||||
"device_verification_self_verify_open_on_other_device_information" = "You need to verify this session in order to read your secure message history.\n\nOpen Element on one of your other devices and follow the instructions.";
|
||||
"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Use Security Key";
|
||||
"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Use Security Phrase or Key";
|
||||
"device_verification_self_verify_wait_recover_secrets_additional_information" = "If you can't access an existing session";
|
||||
"device_verification_self_verify_wait_recover_secrets_additional_help" = "Can't access an existing %@ session?";
|
||||
"device_verification_self_verify_wait_recover_secrets_checking_availability" = "Checking for other verification capabilities ...";
|
||||
|
||||
// MARK: Verify
|
||||
@@ -1748,6 +1751,12 @@ Tap the + to start adding people.";
|
||||
"key_verification_verify_qr_code_scan_other_code_success_title" = "Code validated!";
|
||||
"key_verification_verify_qr_code_scan_other_code_success_message" = "QR code has been successfully validated.";
|
||||
|
||||
"key_verification_scan_qr_code_title" = "Scan QR code";
|
||||
"key_verification_scan_qr_code_information_other_user" = "Point your camera at the QR code displayed on their device to verify their session";
|
||||
"key_verification_scan_qr_code_information_other_device" = "Point your camera at the QR code displayed on your other device to verify this session";
|
||||
"key_verification_scan_qr_code_information_other_session" = "Point your camera at the QR code displayed on your other device to verify your session";
|
||||
"key_verification_scan_qr_code_information_new_session" = "Point your camera at the QR code displayed on your other device to verify your new session";
|
||||
|
||||
// MARK: Scan confirmation
|
||||
|
||||
// Scanning
|
||||
@@ -2287,6 +2296,9 @@ Tap the + to start adding people.";
|
||||
"room_invites_empty_view_title" = "Nothing new.";
|
||||
"room_invites_empty_view_information" = "This is where your invites appear.";
|
||||
|
||||
"room_waiting_other_participants_title" = "Waiting for users to join %@";
|
||||
"room_waiting_other_participants_message" = "Once invited users have joined %@, you will be able to chat and the room will be end-to-end encrypted";
|
||||
|
||||
// MARK: - Space Selector
|
||||
|
||||
"space_selector_title" = "My spaces";
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
{
|
||||
[mxkImageView vc_setRoomAvatarImageWith:self.avatar
|
||||
roomId:self.roomId
|
||||
displayName:self.displayname
|
||||
displayName:self.displayName
|
||||
mediaManager:self.mxSession.mediaManager];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Copyright 2023 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
|
||||
|
||||
/// An implementation of `MXCryptoV2Feature` which uses `UserDefaults` to persist the enabled status
|
||||
/// of `CryptoSDK`, and which uses feature flags to control rollout availability.
|
||||
///
|
||||
/// The implementation uses both remote and local feature flags to control the availability of `CryptoSDK`.
|
||||
/// Whilst remote is more convenient in that it allows changes to the rollout without new app releases,
|
||||
/// it is not available to all users because it requires data tracking user consent. Remote therefore
|
||||
/// represents the safer, albeit limited rollout strategy, whereas the local feature flags allows eventually
|
||||
/// targetting all users, but each target change requires new app release.
|
||||
///
|
||||
/// Additionally users can manually enable this feature from the settings if they are not already in the
|
||||
/// feature group.
|
||||
@objc class CryptoSDKFeature: NSObject, MXCryptoV2Feature {
|
||||
@objc static let shared = CryptoSDKFeature()
|
||||
|
||||
var isEnabled: Bool {
|
||||
RiotSettings.shared.enableCryptoSDK
|
||||
}
|
||||
|
||||
private static let FeatureName = "ios-crypto-sdk"
|
||||
private let remoteFeature: RemoteFeaturesClientProtocol
|
||||
private let localFeature: PhasedRolloutFeature
|
||||
|
||||
init(remoteFeature: RemoteFeaturesClientProtocol = PostHogAnalyticsClient.shared) {
|
||||
self.remoteFeature = remoteFeature
|
||||
self.localFeature = PhasedRolloutFeature(
|
||||
name: Self.FeatureName,
|
||||
// Local feature is currently set to 0% target, and all availability is fully controlled
|
||||
// by the remote feature. Once the remote is fully rolled out, target for local feature will
|
||||
// be gradually increased.
|
||||
targetPercentage: 0.0
|
||||
)
|
||||
}
|
||||
|
||||
func enable() {
|
||||
RiotSettings.shared.enableCryptoSDK = true
|
||||
Analytics.shared.trackCryptoSDKEnabled()
|
||||
|
||||
MXLog.debug("[CryptoSDKFeature] Crypto SDK enabled")
|
||||
}
|
||||
|
||||
func enableIfAvailable(forUserId userId: String!) {
|
||||
guard !isEnabled else {
|
||||
MXLog.debug("[CryptoSDKFeature] enableIfAvailable: Feature is already enabled")
|
||||
return
|
||||
}
|
||||
|
||||
guard let userId else {
|
||||
MXLog.failure("[CryptoSDKFeature] enableIfAvailable: Missing user id")
|
||||
return
|
||||
}
|
||||
|
||||
guard isFeatureEnabled(userId: userId) else {
|
||||
MXLog.debug("[CryptoSDKFeature] enableIfAvailable: Feature is currently not available for this user")
|
||||
return
|
||||
}
|
||||
|
||||
MXLog.debug("[CryptoSDKFeature] enableIfAvailable: Feature has become available for this user and will be enabled")
|
||||
enable()
|
||||
}
|
||||
|
||||
@objc func canManuallyEnable(forUserId userId: String!) -> Bool {
|
||||
guard let userId else {
|
||||
MXLog.failure("[CryptoSDKFeature] canManuallyEnable: Missing user id")
|
||||
return false
|
||||
}
|
||||
|
||||
// User can manually enable only if not already within the automatic feature group
|
||||
return !isFeatureEnabled(userId: userId)
|
||||
}
|
||||
|
||||
@objc func reset() {
|
||||
RiotSettings.shared.enableCryptoSDK = false
|
||||
MXLog.debug("[CryptoSDKFeature] Crypto SDK disabled")
|
||||
}
|
||||
|
||||
private func isFeatureEnabled(userId: String) -> Bool {
|
||||
remoteFeature.isFeatureEnabled(Self.FeatureName) || localFeature.isEnabled(userId: userId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright 2023 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 CryptoKit
|
||||
|
||||
/// Object encapsulating an experiment with an arbitrary number of variants, and a method to deterministically
|
||||
/// and uniformly assign a variant to user. Variants do not carry any implicit semantics, they are plain numbers
|
||||
/// to be interpreted by the caller of the experiment.
|
||||
struct Experiment {
|
||||
let name: String
|
||||
let variants: UInt
|
||||
|
||||
/// Get the assigned variant from the total number of variants and for a given `userId`
|
||||
///
|
||||
/// This variant is chosen deterministically (the same `userId` and experiment `name` will yield the same variant)
|
||||
/// and uniformly (multiple users are distributed roughly evenly among the variants).
|
||||
func variant(userId: String) -> UInt {
|
||||
// Combine user id with experiment name to avoid identical variant
|
||||
// for the same user in different experiments
|
||||
let data = (userId + name).data(using: .utf8) ?? Data()
|
||||
|
||||
// Get the first 8 bytes and map to decimal number (UInt64 = 8 bytes)
|
||||
let decimal = digest(for: data)
|
||||
.prefix(8)
|
||||
.reduce(0) { $0 << 8 | UInt64($1) }
|
||||
|
||||
// Compress the decimal into a set number of variants using modulo
|
||||
return UInt(decimal % UInt64(variants))
|
||||
}
|
||||
|
||||
private func digest(for data: Data) -> SHA256.Digest {
|
||||
var sha = SHA256()
|
||||
sha.update(data: data)
|
||||
return sha.finalize()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Object enabling a phased rollout of features depending on the `userId` and `targetPercentage`.
|
||||
///
|
||||
/// The feature uses an experiment under the hood with 100 variants representing 100%.
|
||||
/// Each userId is deterministically and uniformly assigned a variant, and depending
|
||||
/// on whether this falls below or above the `targetPercentage` threshold, the feature
|
||||
/// is considered enabled or disabled.
|
||||
struct PhasedRolloutFeature {
|
||||
private let experiment: Experiment
|
||||
private let targetPercentage: Double
|
||||
|
||||
init(name: String, targetPercentage: Double) {
|
||||
self.experiment = .init(
|
||||
name: name,
|
||||
// 100 variants where each variant represents a single percentage
|
||||
variants: 100
|
||||
)
|
||||
self.targetPercentage = targetPercentage
|
||||
}
|
||||
|
||||
func isEnabled(userId: String) -> Bool {
|
||||
// Get a bucket number between 0-99
|
||||
let variant = experiment.variant(userId: userId)
|
||||
// Convert to a percentage
|
||||
let percentage = Double(variant) / 100
|
||||
// Consider enabled if falls below rollout target
|
||||
return percentage < targetPercentage
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Copyright 2023 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
|
||||
|
||||
/// A protocol representing a remote features client
|
||||
protocol RemoteFeaturesClientProtocol {
|
||||
func isFeatureEnabled(_ feature: String) -> Bool
|
||||
}
|
||||
@@ -1871,6 +1871,14 @@ public class VectorL10n: NSObject {
|
||||
public static var deviceVerificationSelfVerifyAlertValidateAction: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_self_verify_alert_validate_action")
|
||||
}
|
||||
/// You need to verify this session in order to read your secure message history.\n\nOpen Element on one of your other devices and follow the instructions.
|
||||
public static var deviceVerificationSelfVerifyOpenOnOtherDeviceInformation: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_self_verify_open_on_other_device_information")
|
||||
}
|
||||
/// Open %@ on your other device
|
||||
public static func deviceVerificationSelfVerifyOpenOnOtherDeviceTitle(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "device_verification_self_verify_open_on_other_device_title", p1)
|
||||
}
|
||||
/// Use this session to verify your new one, granting it access to encrypted messages.
|
||||
public static var deviceVerificationSelfVerifyStartInformation: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_self_verify_start_information")
|
||||
@@ -1895,9 +1903,9 @@ public class VectorL10n: NSObject {
|
||||
public static var deviceVerificationSelfVerifyWaitNewSignInTitle: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_self_verify_wait_new_sign_in_title")
|
||||
}
|
||||
/// If you can't access an existing session
|
||||
public static var deviceVerificationSelfVerifyWaitRecoverSecretsAdditionalInformation: String {
|
||||
return VectorL10n.tr("Vector", "device_verification_self_verify_wait_recover_secrets_additional_information")
|
||||
/// Can't access an existing %@ session?
|
||||
public static func deviceVerificationSelfVerifyWaitRecoverSecretsAdditionalHelp(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "device_verification_self_verify_wait_recover_secrets_additional_help", p1)
|
||||
}
|
||||
/// Checking for other verification capabilities ...
|
||||
public static var deviceVerificationSelfVerifyWaitRecoverSecretsCheckingAvailability: String {
|
||||
@@ -2987,6 +2995,26 @@ public class VectorL10n: NSObject {
|
||||
public static func keyVerificationScanConfirmationScanningUserWaitingOther(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "key_verification_scan_confirmation_scanning_user_waiting_other", p1)
|
||||
}
|
||||
/// Point your camera at the QR code displayed on your other device to verify your new session
|
||||
public static var keyVerificationScanQrCodeInformationNewSession: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_scan_qr_code_information_new_session")
|
||||
}
|
||||
/// Point your camera at the QR code displayed on your other device to verify this session
|
||||
public static var keyVerificationScanQrCodeInformationOtherDevice: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_scan_qr_code_information_other_device")
|
||||
}
|
||||
/// Point your camera at the QR code displayed on your other device to verify your session
|
||||
public static var keyVerificationScanQrCodeInformationOtherSession: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_scan_qr_code_information_other_session")
|
||||
}
|
||||
/// Point your camera at the QR code displayed on their device to verify their session
|
||||
public static var keyVerificationScanQrCodeInformationOtherUser: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_scan_qr_code_information_other_user")
|
||||
}
|
||||
/// Scan QR code
|
||||
public static var keyVerificationScanQrCodeTitle: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_scan_qr_code_title")
|
||||
}
|
||||
/// Other users may not trust it.
|
||||
public static var keyVerificationSelfVerifyCurrentSessionAlertMessage: String {
|
||||
return VectorL10n.tr("Vector", "key_verification_self_verify_current_session_alert_message")
|
||||
@@ -5239,6 +5267,10 @@ public class VectorL10n: NSObject {
|
||||
public static var roomCreationNameTitle: String {
|
||||
return VectorL10n.tr("Vector", "room_creation_name_title")
|
||||
}
|
||||
/// You can only invite one email at a time
|
||||
public static var roomCreationOnlyOneEmailInvite: String {
|
||||
return VectorL10n.tr("Vector", "room_creation_only_one_email_invite")
|
||||
}
|
||||
/// (e.g. @bob:homeserver1; @john:homeserver2...)
|
||||
public static var roomCreationParticipantsPlaceholder: String {
|
||||
return VectorL10n.tr("Vector", "room_creation_participants_placeholder")
|
||||
@@ -6595,6 +6627,14 @@ public class VectorL10n: NSObject {
|
||||
public static var roomUnsentMessagesUnknownDevicesNotification: String {
|
||||
return VectorL10n.tr("Vector", "room_unsent_messages_unknown_devices_notification")
|
||||
}
|
||||
/// Once invited users have joined %@, you will be able to chat and the room will be end-to-end encrypted
|
||||
public static func roomWaitingOtherParticipantsMessage(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "room_waiting_other_participants_message", p1)
|
||||
}
|
||||
/// Waiting for users to join %@
|
||||
public static func roomWaitingOtherParticipantsTitle(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "room_waiting_other_participants_title", p1)
|
||||
}
|
||||
/// 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.
|
||||
public static var roomWarningAboutEncryption: String {
|
||||
return VectorL10n.tr("Vector", "room_warning_about_encryption")
|
||||
|
||||
@@ -208,7 +208,7 @@ class CallPresenter: NSObject {
|
||||
if error == nil {
|
||||
JMCallKitProxy.reportCallUpdate(with: newUUID,
|
||||
handle: roomId,
|
||||
displayName: room.summary.displayname,
|
||||
displayName: room.summary.displayName,
|
||||
hasVideo: true)
|
||||
JMCallKitProxy.reportOutgoingCall(with: newUUID, connectedAt: nil)
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
[self.roomDataSource finalizeInitialization];
|
||||
self.roomDataSource.markTimelineInitialEvent = YES;
|
||||
|
||||
self->_roomName = peekingRoom.summary.displayname;
|
||||
self->_roomName = peekingRoom.summary.displayName;
|
||||
self->_roomAvatarUrl = peekingRoom.summary.avatar;
|
||||
|
||||
self->_roomTopic = [MXTools stripNewlineCharacters:peekingRoom.summary.topic];;
|
||||
|
||||
@@ -24,17 +24,8 @@ import AnalyticsEvents
|
||||
/// non-fatal issues and performance. `Analytics` class serves as a façade
|
||||
/// to all these use cases.
|
||||
///
|
||||
/// ## Creating Analytics Events
|
||||
///
|
||||
/// Events are managed in a shared repo for all Element clients https://github.com/matrix-org/matrix-analytics-events
|
||||
/// To add a new event create a PR to that repo with the new/updated schema. Element's Podfile has
|
||||
/// a local version of the pod (commented out) for development purposes.
|
||||
/// Once merged into `main`, follow the steps below to integrate the changes into the project:
|
||||
/// 1. Check if `main` contains any source breaking changes to the events. If so, please
|
||||
/// wait until you are ready to merge your work into element-ios.
|
||||
/// 2. Merge `main` into the `release/swift` branch.
|
||||
/// 3. Run `bundle exec pod update AnalyticsEvents` to update the pod.
|
||||
/// 4. Make sure to commit `Podfile.lock` with the new commit hash.
|
||||
/// Events are managed in a shared repo for all Element clients
|
||||
/// https://github.com/matrix-org/matrix-analytics-events and integrated via SwiftPM
|
||||
///
|
||||
@objcMembers class Analytics: NSObject {
|
||||
|
||||
@@ -44,7 +35,7 @@ import AnalyticsEvents
|
||||
static let shared = Analytics()
|
||||
|
||||
/// The analytics client to send events with.
|
||||
private var client: AnalyticsClientProtocol = PostHogAnalyticsClient()
|
||||
private var client: AnalyticsClientProtocol = PostHogAnalyticsClient.shared
|
||||
|
||||
/// The monitoring client to track crashes, issues and performance
|
||||
private var monitoringClient = SentryMonitoringClient()
|
||||
@@ -230,10 +221,10 @@ extension Analytics {
|
||||
///
|
||||
/// Only non-nil properties will be updated when calling this method.
|
||||
func updateUserProperties(ftueUseCase: UserSessionProperties.UseCase? = nil, numFavouriteRooms: Int? = nil, numSpaces: Int? = nil, allChatsActiveFilter: UserSessionProperties.AllChatsActiveFilter? = nil) {
|
||||
let userProperties = AnalyticsEvent.UserProperties(ftueUseCaseSelection: ftueUseCase?.analyticsName,
|
||||
let userProperties = AnalyticsEvent.UserProperties(allChatsActiveFilter: allChatsActiveFilter?.analyticsName,
|
||||
ftueUseCaseSelection: ftueUseCase?.analyticsName,
|
||||
numFavouriteRooms: numFavouriteRooms,
|
||||
numSpaces: numSpaces,
|
||||
allChatsActiveFilter: allChatsActiveFilter?.analyticsName)
|
||||
numSpaces: numSpaces)
|
||||
client.updateUserProperties(userProperties)
|
||||
}
|
||||
|
||||
@@ -281,7 +272,12 @@ extension Analytics {
|
||||
/// - reason: The error that occurred.
|
||||
/// - context: Additional context of the error that occured
|
||||
func trackE2EEError(_ reason: DecryptionFailureReason, context: String) {
|
||||
let event = AnalyticsEvent.Error(context: context, domain: .E2EE, name: reason.errorName)
|
||||
let event = AnalyticsEvent.Error(
|
||||
context: context,
|
||||
cryptoModule: MXSDKOptions.sharedInstance().enableCryptoSDK ? .Rust : .Native,
|
||||
domain: .E2EE,
|
||||
name: reason.errorName
|
||||
)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
@@ -359,7 +355,7 @@ extension Analytics: MXAnalyticsDelegate {
|
||||
|
||||
func trackCallError(with reason: __MXCallHangupReason, video isVideo: Bool, numberOfParticipants: Int, incoming isIncoming: Bool) {
|
||||
let callEvent = AnalyticsEvent.CallError(isVideo: isVideo, numParticipants: numberOfParticipants, placed: !isIncoming)
|
||||
let event = AnalyticsEvent.Error(context: nil, domain: .VOIP, name: reason.errorName)
|
||||
let event = AnalyticsEvent.Error(context: nil, cryptoModule: nil, domain: .VOIP, name: reason.errorName)
|
||||
capture(event: callEvent)
|
||||
capture(event: event)
|
||||
}
|
||||
|
||||
@@ -1,35 +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 Foundation
|
||||
import AnalyticsEvents
|
||||
|
||||
extension AnalyticsEvent.UserProperties {
|
||||
|
||||
// Initializer for Element. Strips all Web properties.
|
||||
public init(ftueUseCaseSelection: FtueUseCaseSelection?, numFavouriteRooms: Int?, numSpaces: Int?, allChatsActiveFilter: AllChatsActiveFilter?) {
|
||||
self.init(WebMetaSpaceFavouritesEnabled: nil,
|
||||
WebMetaSpaceHomeAllRooms: nil,
|
||||
WebMetaSpaceHomeEnabled: nil,
|
||||
WebMetaSpaceOrphansEnabled: nil,
|
||||
WebMetaSpacePeopleEnabled: nil,
|
||||
allChatsActiveFilter: allChatsActiveFilter,
|
||||
ftueUseCaseSelection: ftueUseCaseSelection,
|
||||
numFavouriteRooms: numFavouriteRooms,
|
||||
numSpaces: numSpaces)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -25,6 +25,8 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
|
||||
/// Any user properties to be included with the next captured event.
|
||||
private(set) var pendingUserProperties: AnalyticsEvent.UserProperties?
|
||||
|
||||
static let shared = PostHogAnalyticsClient()
|
||||
|
||||
var isRunning: Bool { postHog?.enabled ?? false }
|
||||
|
||||
func start() {
|
||||
@@ -79,10 +81,10 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
|
||||
}
|
||||
|
||||
// Merge the updated user properties with the existing ones
|
||||
self.pendingUserProperties = AnalyticsEvent.UserProperties(ftueUseCaseSelection: userProperties.ftueUseCaseSelection ?? pendingUserProperties.ftueUseCaseSelection,
|
||||
self.pendingUserProperties = AnalyticsEvent.UserProperties(allChatsActiveFilter: userProperties.allChatsActiveFilter ?? pendingUserProperties.allChatsActiveFilter,
|
||||
ftueUseCaseSelection: userProperties.ftueUseCaseSelection ?? pendingUserProperties.ftueUseCaseSelection,
|
||||
numFavouriteRooms: userProperties.numFavouriteRooms ?? pendingUserProperties.numFavouriteRooms,
|
||||
numSpaces: userProperties.numSpaces ?? pendingUserProperties.numSpaces,
|
||||
allChatsActiveFilter: userProperties.allChatsActiveFilter ?? pendingUserProperties.allChatsActiveFilter)
|
||||
numSpaces: userProperties.numSpaces ?? pendingUserProperties.numSpaces)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -102,3 +104,9 @@ class PostHogAnalyticsClient: AnalyticsClientProtocol {
|
||||
return properties
|
||||
}
|
||||
}
|
||||
|
||||
extension PostHogAnalyticsClient: RemoteFeaturesClientProtocol {
|
||||
func isFeatureEnabled(_ feature: String) -> Bool {
|
||||
postHog?.isFeatureEnabled(feature) == true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2184,7 +2184,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
|
||||
[self clearCache];
|
||||
|
||||
// Reset Crypto SDK configuration (labs flag for which crypto module to use)
|
||||
[CryptoSDKConfiguration.shared disable];
|
||||
[CryptoSDKFeature.shared reset];
|
||||
|
||||
// Reset key backup banner preferences
|
||||
[SecureBackupBannerPreferences.shared reset];
|
||||
|
||||
@@ -484,7 +484,7 @@ CallAudioRouteMenuViewDelegate>
|
||||
else if (self.mxCall.room)
|
||||
{
|
||||
return [AvatarGenerator generateAvatarForMatrixItem:self.mxCall.room.roomId
|
||||
withDisplayName:self.mxCall.room.summary.displayname
|
||||
withDisplayName:self.mxCall.room.summary.displayName
|
||||
size:self.callerImageViewWidthConstraint.constant
|
||||
andFontSize:fontSize];
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ class CallPiPView: UIView {
|
||||
andFontSize: fontSize)
|
||||
} else if let room = call?.room {
|
||||
return AvatarGenerator.generateAvatar(forMatrixItem: room.roomId,
|
||||
withDisplayName: room.summary.displayname,
|
||||
withDisplayName: room.summary.displayName,
|
||||
size: imageView.bounds.width,
|
||||
andFontSize: fontSize)
|
||||
}
|
||||
|
||||
@@ -1103,7 +1103,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
|
||||
RecentEmptySectionTableViewCell *tableViewCell = [tableView dequeueReusableCellWithIdentifier:[RecentEmptySpaceSectionTableViewCell defaultReuseIdentifier]];
|
||||
|
||||
tableViewCell.iconView.image = [ThemeService.shared isCurrentThemeDark] ? AssetImages.allChatsEmptySpaceArtworkDark.image : AssetImages.allChatsEmptySpaceArtwork.image;
|
||||
tableViewCell.titleLabel.text = [VectorL10n allChatsEmptyViewTitle: self.currentSpace.summary.displayname];
|
||||
tableViewCell.titleLabel.text = [VectorL10n allChatsEmptyViewTitle: self.currentSpace.summary.displayName];
|
||||
tableViewCell.messageLabel.text = VectorL10n.allChatsEmptySpaceInformation;
|
||||
|
||||
return tableViewCell;
|
||||
@@ -1670,7 +1670,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
|
||||
|
||||
NSString* tagOrder = [room.mxSession tagOrderToBeAtIndex:newPath.row from:oldPos withTag:dstRoomTag];
|
||||
|
||||
MXLogDebug(@"[RecentsDataSource] Update the room %@ [%@] tag from %@ to %@ with tag order %@", room.roomId, room.summary.displayname, oldRoomTag, dstRoomTag, tagOrder);
|
||||
MXLogDebug(@"[RecentsDataSource] Update the room %@ [%@] tag from %@ to %@ with tag order %@", room.roomId, room.summary.displayName, oldRoomTag, dstRoomTag, tagOrder);
|
||||
|
||||
[room replaceTag:oldRoomTag
|
||||
byTag:dstRoomTag
|
||||
|
||||
@@ -95,7 +95,7 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol {
|
||||
} else if i % 11 == 0 {
|
||||
room.dataTypes = .serverNotice
|
||||
}
|
||||
room.displayname = "Room \(i+1)"
|
||||
room.displayName = "Room \(i+1)"
|
||||
if let event = MXEvent(fromJSON: [
|
||||
"event_id": MXTools.generateTransactionId() as Any,
|
||||
"room_id": room.roomId,
|
||||
|
||||
@@ -26,7 +26,7 @@ public class MockRoomSummary: NSObject, MXRoomSummaryProtocol {
|
||||
|
||||
public var avatar: String?
|
||||
|
||||
public var displayname: String?
|
||||
public var displayName: String?
|
||||
|
||||
public var topic: String?
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
{
|
||||
[room.summary setRoomAvatarImageIn:self.avatarImageView];
|
||||
|
||||
self.titleLabel.text = room.summary.displayname;
|
||||
self.titleLabel.text = room.summary.displayName;
|
||||
}
|
||||
|
||||
+ (CGFloat)cellHeight
|
||||
|
||||
@@ -40,7 +40,7 @@ class AllChatsEditActionProvider {
|
||||
private var rootSpaceCount: Int = 0
|
||||
private var parentSpace: MXSpace? {
|
||||
didSet {
|
||||
parentName = parentSpace?.summary?.displayname ?? VectorL10n.spaceTag
|
||||
parentName = parentSpace?.summary?.displayName ?? VectorL10n.spaceTag
|
||||
}
|
||||
}
|
||||
private var parentName: String = VectorL10n.spaceTag
|
||||
|
||||
@@ -39,7 +39,7 @@ class AllChatsSpaceActionProvider {
|
||||
|
||||
private var currentSpace: MXSpace? {
|
||||
didSet {
|
||||
spaceName = currentSpace?.summary?.displayname ?? VectorL10n.spaceTag
|
||||
spaceName = currentSpace?.summary?.displayName ?? VectorL10n.spaceTag
|
||||
}
|
||||
}
|
||||
private var spaceName: String = VectorL10n.spaceTag
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
MXRoom *room = [searchDataSource.mxSession roomWithRoomId:roomId];
|
||||
if (room)
|
||||
{
|
||||
roomDisplayName = room.summary.displayname;
|
||||
roomDisplayName = room.summary.displayName;
|
||||
if (!roomDisplayName.length)
|
||||
{
|
||||
roomDisplayName = [VectorL10n roomDisplaynameEmptyRoom];
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
MXRoom* room = [bubbleData.mxSession roomWithRoomId:bubbleData.roomId];
|
||||
if (room)
|
||||
{
|
||||
self.roomNameLabel.text = room.summary.displayname;
|
||||
self.roomNameLabel.text = room.summary.displayName;
|
||||
if (!self.roomNameLabel.text.length)
|
||||
{
|
||||
self.roomNameLabel.text = [VectorL10n roomDisplaynameEmptyRoom];
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
MXRoom* room = [bubbleData.mxSession roomWithRoomId:bubbleData.roomId];
|
||||
if (room)
|
||||
{
|
||||
self.roomNameLabel.text = room.summary.displayname;
|
||||
self.roomNameLabel.text = room.summary.displayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -415,7 +415,7 @@ class AllChatsViewController: HomeViewController {
|
||||
let title: String
|
||||
let informationText: String
|
||||
if let currentSpace = self.dataSource?.currentSpace {
|
||||
title = VectorL10n.allChatsEmptyViewTitle(currentSpace.summary?.displayname ?? VectorL10n.spaceTag)
|
||||
title = VectorL10n.allChatsEmptyViewTitle(currentSpace.summary?.displayName ?? VectorL10n.spaceTag)
|
||||
informationText = VectorL10n.allChatsEmptySpaceInformation
|
||||
} else {
|
||||
let myUser = mainSession.myUser
|
||||
@@ -495,7 +495,7 @@ class AllChatsViewController: HomeViewController {
|
||||
|
||||
private func updateUI() {
|
||||
let currentSpace = self.dataSource?.currentSpace
|
||||
self.title = currentSpace?.summary?.displayname ?? VectorL10n.allChatsTitle
|
||||
self.title = currentSpace?.summary?.displayName ?? VectorL10n.allChatsTitle
|
||||
|
||||
setupEditOptions()
|
||||
updateToolbar(with: editActionProvider.updateMenu(with: mainSession, parentSpace: currentSpace, completion: { [weak self] menu in
|
||||
@@ -638,7 +638,7 @@ class AllChatsViewController: HomeViewController {
|
||||
return
|
||||
}
|
||||
|
||||
let name = spaceSummary.displayname ?? VectorL10n.spaceTag
|
||||
let name = spaceSummary.displayName ?? VectorL10n.spaceTag
|
||||
|
||||
let selectionHeader = MatrixItemChooserSelectionHeader(title: VectorL10n.leaveSpaceSelectionTitle,
|
||||
selectAllTitle: VectorL10n.leaveSpaceSelectionAllRooms,
|
||||
|
||||
@@ -272,7 +272,7 @@ static NSString * _Nonnull kJitsiFeatureFlagChatEnabled = @"chat.enabled";
|
||||
builder.room = conferenceId;
|
||||
builder.videoMuted = !self.startWithVideo;
|
||||
|
||||
builder.subject = roomSummary.displayname;
|
||||
builder.subject = roomSummary.displayName;
|
||||
builder.userInfo = [[JitsiMeetUserInfo alloc] initWithDisplayName:userDisplayName
|
||||
andEmail:nil
|
||||
andAvatar:avatarUrl];
|
||||
|
||||
@@ -252,6 +252,9 @@ final class KeyVerificationCoordinator: KeyVerificationCoordinatorType {
|
||||
}
|
||||
|
||||
private func showVerifyBySAS(transaction: MXSASTransaction, animated: Bool) {
|
||||
if navigationRouter.modules.last is KeyVerificationVerifyBySASCoordinator {
|
||||
return
|
||||
}
|
||||
let coordinator = KeyVerificationVerifyBySASCoordinator(session: self.session, transaction: transaction, verificationKind: self.verificationKind)
|
||||
coordinator.delegate = self
|
||||
coordinator.start()
|
||||
|
||||
+15
-17
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -13,38 +11,38 @@
|
||||
<!--Key Verification Scan Confirmation View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="KeyVerificationScanConfirmationViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="KeyVerificationScanConfirmationViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="852"/>
|
||||
<rect key="frame" x="0.0" y="48" width="414" height="848"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="36d-db-nP0">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="376.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="392"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="A5W-J0-Sfd">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="376.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="392"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Almost there!" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FO8-rD-Y21">
|
||||
<rect key="frame" x="20" y="40" width="374" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<rect key="frame" x="20" y="40" width="374" height="33.5"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="key_verification_success_shield" translatesAutoresizingMaskIntoConstraints="NO" id="Uad-3w-cQ4">
|
||||
<rect key="frame" x="147" y="88" width="120" height="120"/>
|
||||
<rect key="frame" x="147" y="103.5" width="120" height="120"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="120" id="3RB-i8-S1k"/>
|
||||
<constraint firstAttribute="width" secondItem="Uad-3w-cQ4" secondAttribute="height" multiplier="1:1" id="vrj-Sp-Wpu"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="t0L-ns-MGv">
|
||||
<rect key="frame" x="20" y="238" width="374" height="118.5"/>
|
||||
<rect key="frame" x="20" y="253.5" width="374" height="118.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Waiting..." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hwq-X6-Gl2">
|
||||
<rect key="frame" x="151.5" y="0.0" width="71.5" height="20.5"/>
|
||||
<rect key="frame" x="151.5" y="0.0" width="71" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.3803921569" green="0.43921568630000002" blue="0.5450980392" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -61,7 +59,7 @@
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="8Ou-Xh-0rC">
|
||||
<rect key="frame" x="0.0" y="48" width="374" height="40"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kBt-2D-25V" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kBt-2D-25V" customClass="RoundedButton" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="182" height="40"/>
|
||||
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
|
||||
<state key="normal" title="No">
|
||||
@@ -71,7 +69,7 @@
|
||||
<action selector="rejectButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="qCV-Oh-YAG"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="03A-fe-h3s" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="03A-fe-h3s" customClass="RoundedButton" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="192" y="0.0" width="182" height="40"/>
|
||||
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
|
||||
<state key="normal" title="Yes">
|
||||
@@ -142,6 +140,7 @@
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="9U2-KL-ZVA" secondAttribute="bottom" id="7Cb-nY-CsO"/>
|
||||
@@ -149,7 +148,6 @@
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="9U2-KL-ZVA" secondAttribute="trailing" id="sbD-ek-vGJ"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="wTB-V6-IHV"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="confirmButton" destination="03A-fe-h3s" id="lDk-ec-qrl"/>
|
||||
|
||||
-1
@@ -173,7 +173,6 @@ final class KeyVerificationScanConfirmationViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
self.title = viewData.verificationKind.verificationTitle
|
||||
self.titleLabel.text = title
|
||||
self.waitingLabel.text = waitingInfo
|
||||
self.scannedInformationLabel.text = scannedInfo
|
||||
|
||||
+14
-16
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="dBQ-CG-VDL">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="dBQ-CG-VDL">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -13,41 +11,41 @@
|
||||
<!--Key Verification Verified View Controller-->
|
||||
<scene sceneID="EyC-m5-6uM">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="dBQ-CG-VDL" customClass="KeyVerificationVerifiedViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="dBQ-CG-VDL" customClass="KeyVerificationVerifiedViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ht4-fu-3rS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jOh-c7-uod">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="c4q-B8-hPy">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="345"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="358"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fNE-v3-2lx">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="345"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="358"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Verified!" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1Nw-CZ-lKr">
|
||||
<rect key="frame" x="20" y="60" width="335" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<rect key="frame" x="20" y="60" width="335" height="33.5"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="You've successfully verified this device." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Sql-oR-dMk">
|
||||
<rect key="frame" x="20" y="103" width="335" height="18"/>
|
||||
<rect key="frame" x="20" y="116" width="335" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="key_verification_success_shield" translatesAutoresizingMaskIntoConstraints="NO" id="ie2-LW-ek2">
|
||||
<rect key="frame" x="127.5" y="141" width="120" height="120"/>
|
||||
<rect key="frame" x="127.5" y="154" width="120" height="120"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="ie2-LW-ek2" secondAttribute="height" multiplier="1:1" id="Re6-8V-peU"/>
|
||||
<constraint firstAttribute="width" constant="120" id="qg0-4Z-ffM"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4i8-Se-1Ae" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="281" width="335" height="44"/>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="4i8-Se-1Ae" customClass="RoundedButton" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="294" width="335" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="XNP-zj-OmI"/>
|
||||
</constraints>
|
||||
@@ -98,6 +96,7 @@
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="6ex-OQ-2sZ"/>
|
||||
<color key="backgroundColor" red="0.94509803921568625" green="0.96078431372549022" blue="0.97254901960784312" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="jOh-c7-uod" secondAttribute="trailing" id="7K8-MG-xLT"/>
|
||||
@@ -107,7 +106,6 @@
|
||||
<constraint firstItem="jOh-c7-uod" firstAttribute="trailing" secondItem="6ex-OQ-2sZ" secondAttribute="trailing" id="jVN-Fr-MKN"/>
|
||||
<constraint firstItem="jOh-c7-uod" firstAttribute="top" secondItem="6ex-OQ-2sZ" secondAttribute="top" id="s7K-jf-P1z"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="6ex-OQ-2sZ"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="doneButton" destination="4i8-Se-1Ae" id="K4a-4m-cU2"/>
|
||||
|
||||
-1
@@ -97,7 +97,6 @@ final class KeyVerificationVerifiedViewController: UIViewController {
|
||||
informationText = VectorL10n.keyVerificationVerifiedUserInformation
|
||||
}
|
||||
|
||||
self.title = self.verificationKind.verificationTitle
|
||||
self.titleLabel.text = bodyTitle
|
||||
self.informationLabel.text = informationText
|
||||
|
||||
|
||||
+20
-25
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V8j-Lb-PgC">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -13,37 +11,34 @@
|
||||
<!--Key Verification Verify BySAS View Controller-->
|
||||
<scene sceneID="mt5-wz-YKA">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="KeyVerificationVerifyBySASViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="V8j-Lb-PgC" customClass="KeyVerificationVerifyBySASViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="EL9-GA-lwo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9U2-KL-ZVA">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="e7g-um-WO4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="502"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="490.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="voD-3Q-ryt">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="502"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="490.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Compare emoji" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="he8-pl-xE9">
|
||||
<rect key="frame" x="20" y="35" width="335" height="61"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="61" id="Nam-ca-50k"/>
|
||||
</constraints>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="17"/>
|
||||
<rect key="frame" x="20" y="30" width="335" height="33.5"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="For maximum security, we recommend you do this in person or use another trusted means of communication." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bxI-mu-qng">
|
||||
<rect key="frame" x="20" y="115" width="335" height="54"/>
|
||||
<rect key="frame" x="20" y="103.5" width="335" height="54"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="3152 3307 8179" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="RD6-ue-X5c">
|
||||
<rect key="frame" x="37.5" y="264.5" width="300" height="29"/>
|
||||
<rect key="frame" x="37.5" y="253" width="300" height="29"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="300" id="6Q5-M2-ifj"/>
|
||||
</constraints>
|
||||
@@ -52,7 +47,7 @@
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="34i-3X-YvQ">
|
||||
<rect key="frame" x="27.5" y="189" width="320" height="180"/>
|
||||
<rect key="frame" x="27.5" y="177.5" width="320" height="180"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="180" id="kpT-ty-CDI"/>
|
||||
@@ -65,7 +60,7 @@
|
||||
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||
</collectionViewFlowLayout>
|
||||
<cells>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="VerifyEmojiCollectionViewCell" id="iG2-Pq-pYr" customClass="VerifyEmojiCollectionViewCell" customModule="Riot" customModuleProvider="target">
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="VerifyEmojiCollectionViewCell" id="iG2-Pq-pYr" customClass="VerifyEmojiCollectionViewCell" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
|
||||
@@ -105,9 +100,9 @@
|
||||
</connections>
|
||||
</collectionView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" alignment="center" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="6xJ-uL-C85">
|
||||
<rect key="frame" x="20" y="389" width="335" height="44"/>
|
||||
<rect key="frame" x="20" y="377.5" width="335" height="44"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zsb-KY-oCN" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zsb-KY-oCN" customClass="RoundedButton" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="162.5" height="44"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
|
||||
@@ -118,7 +113,7 @@
|
||||
<action selector="cancelButtonAction:" destination="V8j-Lb-PgC" eventType="touchUpInside" id="1NK-nX-rMW"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FvW-07-g2Q" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FvW-07-g2Q" customClass="RoundedButton" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="172.5" y="0.0" width="162.5" height="44"/>
|
||||
<inset key="contentEdgeInsets" minX="10" minY="0.0" maxX="10" maxY="0.0"/>
|
||||
<state key="normal" title="They match">
|
||||
@@ -146,7 +141,7 @@
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="For ultimate security, use another trusted means of communication or do this in person." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gDW-77-nwN">
|
||||
<rect key="frame" x="20" y="453" width="335" height="29"/>
|
||||
<rect key="frame" x="20" y="441.5" width="335" height="29"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -162,7 +157,7 @@
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="bxI-mu-qng" secondAttribute="leading" id="Q9n-7p-gHl"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="QSg-yz-aaB"/>
|
||||
<constraint firstItem="6yX-xD-4X5" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="Rg4-jV-Nht"/>
|
||||
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="he8-pl-xE9" secondAttribute="top" constant="80" id="UL4-x0-oFT"/>
|
||||
<constraint firstItem="bxI-mu-qng" firstAttribute="top" secondItem="he8-pl-xE9" secondAttribute="bottom" constant="40" id="UL4-x0-oFT"/>
|
||||
<constraint firstItem="gDW-77-nwN" firstAttribute="leading" secondItem="voD-3Q-ryt" secondAttribute="leading" constant="20" id="YAC-Wy-mPL"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="ZP8-mV-RBh"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="trailing" secondItem="bxI-mu-qng" secondAttribute="trailing" id="Zeg-U8-uis"/>
|
||||
@@ -171,7 +166,7 @@
|
||||
<constraint firstItem="RD6-ue-X5c" firstAttribute="centerX" secondItem="34i-3X-YvQ" secondAttribute="centerX" id="lBM-R8-C6b"/>
|
||||
<constraint firstItem="34i-3X-YvQ" firstAttribute="top" secondItem="bxI-mu-qng" secondAttribute="bottom" constant="20" id="r7A-9g-Mmb"/>
|
||||
<constraint firstAttribute="bottom" secondItem="gDW-77-nwN" secondAttribute="bottom" constant="20" id="rpv-yg-1jT"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="35" id="s3k-Io-834"/>
|
||||
<constraint firstItem="he8-pl-xE9" firstAttribute="top" secondItem="voD-3Q-ryt" secondAttribute="top" constant="30" id="s3k-Io-834"/>
|
||||
<constraint firstItem="gDW-77-nwN" firstAttribute="top" secondItem="6xJ-uL-C85" secondAttribute="bottom" constant="20" id="wL6-tr-pO2"/>
|
||||
<constraint firstItem="34i-3X-YvQ" firstAttribute="centerX" secondItem="voD-3Q-ryt" secondAttribute="centerX" id="ydb-Fs-K0a"/>
|
||||
</constraints>
|
||||
@@ -201,6 +196,7 @@
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="9U2-KL-ZVA" secondAttribute="bottom" id="7Cb-nY-CsO"/>
|
||||
@@ -208,7 +204,6 @@
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="trailing" secondItem="9U2-KL-ZVA" secondAttribute="trailing" id="sbD-ek-vGJ"/>
|
||||
<constraint firstItem="bFg-jh-JZB" firstAttribute="top" secondItem="9U2-KL-ZVA" secondAttribute="top" id="wTB-V6-IHV"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="bFg-jh-JZB"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="additionalInformationLabel" destination="gDW-77-nwN" id="leX-Mb-wlj"/>
|
||||
|
||||
-1
@@ -145,7 +145,6 @@ final class KeyVerificationVerifyBySASViewController: UIViewController {
|
||||
adviceText = VectorL10n.deviceVerificationSecurityAdviceNumber
|
||||
}
|
||||
|
||||
self.title = self.viewModel.verificationKind.verificationTitle
|
||||
self.titleLabel.text = instructionText
|
||||
self.informationLabel.text = adviceText
|
||||
self.waitingPartnerLabel.text = VectorL10n.deviceVerificationVerifyWaitPartner
|
||||
|
||||
+106
-33
@@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="F9j-7h-dzQ">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="F9j-7h-dzQ">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -13,33 +11,27 @@
|
||||
<!--Key Verification Verify By Scanning View Controller-->
|
||||
<scene sceneID="aq0-GT-wx2">
|
||||
<objects>
|
||||
<viewController id="F9j-7h-dzQ" customClass="KeyVerificationVerifyByScanningViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="F9j-7h-dzQ" customClass="KeyVerificationVerifyByScanningViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="WNg-GS-gGF">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="edw-lO-NVl">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fyB-h5-5v2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="443"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="498.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="D7P-C8-cqw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="113"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="168.5"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="lPc-YT-wnY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="113"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="168.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="J1F-ba-sZ7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="52"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="254" text="Verify by scanning" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VsP-5V-z35">
|
||||
<rect key="frame" x="20" y="20" width="288" height="30"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="20"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1PP-lU-Ags" customClass="CloseButton" customModule="Riot" customModuleProvider="target">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1PP-lU-Ags" customClass="CloseButton" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="318" y="13" width="44" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="44" id="Hfb-fa-cN0"/>
|
||||
@@ -54,17 +46,32 @@
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="VsP-5V-z35" secondAttribute="bottom" id="A4U-o4-Q6E"/>
|
||||
<constraint firstItem="VsP-5V-z35" firstAttribute="centerY" secondItem="1PP-lU-Ags" secondAttribute="centerY" id="F8E-af-0ee"/>
|
||||
<constraint firstItem="VsP-5V-z35" firstAttribute="leading" secondItem="J1F-ba-sZ7" secondAttribute="leading" constant="20" id="gIh-y4-VEc"/>
|
||||
<constraint firstAttribute="height" priority="250" id="guf-gx-dH4"/>
|
||||
<constraint firstItem="1PP-lU-Ags" firstAttribute="leading" secondItem="VsP-5V-z35" secondAttribute="trailing" constant="10" id="lfn-WB-Ilq"/>
|
||||
<constraint firstItem="1PP-lU-Ags" firstAttribute="top" secondItem="J1F-ba-sZ7" secondAttribute="top" constant="13" id="swy-iI-xCv"/>
|
||||
<constraint firstAttribute="trailing" secondItem="1PP-lU-Ags" secondAttribute="trailing" constant="13" id="xNP-5O-bnD"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JWN-4v-RUi">
|
||||
<rect key="frame" x="0.0" y="52" width="375" height="53.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="253" verticalCompressionResistancePriority="751" text="Scan QR Code" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Vv-12-J2A">
|
||||
<rect key="frame" x="20" y="20" width="335" height="33.5"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="4Vv-12-J2A" firstAttribute="leading" secondItem="JWN-4v-RUi" secondAttribute="leading" constant="20" id="4Bf-hJ-6wD"/>
|
||||
<constraint firstAttribute="height" priority="750" id="Elw-li-szT"/>
|
||||
<constraint firstAttribute="bottom" secondItem="4Vv-12-J2A" secondAttribute="bottom" id="j1X-c8-tCi"/>
|
||||
<constraint firstAttribute="trailing" secondItem="4Vv-12-J2A" secondAttribute="trailing" constant="20" id="sAb-CX-1dL"/>
|
||||
<constraint firstItem="4Vv-12-J2A" firstAttribute="top" secondItem="JWN-4v-RUi" secondAttribute="top" constant="20" id="wPc-io-mBg"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SLA-sa-fBw">
|
||||
<rect key="frame" x="0.0" y="50" width="375" height="63"/>
|
||||
<rect key="frame" x="0.0" y="105.5" width="375" height="63"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="253" verticalCompressionResistancePriority="751" text="Scan the code to securely verify each other." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="y2w-7m-BE3">
|
||||
<rect key="frame" x="20" y="20" width="335" height="18"/>
|
||||
@@ -99,7 +106,7 @@
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="bQd-8A-8hf">
|
||||
<rect key="frame" x="0.0" y="113" width="375" height="330"/>
|
||||
<rect key="frame" x="0.0" y="168.5" width="375" height="330"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vrz-UO-PDk">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="200"/>
|
||||
@@ -119,10 +126,76 @@
|
||||
<constraint firstItem="NFT-6Y-5rt" firstAttribute="top" secondItem="vrz-UO-PDk" secondAttribute="top" id="Sfi-ob-xej"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NG7-qS-njl">
|
||||
<rect key="frame" x="0.0" y="200" width="375" height="28"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yMt-yE-2HC">
|
||||
<rect key="frame" x="185.5" y="6" width="40" height="40"/>
|
||||
<color key="backgroundColor" red="0.050980392156862744" green="0.74117647058823533" blue="0.54509803921568623" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="40" id="aE1-Fg-abK"/>
|
||||
<constraint firstAttribute="width" secondItem="yMt-yE-2HC" secondAttribute="height" id="xf4-KK-C59"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IUM-L7-fqo">
|
||||
<rect key="frame" x="149.5" y="6" width="40" height="40"/>
|
||||
<color key="backgroundColor" red="0.050980392159999999" green="0.74117647060000003" blue="0.5450980392" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="IUM-L7-fqo" secondAttribute="height" id="i5Y-Jl-e2K"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Xt1-wB-5jN">
|
||||
<rect key="frame" x="185.5" y="-30" width="40" height="40"/>
|
||||
<color key="backgroundColor" red="0.050980392159999999" green="0.74117647060000003" blue="0.5450980392" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="Xt1-wB-5jN" secondAttribute="height" id="ChW-li-1yx"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="niQ-mw-RBU">
|
||||
<rect key="frame" x="149.5" y="-30" width="40" height="40"/>
|
||||
<color key="backgroundColor" red="0.050980392159999999" green="0.74117647060000003" blue="0.5450980392" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="niQ-mw-RBU" secondAttribute="height" id="qac-f3-yEf"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f4V-Zd-iYY">
|
||||
<rect key="frame" x="187.5" y="8" width="0.0" height="0.0"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="f4V-Zd-iYY" secondAttribute="height" id="p2W-BD-0pf"/>
|
||||
<constraint firstAttribute="width" priority="750" constant="335" id="pQr-eX-6lg"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="IUM-L7-fqo" firstAttribute="width" secondItem="yMt-yE-2HC" secondAttribute="width" id="0qx-12-Oqf"/>
|
||||
<constraint firstItem="Xt1-wB-5jN" firstAttribute="width" secondItem="Xt1-wB-5jN" secondAttribute="height" id="16F-6k-5hE"/>
|
||||
<constraint firstItem="IUM-L7-fqo" firstAttribute="trailing" secondItem="f4V-Zd-iYY" secondAttribute="trailing" constant="2" id="2rc-Na-oKh"/>
|
||||
<constraint firstAttribute="bottom" secondItem="f4V-Zd-iYY" secondAttribute="bottom" constant="20" id="7vU-Kd-aUH"/>
|
||||
<constraint firstItem="niQ-mw-RBU" firstAttribute="bottom" secondItem="f4V-Zd-iYY" secondAttribute="bottom" constant="2" id="90A-GI-ydv"/>
|
||||
<constraint firstItem="Xt1-wB-5jN" firstAttribute="leading" secondItem="f4V-Zd-iYY" secondAttribute="leading" constant="-2" id="ARQ-GC-TtD"/>
|
||||
<constraint firstItem="Xt1-wB-5jN" firstAttribute="bottom" secondItem="f4V-Zd-iYY" secondAttribute="bottom" constant="2" id="BaR-VQ-xl5"/>
|
||||
<constraint firstItem="niQ-mw-RBU" firstAttribute="width" secondItem="niQ-mw-RBU" secondAttribute="height" id="C0X-CO-gMw"/>
|
||||
<constraint firstItem="yMt-yE-2HC" firstAttribute="top" secondItem="f4V-Zd-iYY" secondAttribute="top" constant="-2" id="Dht-S4-RFd"/>
|
||||
<constraint firstItem="f4V-Zd-iYY" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="NG7-qS-njl" secondAttribute="leading" constant="20" id="EfI-5E-z6t"/>
|
||||
<constraint firstItem="Xt1-wB-5jN" firstAttribute="width" secondItem="Xt1-wB-5jN" secondAttribute="height" id="Gcw-x9-kpj"/>
|
||||
<constraint firstItem="yMt-yE-2HC" firstAttribute="leading" secondItem="f4V-Zd-iYY" secondAttribute="leading" constant="-2" id="GeY-lp-P1D"/>
|
||||
<constraint firstItem="IUM-L7-fqo" firstAttribute="width" secondItem="IUM-L7-fqo" secondAttribute="height" id="NRf-2R-0sY"/>
|
||||
<constraint firstItem="niQ-mw-RBU" firstAttribute="trailing" secondItem="f4V-Zd-iYY" secondAttribute="trailing" constant="2" id="U76-gQ-ZZ7"/>
|
||||
<constraint firstItem="f4V-Zd-iYY" firstAttribute="top" secondItem="NG7-qS-njl" secondAttribute="top" constant="8" id="YGv-cp-Sgf"/>
|
||||
<constraint firstItem="niQ-mw-RBU" firstAttribute="width" secondItem="niQ-mw-RBU" secondAttribute="height" id="a0V-il-D07"/>
|
||||
<constraint firstItem="IUM-L7-fqo" firstAttribute="top" secondItem="f4V-Zd-iYY" secondAttribute="top" constant="-2" id="aBA-ut-3yd"/>
|
||||
<constraint firstItem="niQ-mw-RBU" firstAttribute="width" secondItem="niQ-mw-RBU" secondAttribute="height" id="czv-1W-Pza"/>
|
||||
<constraint firstItem="niQ-mw-RBU" firstAttribute="width" secondItem="yMt-yE-2HC" secondAttribute="width" id="ecw-A3-4fu"/>
|
||||
<constraint firstItem="f4V-Zd-iYY" firstAttribute="centerX" secondItem="NG7-qS-njl" secondAttribute="centerX" id="eh4-6M-8Js"/>
|
||||
<constraint firstItem="Xt1-wB-5jN" firstAttribute="width" secondItem="yMt-yE-2HC" secondAttribute="width" id="ujK-gu-UAL"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s4G-bW-EGe">
|
||||
<rect key="frame" x="0.0" y="200" width="375" height="55"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a4h-x5-COe">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="a4h-x5-COe">
|
||||
<rect key="frame" x="20" y="0.0" width="335" height="50"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
@@ -149,7 +222,7 @@
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="n73-GU-j8x">
|
||||
<rect key="frame" x="0.0" y="255" width="375" height="75"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bRZ-4Z-DEJ">
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="bRZ-4Z-DEJ">
|
||||
<rect key="frame" x="20" y="5" width="335" height="50"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
@@ -174,9 +247,6 @@
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="vrz-UO-PDk" firstAttribute="width" secondItem="bQd-8A-8hf" secondAttribute="width" id="N3C-LY-d1y"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
@@ -200,6 +270,7 @@
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="8yo-Sy-Oxb"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="edw-lO-NVl" secondAttribute="bottom" id="9bx-Fp-MtP"/>
|
||||
@@ -207,18 +278,20 @@
|
||||
<constraint firstItem="edw-lO-NVl" firstAttribute="trailing" secondItem="8yo-Sy-Oxb" secondAttribute="trailing" id="Wzg-67-vjz"/>
|
||||
<constraint firstItem="edw-lO-NVl" firstAttribute="top" secondItem="8yo-Sy-Oxb" secondAttribute="top" id="sAk-S7-Gts"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="8yo-Sy-Oxb"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="cannotScanButton" destination="bRZ-4Z-DEJ" id="O51-k2-JJY"/>
|
||||
<outlet property="closeButton" destination="1PP-lU-Ags" id="cwn-nr-esg"/>
|
||||
<outlet property="closeButtonContainer" destination="J1F-ba-sZ7" id="OQp-Vr-iv8"/>
|
||||
<outlet property="codeImageView" destination="NFT-6Y-5rt" id="0FH-o1-GBM"/>
|
||||
<outlet property="informationLabel" destination="y2w-7m-BE3" id="uXB-RC-Ppc"/>
|
||||
<outlet property="qrCodeContainerView" destination="vrz-UO-PDk" id="mMS-rd-ESe"/>
|
||||
<outlet property="qrCodeContainerView" destination="vrz-UO-PDk" id="64E-48-YIv"/>
|
||||
<outlet property="qrCodeReaderContainerView" destination="f4V-Zd-iYY" id="fg8-0P-GG3"/>
|
||||
<outlet property="qrCodeScannerContainerView" destination="NG7-qS-njl" id="xmO-wT-3X5"/>
|
||||
<outlet property="scanButtonContainerView" destination="s4G-bW-EGe" id="TL4-jJ-EDC"/>
|
||||
<outlet property="scanCodeButton" destination="a4h-x5-COe" id="8Cl-iJ-be8"/>
|
||||
<outlet property="titleLabel" destination="VsP-5V-z35" id="t5i-0x-a7m"/>
|
||||
<outlet property="titleView" destination="J1F-ba-sZ7" id="zVT-Mg-8di"/>
|
||||
<outlet property="titleLabel" destination="4Vv-12-J2A" id="HZc-SV-hcP"/>
|
||||
<outlet property="titleView" destination="JWN-4v-RUi" id="p4S-wd-mnh"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="uyZ-jd-xN3" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
||||
+65
-11
@@ -27,8 +27,6 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var scrollView: UIScrollView!
|
||||
|
||||
@IBOutlet private weak var closeButton: UIButton!
|
||||
|
||||
@IBOutlet private weak var titleView: UIView!
|
||||
@@ -36,12 +34,16 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var closeButtonContainer: UIView!
|
||||
|
||||
@IBOutlet private weak var codeImageView: UIImageView!
|
||||
|
||||
@IBOutlet private weak var scanCodeButton: UIButton!
|
||||
@IBOutlet private weak var cannotScanButton: UIButton!
|
||||
|
||||
|
||||
@IBOutlet private weak var qrCodeContainerView: UIView!
|
||||
@IBOutlet private weak var qrCodeScannerContainerView: UIView!
|
||||
@IBOutlet private weak var qrCodeReaderContainerView: UIView!
|
||||
|
||||
@IBOutlet private weak var scanButtonContainerView: UIView!
|
||||
|
||||
@@ -55,6 +57,7 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
private var cameraAccessManager: CameraAccessManager!
|
||||
|
||||
private weak var qrCodeReaderViewController: QRCodeReaderViewController?
|
||||
private var qrCodeReaderView: QRCodeReaderView?
|
||||
|
||||
private var alertPresentingViewController: UIViewController {
|
||||
return self.qrCodeReaderViewController ?? self
|
||||
@@ -135,25 +138,26 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in
|
||||
self?.cancelButtonAction()
|
||||
}
|
||||
|
||||
self.titleView.isHidden = self.navigationController != nil
|
||||
|
||||
|
||||
self.navigationItem.rightBarButtonItem = cancelBarButtonItem
|
||||
self.closeButtonContainer.isHidden = self.navigationController != nil
|
||||
|
||||
self.title = VectorL10n.keyVerificationVerifyQrCodeTitle
|
||||
self.titleLabel.text = VectorL10n.keyVerificationVerifyQrCodeTitle
|
||||
self.informationLabel.text = VectorL10n.keyVerificationVerifyQrCodeInformation
|
||||
|
||||
// Hide until we have the type of the verification request
|
||||
self.scanCodeButton.isHidden = true
|
||||
|
||||
self.cannotScanButton.setTitle(VectorL10n.keyVerificationVerifyQrCodeCannotScanAction, for: .normal)
|
||||
|
||||
removeQRCodeReaderView()
|
||||
}
|
||||
|
||||
private func render(viewState: KeyVerificationVerifyByScanningViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded(viewData: let viewData) where viewData.qrCodeData == nil && viewData.showScanAction:
|
||||
self.renderLoadedWithoutQRCodeData(viewData: viewData)
|
||||
case .loaded(viewData: let viewData):
|
||||
self.renderLoaded(viewData: viewData)
|
||||
case .error(let error):
|
||||
@@ -171,9 +175,56 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded(viewData: KeyVerificationVerifyByScanningViewData) {
|
||||
private func addQRCodeReaderView() {
|
||||
if self.qrCodeReaderView == nil {
|
||||
// configure QRCodeReaderView
|
||||
let qrCodeReaderView = QRCodeReaderView()
|
||||
qrCodeReaderView.didFoundData = { [weak self] data in
|
||||
self?.viewModel.process(viewAction: .scannedCode(payloadData: data))
|
||||
}
|
||||
self.qrCodeReaderView = qrCodeReaderView
|
||||
self.qrCodeReaderContainerView.vc_addSubViewMatchingParent(qrCodeReaderView)
|
||||
}
|
||||
self.qrCodeScannerContainerView.isHidden = false
|
||||
}
|
||||
|
||||
private func removeQRCodeReaderView() {
|
||||
if let qrCodeReaderView {
|
||||
qrCodeReaderView.removeFromSuperview()
|
||||
self.qrCodeReaderView = nil
|
||||
}
|
||||
self.qrCodeScannerContainerView.isHidden = true
|
||||
}
|
||||
|
||||
private func renderLoadedWithoutQRCodeData(viewData: KeyVerificationVerifyByScanningViewData) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
// We don't have a QR code to display
|
||||
self.qrCodeContainerView.isHidden = true
|
||||
// We will display a QR code scanner view, so no need to display the scan button
|
||||
self.scanButtonContainerView.isHidden = true
|
||||
|
||||
self.titleLabel.text = VectorL10n.keyVerificationScanQrCodeTitle
|
||||
|
||||
let informationText: String
|
||||
switch viewData.verificationKind {
|
||||
case .user:
|
||||
informationText = VectorL10n.keyVerificationScanQrCodeInformationOtherUser
|
||||
case .newSession:
|
||||
informationText = VectorL10n.keyVerificationScanQrCodeInformationNewSession
|
||||
case .otherSession:
|
||||
informationText = VectorL10n.keyVerificationScanQrCodeInformationOtherSession
|
||||
default:
|
||||
informationText = VectorL10n.keyVerificationScanQrCodeInformationOtherDevice
|
||||
}
|
||||
self.informationLabel.text = informationText
|
||||
|
||||
addQRCodeReaderView()
|
||||
}
|
||||
|
||||
private func renderLoaded(viewData: KeyVerificationVerifyByScanningViewData) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
let hideQRCodeImage: Bool
|
||||
|
||||
if let qrCodePayloadData = viewData.qrCodeData {
|
||||
@@ -183,7 +234,6 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
hideQRCodeImage = true
|
||||
}
|
||||
|
||||
self.title = viewData.verificationKind.verificationTitle
|
||||
self.titleLabel.text = viewData.verificationKind.verificationTitle
|
||||
self.qrCodeContainerView.isHidden = hideQRCodeImage
|
||||
self.scanButtonContainerView.isHidden = !viewData.showScanAction
|
||||
@@ -288,6 +338,8 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
|
||||
if let qrCodeReaderViewController = self.qrCodeReaderViewController {
|
||||
qrCodeReaderViewController.present(alert, animated: animated, completion: nil)
|
||||
} else {
|
||||
self.present(alert, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +368,7 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
|
||||
private func dismissQRCodeScanningIfPresented(animated: Bool, completion: (() -> Void)? = nil) {
|
||||
guard self.qrCodeReaderViewController?.presentingViewController != nil else {
|
||||
completion?()
|
||||
return
|
||||
}
|
||||
self.dismiss(animated: animated, completion: completion)
|
||||
@@ -328,6 +381,7 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
|
||||
}
|
||||
|
||||
@IBAction private func cannotScanAction(_ sender: Any) {
|
||||
qrCodeReaderView?.stopScanning()
|
||||
self.viewModel.process(viewAction: .cannotScan)
|
||||
}
|
||||
|
||||
@@ -352,7 +406,7 @@ extension KeyVerificationVerifyByScanningViewController: KeyVerificationVerifyBy
|
||||
// MARK: - QRCodeReaderViewControllerDelegate
|
||||
extension KeyVerificationVerifyByScanningViewController: QRCodeReaderViewControllerDelegate {
|
||||
|
||||
func qrCodeReaderViewController(_ viewController: QRCodeReaderViewController, didFound payloadData: Data) {
|
||||
func qrCodeReaderViewController(_ viewController: QRCodeReaderViewController, didFound payloadData: Data) {
|
||||
self.viewModel.process(viewAction: .scannedCode(payloadData: payloadData))
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -77,7 +77,7 @@ final class KeyVerificationVerifyByScanningViewModel: KeyVerificationVerifyBySca
|
||||
|
||||
private func loadData() {
|
||||
|
||||
let qrCodePlayloadData: Data?
|
||||
let qrCodePayloadData: Data?
|
||||
let canShowScanAction: Bool
|
||||
|
||||
self.qrCodeTransaction = self.keyVerificationManager.qrCodeTransaction(withTransactionId: self.keyVerificationRequest.requestId)
|
||||
@@ -85,19 +85,19 @@ final class KeyVerificationVerifyByScanningViewModel: KeyVerificationVerifyBySca
|
||||
if let supportedVerificationMethods = self.keyVerificationRequest.myMethods {
|
||||
|
||||
if let qrCodeData = self.qrCodeTransaction?.qrCodeData {
|
||||
qrCodePlayloadData = self.qrCodeDataCoder.encode(qrCodeData)
|
||||
qrCodePayloadData = self.qrCodeDataCoder.encode(qrCodeData)
|
||||
} else {
|
||||
qrCodePlayloadData = nil
|
||||
qrCodePayloadData = nil
|
||||
}
|
||||
|
||||
canShowScanAction = self.canShowScanAction(from: supportedVerificationMethods)
|
||||
} else {
|
||||
qrCodePlayloadData = nil
|
||||
qrCodePayloadData = nil
|
||||
canShowScanAction = false
|
||||
}
|
||||
|
||||
let viewData = KeyVerificationVerifyByScanningViewData(verificationKind: self.verificationKind,
|
||||
qrCodeData: qrCodePlayloadData,
|
||||
qrCodeData: qrCodePayloadData,
|
||||
showScanAction: canShowScanAction)
|
||||
|
||||
self.update(viewState: .loaded(viewData: viewData))
|
||||
|
||||
+34
-34
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="79A-qb-tmk">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="79A-qb-tmk">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -11,7 +11,7 @@
|
||||
<!--Key Verification Self Verify Wait View Controller-->
|
||||
<scene sceneID="a9K-1U-7Nm">
|
||||
<objects>
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="79A-qb-tmk" customClass="KeyVerificationSelfVerifyWaitViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController extendedLayoutIncludesOpaqueBars="YES" automaticallyAdjustsScrollViewInsets="NO" id="79A-qb-tmk" customClass="KeyVerificationSelfVerifyWaitViewController" customModule="Element" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="tAM-kt-f0s">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
@@ -20,22 +20,28 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tIM-sl-gwE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="518.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="562"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IlB-Ch-LEo">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="518.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="562"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Open Element on your other device" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="aOD-RJ-1qU">
|
||||
<rect key="frame" x="20" y="20" width="335" height="67"/>
|
||||
<fontDescription key="fontDescription" type="boldSystem" pointSize="28"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="d5Y-pj-XsI">
|
||||
<rect key="frame" x="20" y="20" width="335" height="84"/>
|
||||
<string key="text">Verify this session from one of your others sessions, granting it access to encrypted messages.
|
||||
<rect key="frame" x="20" y="117" width="335" height="84"/>
|
||||
<string key="text">You need to verify this session in order to read your secure message history.
|
||||
|
||||
Use the latest Riot on your other devices:</string>
|
||||
Open Element on one of your other devices and follow the instructions.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="ANK-XS-dY7">
|
||||
<rect key="frame" x="27.5" y="144" width="320" height="48"/>
|
||||
<rect key="frame" x="27.5" y="241" width="320" height="48"/>
|
||||
<subviews>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" spacing="14" translatesAutoresizingMaskIntoConstraints="NO" id="3at-ql-vhb">
|
||||
<rect key="frame" x="0.0" y="0.0" width="160" height="48"/>
|
||||
@@ -66,14 +72,8 @@ Use the latest Riot on your other devices:</string>
|
||||
<constraint firstAttribute="width" constant="320" id="TyM-5Y-YSw"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="This works with Element and other cross-signing capable Matrix clients." textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LnW-1H-Ltq">
|
||||
<rect key="frame" x="20" y="232" width="335" height="33.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="8oJ-o6-DLK">
|
||||
<rect key="frame" x="20" y="285.5" width="335" height="233"/>
|
||||
<rect key="frame" x="20" y="329" width="335" height="233"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dXT-cL-ukJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="335" height="114.5"/>
|
||||
@@ -102,8 +102,14 @@ Use the latest Riot on your other devices:</string>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nf8-Ye-b9r">
|
||||
<rect key="frame" x="0.0" y="114.5" width="335" height="118.5"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OEt-k0-vgM" customClass="RoundedButton" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="10" width="335" height="44"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="If you can't accessing an existing session" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Ou-cM-K9C">
|
||||
<rect key="frame" x="20" y="10" width="295" height="24.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OEt-k0-vgM" customClass="RoundedButton" customModule="Element" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="44.5" width="335" height="44"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="44" id="7ws-Nc-I7y"/>
|
||||
</constraints>
|
||||
@@ -115,19 +121,13 @@ Use the latest Riot on your other devices:</string>
|
||||
<action selector="recoverSecretsButtonAction:" destination="79A-qb-tmk" eventType="touchUpInside" id="FY5-tR-dyT"/>
|
||||
</connections>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="If you can't accessing an existing session" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4Ou-cM-K9C">
|
||||
<rect key="frame" x="20" y="64" width="295" height="24.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="12"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="4Ou-cM-K9C" secondAttribute="trailing" constant="20" id="6a1-gA-Cev"/>
|
||||
<constraint firstAttribute="bottom" secondItem="4Ou-cM-K9C" secondAttribute="bottom" constant="30" id="X1c-Yc-qLh"/>
|
||||
<constraint firstItem="4Ou-cM-K9C" firstAttribute="top" secondItem="OEt-k0-vgM" secondAttribute="bottom" constant="10" id="Y5b-Bf-IYu"/>
|
||||
<constraint firstItem="OEt-k0-vgM" firstAttribute="top" secondItem="nf8-Ye-b9r" secondAttribute="top" constant="10" id="fvJ-vZ-MMa"/>
|
||||
<constraint firstAttribute="bottom" secondItem="OEt-k0-vgM" secondAttribute="bottom" constant="30" id="LgI-8r-jEC"/>
|
||||
<constraint firstItem="4Ou-cM-K9C" firstAttribute="top" secondItem="nf8-Ye-b9r" secondAttribute="top" constant="10" id="Y5b-Bf-IYu"/>
|
||||
<constraint firstItem="OEt-k0-vgM" firstAttribute="top" secondItem="4Ou-cM-K9C" secondAttribute="bottom" constant="10" id="fvJ-vZ-MMa"/>
|
||||
<constraint firstItem="OEt-k0-vgM" firstAttribute="leading" secondItem="nf8-Ye-b9r" secondAttribute="leading" id="loc-cf-E6p"/>
|
||||
<constraint firstItem="4Ou-cM-K9C" firstAttribute="leading" secondItem="nf8-Ye-b9r" secondAttribute="leading" constant="20" id="o3E-Q9-7h1"/>
|
||||
<constraint firstAttribute="trailing" secondItem="OEt-k0-vgM" secondAttribute="trailing" id="yQK-YS-7Yr"/>
|
||||
@@ -145,16 +145,16 @@ Use the latest Riot on your other devices:</string>
|
||||
<constraint firstAttribute="height" priority="250" id="72E-My-WHK"/>
|
||||
<constraint firstItem="ANK-XS-dY7" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="IlB-Ch-LEo" secondAttribute="leading" id="9BR-a1-3By"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8oJ-o6-DLK" secondAttribute="bottom" id="CB2-Nd-n2g"/>
|
||||
<constraint firstItem="LnW-1H-Ltq" firstAttribute="top" secondItem="ANK-XS-dY7" secondAttribute="bottom" constant="40" id="Mph-SR-U7u"/>
|
||||
<constraint firstAttribute="trailing" secondItem="LnW-1H-Ltq" secondAttribute="trailing" constant="20" id="NZH-JW-JVA"/>
|
||||
<constraint firstItem="d5Y-pj-XsI" firstAttribute="leading" secondItem="IlB-Ch-LEo" secondAttribute="leading" constant="20" id="Okc-DX-xdE"/>
|
||||
<constraint firstItem="d5Y-pj-XsI" firstAttribute="top" secondItem="IlB-Ch-LEo" secondAttribute="top" constant="20" id="Pkt-ox-zZs"/>
|
||||
<constraint firstItem="d5Y-pj-XsI" firstAttribute="top" secondItem="aOD-RJ-1qU" secondAttribute="bottom" constant="30" id="Pkt-ox-zZs"/>
|
||||
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="ANK-XS-dY7" secondAttribute="trailing" id="RQI-kF-Z1h"/>
|
||||
<constraint firstItem="8oJ-o6-DLK" firstAttribute="leading" secondItem="IlB-Ch-LEo" secondAttribute="leading" constant="20" id="RtP-rm-bjH"/>
|
||||
<constraint firstItem="LnW-1H-Ltq" firstAttribute="leading" secondItem="IlB-Ch-LEo" secondAttribute="leading" constant="20" id="VOi-rz-Qf1"/>
|
||||
<constraint firstAttribute="trailing" secondItem="aOD-RJ-1qU" secondAttribute="trailing" constant="20" id="ST6-T5-XSg"/>
|
||||
<constraint firstItem="aOD-RJ-1qU" firstAttribute="leading" secondItem="IlB-Ch-LEo" secondAttribute="leading" constant="20" id="e2Y-ak-D86"/>
|
||||
<constraint firstItem="aOD-RJ-1qU" firstAttribute="top" secondItem="IlB-Ch-LEo" secondAttribute="top" constant="20" id="fDO-Bh-1Gn"/>
|
||||
<constraint firstItem="ANK-XS-dY7" firstAttribute="top" secondItem="d5Y-pj-XsI" secondAttribute="bottom" constant="40" id="gnp-AK-DYa"/>
|
||||
<constraint firstItem="ANK-XS-dY7" firstAttribute="centerX" secondItem="IlB-Ch-LEo" secondAttribute="centerX" id="nEo-IT-GtP"/>
|
||||
<constraint firstItem="8oJ-o6-DLK" firstAttribute="top" secondItem="LnW-1H-Ltq" secondAttribute="bottom" constant="20" id="tLd-x1-rOB"/>
|
||||
<constraint firstItem="8oJ-o6-DLK" firstAttribute="top" secondItem="ANK-XS-dY7" secondAttribute="bottom" constant="40" id="tLd-x1-rOB"/>
|
||||
<constraint firstAttribute="trailing" secondItem="8oJ-o6-DLK" secondAttribute="trailing" constant="20" id="vev-7p-7ua"/>
|
||||
</constraints>
|
||||
</view>
|
||||
@@ -178,6 +178,7 @@ Use the latest Riot on your other devices:</string>
|
||||
</constraints>
|
||||
</scrollView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="GnW-bb-rsL"/>
|
||||
<color key="backgroundColor" red="0.94509803920000002" green="0.96078431369999995" blue="0.97254901959999995" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="asO-rj-82y" firstAttribute="trailing" secondItem="GnW-bb-rsL" secondAttribute="trailing" id="Isw-tg-7go"/>
|
||||
@@ -185,10 +186,8 @@ Use the latest Riot on your other devices:</string>
|
||||
<constraint firstItem="GnW-bb-rsL" firstAttribute="top" secondItem="asO-rj-82y" secondAttribute="top" id="b3e-2d-oiV"/>
|
||||
<constraint firstAttribute="bottomMargin" secondItem="asO-rj-82y" secondAttribute="bottom" id="wMk-Oq-DwP"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="GnW-bb-rsL"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="additionalInformationLabel" destination="LnW-1H-Ltq" id="TJS-GC-ABf"/>
|
||||
<outlet property="desktopClientImageView" destination="nrB-Kj-4zE" id="yl0-Ju-luK"/>
|
||||
<outlet property="informationLabel" destination="d5Y-pj-XsI" id="TMb-bc-58a"/>
|
||||
<outlet property="mobileClientImageView" destination="P0P-X4-uSQ" id="WtT-ix-yq8"/>
|
||||
@@ -198,6 +197,7 @@ Use the latest Riot on your other devices:</string>
|
||||
<outlet property="recoverSecretsAvailabilityLoadingLabel" destination="A4x-sK-d5C" id="n5k-IO-RkV"/>
|
||||
<outlet property="recoverSecretsButton" destination="OEt-k0-vgM" id="RHU-ps-4m7"/>
|
||||
<outlet property="recoverSecretsContainerView" destination="nf8-Ye-b9r" id="4az-pe-0Uc"/>
|
||||
<outlet property="titleLabel" destination="aOD-RJ-1qU" id="qvC-fk-TiU"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="OMs-Ee-CjK" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
||||
+9
-15
@@ -30,13 +30,12 @@ final class KeyVerificationSelfVerifyWaitViewController: UIViewController {
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var desktopClientImageView: UIImageView!
|
||||
@IBOutlet private weak var mobileClientImageView: UIImageView!
|
||||
|
||||
@IBOutlet private weak var additionalInformationLabel: UILabel!
|
||||
|
||||
|
||||
@IBOutlet private weak var recoverSecretsAvailabilityLoadingContainerView: UIView!
|
||||
@IBOutlet private weak var recoverSecretsAvailabilityLoadingLabel: UILabel!
|
||||
@IBOutlet private weak var recoverSecretsAvailabilityActivityIndicatorView: UIActivityIndicatorView!
|
||||
@@ -70,7 +69,6 @@ final class KeyVerificationSelfVerifyWaitViewController: UIViewController {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.setupViews()
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
@@ -96,11 +94,11 @@ final class KeyVerificationSelfVerifyWaitViewController: UIViewController {
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
self.informationLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
self.informationLabel.textColor = theme.textSecondaryColor
|
||||
self.desktopClientImageView.tintColor = theme.tintColor
|
||||
self.mobileClientImageView.tintColor = theme.tintColor
|
||||
self.additionalInformationLabel.textColor = theme.textPrimaryColor
|
||||
self.recoverSecretsAvailabilityLoadingLabel.textColor = theme.textSecondaryColor
|
||||
self.recoverSecretsAvailabilityActivityIndicatorView.color = theme.tintColor
|
||||
}
|
||||
@@ -125,16 +123,13 @@ final class KeyVerificationSelfVerifyWaitViewController: UIViewController {
|
||||
self.cancelBarButtonItem = cancelBarButtonItem
|
||||
}
|
||||
|
||||
self.title = VectorL10n.deviceVerificationSelfVerifyWaitTitle
|
||||
|
||||
self.informationLabel.text = VectorL10n.deviceVerificationSelfVerifyWaitInformation(AppInfo.current.displayName)
|
||||
self.titleLabel.text = VectorL10n.deviceVerificationSelfVerifyOpenOnOtherDeviceTitle(AppInfo.current.displayName)
|
||||
self.informationLabel.text = VectorL10n.deviceVerificationSelfVerifyOpenOnOtherDeviceInformation
|
||||
|
||||
self.desktopClientImageView.image = Asset.Images.monitor.image.withRenderingMode(.alwaysTemplate)
|
||||
self.mobileClientImageView.image = Asset.Images.smartphone.image.withRenderingMode(.alwaysTemplate)
|
||||
|
||||
self.additionalInformationLabel.text = VectorL10n.deviceVerificationSelfVerifyWaitAdditionalInformation(AppInfo.current.displayName)
|
||||
|
||||
self.recoverSecretsAdditionalInformationLabel.text = VectorL10n.deviceVerificationSelfVerifyWaitRecoverSecretsAdditionalInformation
|
||||
|
||||
self.recoverSecretsAdditionalInformationLabel.text = VectorL10n.deviceVerificationSelfVerifyWaitRecoverSecretsAdditionalHelp(AppInfo.current.displayName)
|
||||
}
|
||||
|
||||
private func render(viewState: KeyVerificationSelfVerifyWaitViewState) {
|
||||
@@ -168,7 +163,6 @@ final class KeyVerificationSelfVerifyWaitViewController: UIViewController {
|
||||
private func renderLoaded(viewData: KeyVerificationSelfVerifyWaitViewData) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
|
||||
self.title = viewData.isNewSignIn ? VectorL10n.deviceVerificationSelfVerifyWaitNewSignInTitle : VectorL10n.deviceVerificationSelfVerifyWaitTitle
|
||||
self.cancelBarButtonItem?.title = viewData.isNewSignIn ? VectorL10n.skip : VectorL10n.cancel
|
||||
|
||||
let hideRecoverSecrets: Bool
|
||||
|
||||
+2
-1
@@ -285,7 +285,8 @@ final class KeyVerificationSelfVerifyWaitViewModel: KeyVerificationSelfVerifyWai
|
||||
private func sasTransactionDidStateChange(_ transaction: MXSASTransaction) {
|
||||
switch transaction.state {
|
||||
case MXSASTransactionStateIncomingShowAccept:
|
||||
transaction.accept()
|
||||
// The transaction will be automatically accepted by the MXKeyVerificationManager when the SAS start event is handled
|
||||
break
|
||||
case MXSASTransactionStateShowSAS:
|
||||
self.unregisterTransactionDidStateChangeNotification()
|
||||
self.coordinatorDelegate?.keyVerificationSelfVerifyWaitViewModel(self, didAcceptIncomingSASTransaction: transaction)
|
||||
|
||||
@@ -503,7 +503,7 @@ static const CGFloat kLocalPreviewMargin = 20;
|
||||
}
|
||||
else if (mxCall.isConferenceCall)
|
||||
{
|
||||
peerDisplayName = mxCall.room.summary.displayname;
|
||||
peerDisplayName = mxCall.room.summary.displayName;
|
||||
peerAvatarURL = mxCall.room.summary.avatar;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,10 +26,21 @@
|
||||
*/
|
||||
NSMutableDictionary *roomDataSources;
|
||||
|
||||
/**
|
||||
The list of rooms with a "late decryption" event. Causing bubbles issues
|
||||
Each element is a room ID.
|
||||
*/
|
||||
NSMutableSet *roomDataSourcesToDestroy;
|
||||
|
||||
/**
|
||||
Observe UIApplicationDidReceiveMemoryWarningNotification to dispose of any resources that can be recreated.
|
||||
*/
|
||||
id UIApplicationDidReceiveMemoryWarningNotificationObserver;
|
||||
|
||||
/**
|
||||
Observe kMXEventDidDecryptNotification to get late decrypted events.
|
||||
*/
|
||||
id mxEventDidDecryptNotificationObserver;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -119,6 +130,7 @@ static Class _roomDataSourceClass;
|
||||
{
|
||||
mxSession = matrixSession;
|
||||
roomDataSources = [NSMutableDictionary dictionary];
|
||||
roomDataSourcesToDestroy = [NSMutableSet set];
|
||||
_releasePolicy = MXKRoomDataSourceManagerReleasePolicyNeverRelease;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didMXSessionDidLeaveRoom:) name:kMXSessionDidLeaveRoomNotification object:nil];
|
||||
@@ -138,6 +150,12 @@ static Class _roomDataSourceClass;
|
||||
}
|
||||
|
||||
}];
|
||||
|
||||
// Observe late decrypted events, and store rooms ids in memory
|
||||
mxEventDidDecryptNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXEventDidDecryptNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
MXEvent *decryptedEvent = notif.object;
|
||||
[self->roomDataSourcesToDestroy addObject:decryptedEvent.roomId];
|
||||
}];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -156,6 +174,11 @@ static Class _roomDataSourceClass;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:UIApplicationDidReceiveMemoryWarningNotificationObserver];
|
||||
UIApplicationDidReceiveMemoryWarningNotificationObserver = nil;
|
||||
}
|
||||
if (mxEventDidDecryptNotificationObserver)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:mxEventDidDecryptNotificationObserver];
|
||||
mxEventDidDecryptNotificationObserver = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
@@ -202,9 +225,19 @@ static Class _roomDataSourceClass;
|
||||
|
||||
// If not available yet, create the room data source
|
||||
MXKRoomDataSource *roomDataSource = roomDataSources[roomId];
|
||||
|
||||
|
||||
// check if the room's dataSource has events with late decryption issues and destroys it
|
||||
BOOL roomDataSourceToBeDestroyed = [roomDataSourcesToDestroy containsObject:roomId];
|
||||
|
||||
if (roomDataSource && roomDataSourceToBeDestroyed && create) {
|
||||
[roomDataSource destroy];
|
||||
roomDataSources[roomId] = nil;
|
||||
roomDataSource = nil;
|
||||
}
|
||||
|
||||
if (!roomDataSource && create && roomId)
|
||||
{
|
||||
[roomDataSourcesToDestroy removeObject:roomId];
|
||||
[_roomDataSourceClass loadRoomDataSourceWithRoomId:roomId threadId:nil andMatrixSession:mxSession onComplete:^(id roomDataSource) {
|
||||
[self addRoomDataSource:roomDataSource];
|
||||
onComplete(roomDataSource);
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
{
|
||||
return self.roomSummary.spaceChildInfo.displayName;
|
||||
}
|
||||
return roomSummary.displayname;
|
||||
return roomSummary.displayName;
|
||||
}
|
||||
|
||||
- (NSString *)avatarUrl
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
MXRoom *room = [searchDataSource.mxSession roomWithRoomId:searchResult.result.roomId];
|
||||
if (room)
|
||||
{
|
||||
title = room.summary.displayname;
|
||||
title = room.summary.displayName;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
if ((row >= 0) && (row < rooms.count))
|
||||
{
|
||||
MXRoom* room = [rooms objectAtIndex:row];
|
||||
_inputTextField.text = room.summary.displayname;
|
||||
_inputTextField.text = room.summary.displayName;
|
||||
_addButton.enabled = YES;
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
rooms = [_mxSession.rooms sortedArrayUsingComparator:^NSComparisonResult(MXRoom* firstRoom, MXRoom* secondRoom) {
|
||||
|
||||
// Alphabetic order
|
||||
return [firstRoom.summary.displayname compare:secondRoom.summary.displayname options:NSCaseInsensitiveSearch];
|
||||
return [firstRoom.summary.displayName compare:secondRoom.summary.displayName options:NSCaseInsensitiveSearch];
|
||||
}];
|
||||
|
||||
return rooms.count;
|
||||
@@ -202,7 +202,7 @@
|
||||
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
|
||||
{
|
||||
MXRoom* room = [rooms objectAtIndex:row];
|
||||
return room.summary.displayname;
|
||||
return room.summary.displayName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
MXRoom *room = [_mxSession roomWithRoomId:mxPushRule.ruleId];
|
||||
if (room)
|
||||
{
|
||||
description = [VectorL10n notificationSettingsRoomRuleTitle:room.summary.displayname];
|
||||
description = [VectorL10n notificationSettingsRoomRuleTitle:room.summary.displayName];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
if (_mxRoom)
|
||||
{
|
||||
// Replace empty string by nil : avoid having the placeholder 'Room name" when there is no displayname
|
||||
self.displayNameTextField.text = (_mxRoom.summary.displayname.length) ? _mxRoom.summary.displayname : nil;
|
||||
self.displayNameTextField.text = (_mxRoom.summary.displayName.length) ? _mxRoom.summary.displayName : nil;
|
||||
}
|
||||
else if (_mxUser)
|
||||
{
|
||||
@@ -189,7 +189,7 @@
|
||||
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName])
|
||||
{
|
||||
// Only the room name is edited here, update the text field with the room name
|
||||
textField.text = _mxRoom.summary.displayname;
|
||||
textField.text = _mxRoom.summary.displayName;
|
||||
textField.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
else
|
||||
@@ -236,7 +236,7 @@
|
||||
textField.backgroundColor = [UIColor clearColor];
|
||||
|
||||
NSString *roomName = textField.text;
|
||||
if ((roomName.length || _mxRoom.summary.displayname.length) && [roomName isEqualToString:_mxRoom.summary.displayname] == NO)
|
||||
if ((roomName.length || _mxRoom.summary.displayName.length) && [roomName isEqualToString:_mxRoom.summary.displayName] == NO)
|
||||
{
|
||||
if ([self.delegate respondsToSelector:@selector(roomTitleView:isSaving:)])
|
||||
{
|
||||
@@ -266,7 +266,7 @@
|
||||
}
|
||||
|
||||
// Revert change
|
||||
textField.text = strongSelf.mxRoom.summary.displayname;
|
||||
textField.text = strongSelf.mxRoom.summary.displayName;
|
||||
MXLogDebug(@"[MXKRoomTitleView] Rename room failed");
|
||||
// Notify MatrixKit user
|
||||
NSString *myUserId = strongSelf.mxRoom.mxSession.myUser.userId;
|
||||
@@ -278,7 +278,7 @@
|
||||
else
|
||||
{
|
||||
// No change on room name, restore title with room displayName
|
||||
textField.text = _mxRoom.summary.displayname;
|
||||
textField.text = _mxRoom.summary.displayName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@
|
||||
if (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kMXEventTypeStringRoomName])
|
||||
{
|
||||
// Only the room name is edited here, update the text field with the room name
|
||||
textField.text = self.mxRoom.summary.displayname;
|
||||
textField.text = self.mxRoom.summary.displayName;
|
||||
textField.backgroundColor = [UIColor whiteColor];
|
||||
}
|
||||
else
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
//
|
||||
// Copyright 2023 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import ZXingObjC
|
||||
import Combine
|
||||
|
||||
final class QRCodeReaderView: UIView {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var didFoundData: (Data) -> Void = { _ in }
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private lazy var zxCapture: ZXCapture = ZXCapture()
|
||||
private var captureSizeTransform: CGAffineTransform?
|
||||
private var isScanning: Bool = false
|
||||
private var isFirstApplyOrientation: Bool = false
|
||||
|
||||
private var rotationObserver: AnyCancellable?
|
||||
|
||||
init() {
|
||||
super.init(frame: .zero)
|
||||
setup()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
setup()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
setup()
|
||||
}
|
||||
|
||||
deinit {
|
||||
#if !targetEnvironment(simulator)
|
||||
self.zxCapture.layer.removeFromSuperlayer()
|
||||
self.zxCapture.hard_stop()
|
||||
#endif
|
||||
}
|
||||
|
||||
override func didMoveToSuperview() {
|
||||
super.didMoveToSuperview()
|
||||
|
||||
if superview == nil {
|
||||
stopScanning()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func startScanning() {
|
||||
#if !targetEnvironment(simulator)
|
||||
self.zxCapture.start()
|
||||
#endif
|
||||
isScanning = true
|
||||
}
|
||||
|
||||
func stopScanning() {
|
||||
#if !targetEnvironment(simulator)
|
||||
self.zxCapture.stop()
|
||||
#endif
|
||||
isScanning = false
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
guard isFirstApplyOrientation == false else {
|
||||
return
|
||||
}
|
||||
|
||||
isFirstApplyOrientation = true
|
||||
applyOrientation()
|
||||
}
|
||||
|
||||
private func setup() {
|
||||
isUserInteractionEnabled = true
|
||||
clipsToBounds = true
|
||||
self.setupQRCodeReaderView()
|
||||
|
||||
rotationObserver = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
|
||||
.sink(receiveValue: { [weak self] _ in
|
||||
self?.applyOrientation()
|
||||
})
|
||||
}
|
||||
|
||||
private func setupQRCodeReaderView() {
|
||||
#if !targetEnvironment(simulator)
|
||||
zxCapture.delegate = self
|
||||
zxCapture.camera = zxCapture.back()
|
||||
zxCapture.layer.frame = self.bounds
|
||||
self.layer.addSublayer(zxCapture.layer)
|
||||
#endif
|
||||
}
|
||||
|
||||
private func applyOrientation() {
|
||||
|
||||
let orientation = UIApplication.shared.statusBarOrientation
|
||||
let captureRotation: Double
|
||||
let scanRectRotation: Double
|
||||
|
||||
switch orientation {
|
||||
case .portrait:
|
||||
captureRotation = 0
|
||||
scanRectRotation = 90
|
||||
case .landscapeLeft:
|
||||
captureRotation = 90
|
||||
scanRectRotation = 180
|
||||
case .landscapeRight:
|
||||
captureRotation = 270
|
||||
scanRectRotation = 0
|
||||
case .portraitUpsideDown:
|
||||
captureRotation = 180
|
||||
scanRectRotation = 270
|
||||
default:
|
||||
captureRotation = 0
|
||||
scanRectRotation = 90
|
||||
}
|
||||
|
||||
applyRectOfInterest(orientation: orientation)
|
||||
|
||||
let angleRadius = captureRotation / 180.0 * Double.pi
|
||||
let captureTranform = CGAffineTransform(rotationAngle: CGFloat(angleRadius))
|
||||
|
||||
zxCapture.transform = captureTranform
|
||||
zxCapture.rotation = CGFloat(scanRectRotation)
|
||||
zxCapture.layer.frame = self.bounds
|
||||
}
|
||||
|
||||
private func applyRectOfInterest(orientation: UIInterfaceOrientation) {
|
||||
var transformedVideoRect = self.frame
|
||||
let cameraSessionPreset = zxCapture.sessionPreset
|
||||
|
||||
var scaleVideoX, scaleVideoY: CGFloat
|
||||
var videoHeight, videoWidth: CGFloat
|
||||
|
||||
// Currently support only for 1920x1080 || 1280x720
|
||||
if cameraSessionPreset == AVCaptureSession.Preset.hd1920x1080.rawValue {
|
||||
videoHeight = 1080.0
|
||||
videoWidth = 1920.0
|
||||
} else {
|
||||
videoHeight = 720.0
|
||||
videoWidth = 1280.0
|
||||
}
|
||||
|
||||
if orientation == UIInterfaceOrientation.portrait {
|
||||
scaleVideoX = self.frame.width / videoHeight
|
||||
scaleVideoY = self.frame.height / videoWidth
|
||||
|
||||
// Convert CGPoint under portrait mode to map with orientation of image
|
||||
// because the image will be cropped before rotate
|
||||
// reference: https://github.com/TheLevelUp/ZXingObjC/issues/222
|
||||
let realX = transformedVideoRect.origin.y
|
||||
let realY = self.frame.size.width - transformedVideoRect.size.width - transformedVideoRect.origin.x
|
||||
let realWidth = transformedVideoRect.size.height
|
||||
let realHeight = transformedVideoRect.size.width
|
||||
transformedVideoRect = CGRect(x: realX, y: realY, width: realWidth, height: realHeight)
|
||||
|
||||
} else {
|
||||
scaleVideoX = self.frame.width / videoWidth
|
||||
scaleVideoY = self.frame.height / videoHeight
|
||||
}
|
||||
|
||||
captureSizeTransform = CGAffineTransform(scaleX: 1.0/scaleVideoX, y: 1.0/scaleVideoY)
|
||||
|
||||
guard let _captureSizeTransform = captureSizeTransform else {
|
||||
return
|
||||
}
|
||||
|
||||
let transformRect = transformedVideoRect.applying(_captureSizeTransform)
|
||||
zxCapture.scanRect = transformRect
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - ZXCaptureDelegate
|
||||
extension QRCodeReaderView: ZXCaptureDelegate {
|
||||
|
||||
func captureCameraIsReady(_ capture: ZXCapture!) {
|
||||
isScanning = true
|
||||
}
|
||||
|
||||
func captureResult(_ capture: ZXCapture!, result: ZXResult!) {
|
||||
guard let zxResult = result, isScanning == true else {
|
||||
return
|
||||
}
|
||||
|
||||
guard zxResult.barcodeFormat == kBarcodeFormatQRCode else {
|
||||
return
|
||||
}
|
||||
|
||||
self.stopScanning()
|
||||
|
||||
if let bytes = result.resultMetadata.object(forKey: kResultMetadataTypeByteSegments.rawValue) as? NSArray,
|
||||
let byteArray = bytes.firstObject as? ZXByteArray {
|
||||
|
||||
let data = Data(bytes: UnsafeRawPointer(byteArray.array), count: Int(byteArray.length))
|
||||
|
||||
self.didFoundData(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import ZXingObjC
|
||||
|
||||
protocol QRCodeReaderViewControllerDelegate: AnyObject {
|
||||
func qrCodeReaderViewController(_ viewController: QRCodeReaderViewController, didFound payloadData: Data)
|
||||
@@ -40,10 +39,7 @@ final class QRCodeReaderViewController: UIViewController {
|
||||
private var theme: Theme!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
|
||||
private lazy var zxCapture: ZXCapture = ZXCapture()
|
||||
private var captureSizeTransform: CGAffineTransform?
|
||||
private var isScanning: Bool = false
|
||||
private var isFirstApplyOrientation: Bool = false
|
||||
private var qrCodeReaderView: QRCodeReaderView!
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@@ -56,12 +52,7 @@ final class QRCodeReaderViewController: UIViewController {
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
return viewController
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.zxCapture.layer.removeFromSuperlayer()
|
||||
self.zxCapture.hard_stop()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
@@ -92,40 +83,14 @@ final class QRCodeReaderViewController: UIViewController {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
override func viewDidLayoutSubviews() {
|
||||
super.viewDidLayoutSubviews()
|
||||
|
||||
guard isFirstApplyOrientation == false else {
|
||||
return
|
||||
}
|
||||
|
||||
isFirstApplyOrientation = true
|
||||
applyOrientation()
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
coordinator.animate(alongsideTransition: { (context) in
|
||||
// do nothing
|
||||
}, completion: { [weak self] (context) in
|
||||
guard let self = self else {
|
||||
return
|
||||
}
|
||||
self.applyOrientation()
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func startScanning() {
|
||||
self.zxCapture.start()
|
||||
isScanning = true
|
||||
qrCodeReaderView.startScanning()
|
||||
}
|
||||
|
||||
func stopScanning() {
|
||||
self.zxCapture.stop()
|
||||
isScanning = false
|
||||
qrCodeReaderView.stopScanning()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
@@ -145,94 +110,15 @@ final class QRCodeReaderViewController: UIViewController {
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
self.setupQRCodeReaderView()
|
||||
let qrCodeReaderView = QRCodeReaderView()
|
||||
qrCodeReaderView.didFoundData = qrCodeReader(didFound:)
|
||||
self.qrCodeReaderView = qrCodeReaderView
|
||||
|
||||
self.codeReaderContainerView.vc_addSubViewMatchingParent(qrCodeReaderView)
|
||||
}
|
||||
|
||||
private func setupQRCodeReaderView() {
|
||||
zxCapture.delegate = self
|
||||
zxCapture.camera = zxCapture.back()
|
||||
|
||||
zxCapture.layer.frame = codeReaderContainerView.bounds
|
||||
codeReaderContainerView.layer.addSublayer(zxCapture.layer)
|
||||
}
|
||||
|
||||
private func applyOrientation() {
|
||||
|
||||
let orientation = UIApplication.shared.statusBarOrientation
|
||||
let captureRotation: Double
|
||||
let scanRectRotation: Double
|
||||
|
||||
switch orientation {
|
||||
case .portrait:
|
||||
captureRotation = 0
|
||||
scanRectRotation = 90
|
||||
case .landscapeLeft:
|
||||
captureRotation = 90
|
||||
scanRectRotation = 180
|
||||
case .landscapeRight:
|
||||
captureRotation = 270
|
||||
scanRectRotation = 0
|
||||
case .portraitUpsideDown:
|
||||
captureRotation = 180
|
||||
scanRectRotation = 270
|
||||
default:
|
||||
captureRotation = 0
|
||||
scanRectRotation = 90
|
||||
}
|
||||
|
||||
applyRectOfInterest(orientation: orientation)
|
||||
|
||||
let angleRadius = captureRotation / 180.0 * Double.pi
|
||||
let captureTranform = CGAffineTransform(rotationAngle: CGFloat(angleRadius))
|
||||
|
||||
zxCapture.transform = captureTranform
|
||||
zxCapture.rotation = CGFloat(scanRectRotation)
|
||||
zxCapture.layer.frame = codeReaderContainerView.frame
|
||||
}
|
||||
|
||||
private func applyRectOfInterest(orientation: UIInterfaceOrientation) {
|
||||
guard var transformedVideoRect = codeReaderContainerView?.frame,
|
||||
let cameraSessionPreset = zxCapture.sessionPreset
|
||||
else { return }
|
||||
|
||||
var scaleVideoX, scaleVideoY: CGFloat
|
||||
var videoHeight, videoWidth: CGFloat
|
||||
|
||||
// Currently support only for 1920x1080 || 1280x720
|
||||
if cameraSessionPreset == AVCaptureSession.Preset.hd1920x1080.rawValue {
|
||||
videoHeight = 1080.0
|
||||
videoWidth = 1920.0
|
||||
} else {
|
||||
videoHeight = 720.0
|
||||
videoWidth = 1280.0
|
||||
}
|
||||
|
||||
if orientation == UIInterfaceOrientation.portrait {
|
||||
scaleVideoX = self.view.frame.width / videoHeight
|
||||
scaleVideoY = self.view.frame.height / videoWidth
|
||||
|
||||
// Convert CGPoint under portrait mode to map with orientation of image
|
||||
// because the image will be cropped before rotate
|
||||
// reference: https://github.com/TheLevelUp/ZXingObjC/issues/222
|
||||
let realX = transformedVideoRect.origin.y
|
||||
let realY = self.view.frame.size.width - transformedVideoRect.size.width - transformedVideoRect.origin.x
|
||||
let realWidth = transformedVideoRect.size.height
|
||||
let realHeight = transformedVideoRect.size.width
|
||||
transformedVideoRect = CGRect(x: realX, y: realY, width: realWidth, height: realHeight)
|
||||
|
||||
} else {
|
||||
scaleVideoX = self.view.frame.width / videoWidth
|
||||
scaleVideoY = self.view.frame.height / videoHeight
|
||||
}
|
||||
|
||||
captureSizeTransform = CGAffineTransform(scaleX: 1.0/scaleVideoX, y: 1.0/scaleVideoY)
|
||||
|
||||
guard let _captureSizeTransform = captureSizeTransform else {
|
||||
return
|
||||
}
|
||||
|
||||
let transformRect = transformedVideoRect.applying(_captureSizeTransform)
|
||||
zxCapture.scanRect = transformRect
|
||||
private func qrCodeReader(didFound data: Data) {
|
||||
self.delegate?.qrCodeReaderViewController(self, didFound: data)
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
@@ -241,31 +127,3 @@ final class QRCodeReaderViewController: UIViewController {
|
||||
self.delegate?.qrCodeReaderViewControllerDidCancel(self)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ZXCaptureDelegate
|
||||
extension QRCodeReaderViewController: ZXCaptureDelegate {
|
||||
|
||||
func captureCameraIsReady(_ capture: ZXCapture!) {
|
||||
isScanning = true
|
||||
}
|
||||
|
||||
func captureResult(_ capture: ZXCapture!, result: ZXResult!) {
|
||||
guard let zxResult = result, isScanning == true else {
|
||||
return
|
||||
}
|
||||
|
||||
guard zxResult.barcodeFormat == kBarcodeFormatQRCode else {
|
||||
return
|
||||
}
|
||||
|
||||
self.stopScanning()
|
||||
|
||||
if let bytes = result.resultMetadata.object(forKey: kResultMetadataTypeByteSegments.rawValue) as? NSArray,
|
||||
let byteArray = bytes.firstObject as? ZXByteArray {
|
||||
|
||||
let data = Data(bytes: UnsafeRawPointer(byteArray.array), count: Int(byteArray.length))
|
||||
|
||||
self.delegate?.qrCodeReaderViewController(self, didFound: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1072,6 +1072,15 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
|
||||
// We do not want to merge room create event cells with other cell types
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (self.tag == RoomBubbleCellDataTagPoll) {
|
||||
MXEvent* event = self.events.firstObject;
|
||||
|
||||
if (event) {
|
||||
// m.poll.ended events should always show the sender information
|
||||
return event.eventType != MXEventTypePollEnd;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.hasThreadRoot || bubbleCellData.hasThreadRoot)
|
||||
{
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ final class RoomCreationEventsModalViewModel: RoomCreationEventsModalViewModelTy
|
||||
guard let summary = session.roomSummary(withRoomId: roomState.roomId) else {
|
||||
return nil
|
||||
}
|
||||
return summary.displayname
|
||||
return summary.displayName
|
||||
}
|
||||
|
||||
var roomInfo: String? {
|
||||
|
||||
@@ -760,7 +760,7 @@
|
||||
else
|
||||
{
|
||||
// set default title
|
||||
self.navigationItem.title = roomDataSource.room.summary.displayname;
|
||||
self.navigationItem.title = roomDataSource.room.summary.displayName;
|
||||
}
|
||||
|
||||
// Show input tool bar
|
||||
@@ -780,7 +780,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
self.navigationItem.title = roomDataSource.room.summary.displayname;
|
||||
self.navigationItem.title = roomDataSource.room.summary.displayName;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -890,7 +890,7 @@
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
cancelIndicator();
|
||||
MXLogDebug(@"[MXKRoomVC] Failed to join room (%@)", self->roomDataSource.room.summary.displayname);
|
||||
MXLogDebug(@"[MXKRoomVC] Failed to join room (%@)", self->roomDataSource.room.summary.displayName);
|
||||
[self processRoomJoinFailureWithError:error completion:completion];
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -366,11 +366,11 @@
|
||||
|
||||
switch (roomPowerLevel) {
|
||||
case RoomPowerLevelAdmin:
|
||||
self.roomMemberPowerLevelLabel.text = [VectorL10n roomMemberPowerLevelAdminIn:self.mxRoom.summary.displayname];
|
||||
self.roomMemberPowerLevelLabel.text = [VectorL10n roomMemberPowerLevelAdminIn:self.mxRoom.summary.displayName];
|
||||
self.roomMemberPowerLevelContainerView.hidden = NO;
|
||||
break;
|
||||
case RoomPowerLevelModerator:
|
||||
self.roomMemberPowerLevelLabel.text = [VectorL10n roomMemberPowerLevelModeratorIn:self.mxRoom.summary.displayname];
|
||||
self.roomMemberPowerLevelLabel.text = [VectorL10n roomMemberPowerLevelModeratorIn:self.mxRoom.summary.displayName];
|
||||
self.roomMemberPowerLevelContainerView.hidden = NO;
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -31,6 +31,7 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
||||
private let parentSpaceId: String?
|
||||
private let initialSection: RoomInfoSection
|
||||
private let dismissOnCancel: Bool
|
||||
private let canAddParticipants: Bool
|
||||
private weak var roomSettingsViewController: RoomSettingsViewController?
|
||||
|
||||
private lazy var segmentedViewController: SegmentedViewController = {
|
||||
@@ -43,6 +44,8 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
||||
participants.parentSpaceId = self.parentSpaceId
|
||||
participants.delegate = self
|
||||
participants.screenTracker = AnalyticsScreenTracker(screen: .roomMembers)
|
||||
participants.showInviteUserFab = self.canAddParticipants
|
||||
|
||||
|
||||
let files = RoomFilesViewController()
|
||||
files.finalizeInit()
|
||||
@@ -105,6 +108,7 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
||||
self.room = parameters.room
|
||||
self.parentSpaceId = parameters.parentSpaceId
|
||||
self.initialSection = parameters.initialSection
|
||||
self.canAddParticipants = parameters.canAddParticipants
|
||||
self.dismissOnCancel = parameters.dismissOnCancel
|
||||
}
|
||||
|
||||
|
||||
@@ -33,12 +33,14 @@ class RoomInfoCoordinatorParameters: NSObject {
|
||||
let parentSpaceId: String?
|
||||
let initialSection: RoomInfoSection
|
||||
let dismissOnCancel: Bool
|
||||
let canAddParticipants: Bool
|
||||
|
||||
init(session: MXSession, room: MXRoom, parentSpaceId: String?, initialSection: RoomInfoSection, dismissOnCancel: Bool) {
|
||||
init(session: MXSession, room: MXRoom, parentSpaceId: String?, initialSection: RoomInfoSection, canAddParticipants: Bool = true, dismissOnCancel: Bool) {
|
||||
self.session = session
|
||||
self.room = room
|
||||
self.parentSpaceId = parentSpaceId
|
||||
self.initialSection = initialSection
|
||||
self.canAddParticipants = canAddParticipants
|
||||
self.dismissOnCancel = dismissOnCancel
|
||||
super.init()
|
||||
}
|
||||
@@ -50,4 +52,8 @@ class RoomInfoCoordinatorParameters: NSObject {
|
||||
convenience init(session: MXSession, room: MXRoom, parentSpaceId: String?, initialSection: RoomInfoSection) {
|
||||
self.init(session: session, room: room, parentSpaceId: parentSpaceId, initialSection: initialSection, dismissOnCancel: false)
|
||||
}
|
||||
|
||||
convenience init(session: MXSession, room: MXRoom, parentSpaceId: String?, initialSection: RoomInfoSection, canAddParticipants: Bool) {
|
||||
self.init(session: session, room: room, parentSpaceId: parentSpaceId, initialSection: initialSection, canAddParticipants: canAddParticipants, dismissOnCancel: false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ final class RoomInfoListViewModel: NSObject, RoomInfoListViewModelType {
|
||||
let basicInfoViewData = RoomInfoBasicViewData(avatarUrl: room.summary.avatar,
|
||||
mediaManager: session.mediaManager,
|
||||
roomId: room.roomId,
|
||||
roomDisplayName: room.summary.displayname,
|
||||
roomDisplayName: room.summary.displayName,
|
||||
mainRoomAlias: room.summary.aliases?.first,
|
||||
roomTopic: room.summary.topic,
|
||||
encryptionImage: encryptionImage,
|
||||
|
||||
@@ -125,6 +125,8 @@ extern NSTimeInterval const kResizeComposerAnimationDuration;
|
||||
|
||||
@property (nonatomic, strong, nullable) ComposerLinkActionBridgePresenter *composerLinkActionBridgePresenter;
|
||||
|
||||
@property (weak, nonatomic, nullable) UIViewController *waitingOtherParticipantViewController;
|
||||
@property (nonatomic) BOOL isWaitingForOtherParticipants;
|
||||
|
||||
/**
|
||||
Retrieve the live data source in cases where the timeline is not live.
|
||||
|
||||
@@ -185,6 +185,9 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
|
||||
// Time to display notification content in the timeline
|
||||
MXTaskProfile *notificationTaskProfile;
|
||||
|
||||
// Observe kMXEventTypeStringRoomMember events
|
||||
__weak id roomMemberEventListener;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) RemoveJitsiWidgetView *removeJitsiWidgetView;
|
||||
@@ -233,6 +236,9 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
// scroll state just before the layout change, and restore it after the layout.
|
||||
@property (nonatomic) BOOL wasScrollAtBottomBeforeLayout;
|
||||
|
||||
// Check if we should wait for other participants
|
||||
@property (nonatomic, readonly) BOOL shouldWaitForOtherParticipants;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomViewController
|
||||
@@ -329,6 +335,7 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
|
||||
_showMissedDiscussionsBadge = YES;
|
||||
_scrollToBottomHidden = YES;
|
||||
_isWaitingForOtherParticipants = NO;
|
||||
|
||||
// Listen to the event sent state changes
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeSentState:) name:kMXEventDidChangeSentStateNotification object:nil];
|
||||
@@ -372,7 +379,10 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
|
||||
// Prepare missed dicussion badge (if any)
|
||||
self.showMissedDiscussionsBadge = _showMissedDiscussionsBadge;
|
||||
|
||||
|
||||
// Refresh the waiting for other participants state
|
||||
[self refreshWaitForOtherParticipantsState];
|
||||
|
||||
// Set up the room title view according to the data source (if any)
|
||||
[self refreshRoomTitle];
|
||||
|
||||
@@ -1194,9 +1204,9 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
|
||||
BOOL canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:kMXEventTypeStringRoomMessage]);
|
||||
BOOL isRoomObsolete = self.roomDataSource.roomState.isObsolete;
|
||||
BOOL isResourceLimitExceeded = [self.roomDataSource.mxSession.syncError.errcode isEqualToString:kMXErrCodeStringResourceLimitExceeded];
|
||||
BOOL isResourceLimitExceeded = [self.roomDataSource.mxSession.syncError.errcode isEqualToString:kMXErrCodeStringResourceLimitExceeded];
|
||||
|
||||
if (isRoomObsolete || isResourceLimitExceeded)
|
||||
if (isRoomObsolete || isResourceLimitExceeded || _isWaitingForOtherParticipants)
|
||||
{
|
||||
roomInputToolbarViewClass = nil;
|
||||
shouldDismissContextualMenu = YES;
|
||||
@@ -1532,6 +1542,8 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXEventDidChangeSentStateNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXEventDidChangeIdentifierNotification object:nil];
|
||||
|
||||
[self waitForOtherParticipant:NO];
|
||||
|
||||
[super destroy];
|
||||
}
|
||||
|
||||
@@ -1638,6 +1650,57 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
return self.customizedRoomDataSource.isCurrentUserSharingActiveLocation;
|
||||
}
|
||||
|
||||
#pragma mark - Wait for 3rd party invitee
|
||||
|
||||
- (void)setIsWaitingForOtherParticipants:(BOOL)isWaitingForOtherParticipants
|
||||
{
|
||||
if (_isWaitingForOtherParticipants == isWaitingForOtherParticipants)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isWaitingForOtherParticipants = isWaitingForOtherParticipants;
|
||||
[self updateRoomInputToolbarViewClassIfNeeded];
|
||||
|
||||
if (_isWaitingForOtherParticipants)
|
||||
{
|
||||
if (self->roomMemberEventListener == nil)
|
||||
{
|
||||
MXWeakify(self);
|
||||
self->roomMemberEventListener = [self.roomDataSource.room listenToEventsOfTypes:@[kMXEventTypeStringRoomMember] onEvent:^(MXEvent *event, MXTimelineDirection direction, MXRoomState *roomState) {
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
if (direction != MXTimelineDirectionForwards)
|
||||
{
|
||||
return;
|
||||
}
|
||||
[self refreshWaitForOtherParticipantsState];
|
||||
}];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self->roomMemberEventListener != nil)
|
||||
{
|
||||
[self.roomDataSource.room removeListener:self->roomMemberEventListener];
|
||||
self->roomMemberEventListener = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)shouldWaitForOtherParticipants
|
||||
{
|
||||
MXRoomState *roomState = self.roomDataSource.roomState;
|
||||
BOOL isDirect = self.roomDataSource.room.isDirect;
|
||||
|
||||
// Wait for the other participant only if it is a direct encrypted room with only one member waiting for a third party guest.
|
||||
return (isDirect && roomState.isEncrypted && roomState.membersCount.members == 1 && roomState.thirdPartyInvites.count > 0);
|
||||
}
|
||||
|
||||
- (void)refreshWaitForOtherParticipantsState
|
||||
{
|
||||
[self waitForOtherParticipant:self.shouldWaitForOtherParticipants];
|
||||
}
|
||||
|
||||
#pragma mark - Internals
|
||||
|
||||
- (UIBarButtonItem *)videoCallBarButtonItem
|
||||
@@ -1948,7 +2011,7 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
|
||||
[self refreshMissedDiscussionsCount:YES];
|
||||
|
||||
if (RiotSettings.shared.enableThreads)
|
||||
if (RiotSettings.shared.enableThreads && !_isWaitingForOtherParticipants)
|
||||
{
|
||||
if (self.roomDataSource.threadId)
|
||||
{
|
||||
@@ -2260,8 +2323,8 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
|
||||
- (void)showRoomInfoWithInitialSection:(RoomInfoSection)roomInfoSection animated:(BOOL)animated
|
||||
{
|
||||
RoomInfoCoordinatorParameters *parameters = [[RoomInfoCoordinatorParameters alloc] initWithSession:self.roomDataSource.mxSession room:self.roomDataSource.room parentSpaceId:self.parentSpaceId initialSection:roomInfoSection];
|
||||
|
||||
RoomInfoCoordinatorParameters *parameters = [[RoomInfoCoordinatorParameters alloc] initWithSession:self.roomDataSource.mxSession room:self.roomDataSource.room parentSpaceId:self.parentSpaceId initialSection:roomInfoSection canAddParticipants: !self.isWaitingForOtherParticipants];
|
||||
|
||||
self.roomInfoCoordinatorBridgePresenter = [[RoomInfoCoordinatorBridgePresenter alloc] initWithParameters:parameters];
|
||||
|
||||
self.roomInfoCoordinatorBridgePresenter.delegate = self;
|
||||
@@ -7450,7 +7513,7 @@ static CGSize kThreadListBarButtonItemImageSize;
|
||||
|
||||
- (void)updateThreadListBarButtonItem:(UIBarButtonItem *)barButtonItem with:(MXThreadingService *)service
|
||||
{
|
||||
if (!service)
|
||||
if (!service || _isWaitingForOtherParticipants)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -251,6 +251,50 @@ extension RoomViewController {
|
||||
composerLinkActionBridgePresenter = presenter
|
||||
presenter.present(from: self, animated: true)
|
||||
}
|
||||
|
||||
@objc func showWaitingOtherParticipantHeader() {
|
||||
let controller = VectorHostingController(rootView: RoomWaitingForMembers())
|
||||
guard let headerView = controller.view else {
|
||||
return
|
||||
}
|
||||
self.waitingOtherParticipantViewController = controller
|
||||
self.addChild(controller)
|
||||
|
||||
let containerView = UIView()
|
||||
containerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
headerView.translatesAutoresizingMaskIntoConstraints = false
|
||||
containerView.vc_addSubViewMatchingParent(headerView, withInsets: UIEdgeInsets(top: 9, left: 9, bottom: -9, right: -9))
|
||||
|
||||
self.bubblesTableView.tableHeaderView = containerView
|
||||
NSLayoutConstraint.activate([
|
||||
containerView.centerXAnchor.constraint(equalTo: self.bubblesTableView.centerXAnchor),
|
||||
containerView.widthAnchor.constraint(equalTo: self.bubblesTableView.widthAnchor),
|
||||
containerView.topAnchor.constraint(equalTo: self.bubblesTableView.topAnchor)
|
||||
])
|
||||
controller.didMove(toParent: self)
|
||||
|
||||
self.bubblesTableView.tableHeaderView?.layoutIfNeeded()
|
||||
}
|
||||
|
||||
@objc func hideWaitingOtherParticipantHeader() {
|
||||
guard let waitingOtherParticipantViewController else {
|
||||
return
|
||||
}
|
||||
waitingOtherParticipantViewController.removeFromParent()
|
||||
self.bubblesTableView.tableHeaderView = nil
|
||||
waitingOtherParticipantViewController.didMove(toParent: nil)
|
||||
self.waitingOtherParticipantViewController = nil
|
||||
}
|
||||
|
||||
@objc func waitForOtherParticipant(_ wait: Bool) {
|
||||
self.isWaitingForOtherParticipants = wait
|
||||
if wait {
|
||||
showWaitingOtherParticipantHeader()
|
||||
} else {
|
||||
hideWaitingOtherParticipantHeader()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Private Helpers
|
||||
|
||||
@@ -237,13 +237,13 @@ class RoomGroupCallStatusCell: RoomCallBaseCell {
|
||||
TimeInterval(widgetEvent.age)/MSEC_PER_SEC < Constants.secondsToDisplayAnswerDeclineOptions {
|
||||
|
||||
if JitsiService.shared.isWidgetDeclined(withId: widgetId) {
|
||||
innerContentView.callerNameLabel.text = room.summary.displayname
|
||||
innerContentView.callerNameLabel.text = room.summary.displayName
|
||||
room.summary.setRoomAvatarImageIn(innerContentView.avatarImageView)
|
||||
|
||||
viewState = .declined
|
||||
statusText = VectorL10n.eventFormatterCallYouDeclined
|
||||
} else {
|
||||
innerContentView.callerNameLabel.text = VectorL10n.eventFormatterGroupCallIncoming(bubbleCellData.senderDisplayName, room.summary.displayname)
|
||||
innerContentView.callerNameLabel.text = VectorL10n.eventFormatterGroupCallIncoming(bubbleCellData.senderDisplayName, room.summary.displayName)
|
||||
|
||||
innerContentView.avatarImageView.setImageURI(bubbleCellData.senderAvatarUrl,
|
||||
withType: nil,
|
||||
@@ -257,7 +257,7 @@ class RoomGroupCallStatusCell: RoomCallBaseCell {
|
||||
statusText = nil
|
||||
}
|
||||
} else {
|
||||
innerContentView.callerNameLabel.text = room.summary.displayname
|
||||
innerContentView.callerNameLabel.text = room.summary.displayName
|
||||
|
||||
room.summary.setRoomAvatarImageIn(innerContentView.avatarImageView)
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ class RoomCreationIntroCell: MXKRoomBubbleTableViewCell {
|
||||
discussionType = .room(topic: roomSummary.topic, canInvitePeople: bubbleData.canInvitePeople)
|
||||
}
|
||||
|
||||
let displayName = roomSummary.displayname ?? ""
|
||||
let displayName = roomSummary.displayName ?? ""
|
||||
|
||||
let roomAvatarViewData = RoomAvatarViewData(roomId: roomId,
|
||||
displayName: displayName,
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
[super customizeViewRendering];
|
||||
|
||||
self.backgroundColor = UIColor.clearColor;
|
||||
self.displayNameTextField.textColor = (self.mxRoom.summary.displayname.length ? ThemeService.shared.theme.textPrimaryColor : ThemeService.shared.theme.textSecondaryColor);
|
||||
self.displayNameTextField.textColor = (self.mxRoom.summary.displayName.length ? ThemeService.shared.theme.textPrimaryColor : ThemeService.shared.theme.textSecondaryColor);
|
||||
self.typingLabel.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
self.dotView.backgroundColor = ThemeService.shared.theme.warningColor;
|
||||
self.missedDiscussionsBadgeLabel.textColor = ThemeService.shared.theme.tintColor;
|
||||
@@ -121,7 +121,7 @@
|
||||
[self.presenceIndicatorView stopListeningPresenceUpdates];
|
||||
}
|
||||
|
||||
self.displayNameTextField.text = self.mxRoom.summary.displayname;
|
||||
self.displayNameTextField.text = self.mxRoom.summary.displayName;
|
||||
if (!self.displayNameTextField.text.length)
|
||||
{
|
||||
self.displayNameTextField.text = [VectorL10n roomDisplaynameEmptyRoom];
|
||||
|
||||
@@ -48,7 +48,7 @@ import MediaPlayer
|
||||
didSet {
|
||||
// set avatar placeholder for now
|
||||
roomAvatar = AvatarGenerator.generateAvatar(forMatrixItem: currentRoomSummary?.roomId,
|
||||
withDisplayName: currentRoomSummary?.displayname,
|
||||
withDisplayName: currentRoomSummary?.displayName,
|
||||
size: Constants.roomAvatarImageSize.width,
|
||||
andFontSize: Constants.roomAvatarFontSize)
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ final class ShowDirectoryViewModel: NSObject, ShowDirectoryViewModelType {
|
||||
}
|
||||
|
||||
private func roomCellViewModel(with room: MXRoom) -> DirectoryRoomTableViewCellVM {
|
||||
let displayName = room.summary.displayname
|
||||
let displayName = room.summary.displayName
|
||||
let joinedMembersCount = Int(room.summary.membersCount.joined)
|
||||
let topic = MXTools.stripNewlineCharacters(room.summary.topic)
|
||||
let isJoined = room.summary.membership == .join || room.summary.membershipTransitionState == .joined
|
||||
|
||||
@@ -588,7 +588,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
if (BuildSettings.settingsScreenShowLabSettings)
|
||||
{
|
||||
Section *sectionLabs = [Section sectionWithTag:SECTION_TAG_LABS];
|
||||
if (MXSDKOptions.sharedInstance.isCryptoSDKAvailable)
|
||||
if ([CryptoSDKFeature.shared canManuallyEnableForUserId:self.mainSession.myUserId])
|
||||
{
|
||||
[sectionLabs addRowWithTag:LABS_ENABLE_CRYPTO_SDK];
|
||||
}
|
||||
@@ -2589,20 +2589,17 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
else
|
||||
else if (row == LABS_ENABLE_CRYPTO_SDK)
|
||||
{
|
||||
if (row == LABS_ENABLE_CRYPTO_SDK)
|
||||
{
|
||||
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
BOOL isEnabled = MXSDKOptions.sharedInstance.enableCryptoSDK;
|
||||
labelAndSwitchCell.mxkLabel.text = isEnabled ? VectorL10n.settingsLabsDisableCryptoSdk : VectorL10n.settingsLabsEnableCryptoSdk;
|
||||
labelAndSwitchCell.mxkSwitch.on = isEnabled;
|
||||
[labelAndSwitchCell.mxkSwitch setEnabled:!isEnabled];
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(enableCryptoSDKFeature:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
MXKTableViewCellWithLabelAndSwitch *labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
|
||||
BOOL isEnabled = MXSDKOptions.sharedInstance.enableCryptoSDK;
|
||||
labelAndSwitchCell.mxkLabel.text = isEnabled ? VectorL10n.settingsLabsDisableCryptoSdk : VectorL10n.settingsLabsEnableCryptoSdk;
|
||||
labelAndSwitchCell.mxkSwitch.on = isEnabled;
|
||||
[labelAndSwitchCell.mxkSwitch setEnabled:!isEnabled];
|
||||
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
|
||||
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(enableCryptoSDKFeature:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
cell = labelAndSwitchCell;
|
||||
}
|
||||
}
|
||||
else if (section == SECTION_TAG_SECURITY)
|
||||
@@ -3391,10 +3388,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
|
||||
}]];
|
||||
|
||||
[confirmationAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n continue] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
|
||||
|
||||
[CryptoSDKConfiguration.shared enable];
|
||||
[Analytics.shared trackCryptoSDKEnabled];
|
||||
|
||||
[CryptoSDKFeature.shared enable];
|
||||
[[AppDelegate theDelegate] reloadMatrixSessions:YES];
|
||||
}]];
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ class SpaceDetailViewModel: SpaceDetailViewModelType {
|
||||
}
|
||||
|
||||
let parameters = SpaceDetailLoadedParameters(spaceId: space.spaceId,
|
||||
displayName: summary.displayname,
|
||||
displayName: summary.displayName,
|
||||
topic: summary.topic,
|
||||
avatarUrl: summary.avatar,
|
||||
joinRule: nil,
|
||||
@@ -130,7 +130,7 @@ class SpaceDetailViewModel: SpaceDetailViewModelType {
|
||||
})
|
||||
|
||||
let parameters = SpaceDetailLoadedParameters(spaceId: space.spaceId,
|
||||
displayName: summary.displayname,
|
||||
displayName: summary.displayName,
|
||||
topic: summary.topic,
|
||||
avatarUrl: summary.avatar,
|
||||
joinRule: joinRule,
|
||||
|
||||
@@ -248,9 +248,9 @@ final class SpaceListViewModel: SpaceListViewModelType {
|
||||
var invites: [SpaceListItemViewData] = []
|
||||
var spaces: [SpaceListItemViewData] = []
|
||||
session.spaceService.rootSpaceSummaries.forEach { summary in
|
||||
let avatarViewData = AvatarViewData(matrixItemId: summary.roomId, displayName: summary.displayname, avatarUrl: summary.avatar, mediaManager: session.mediaManager, fallbackImage: .matrixItem(summary.roomId, summary.displayname))
|
||||
let avatarViewData = AvatarViewData(matrixItemId: summary.roomId, displayName: summary.displayName, avatarUrl: summary.avatar, mediaManager: session.mediaManager, fallbackImage: .matrixItem(summary.roomId, summary.displayName))
|
||||
let notificationState = session.spaceService.notificationCounter.notificationState(forSpaceWithId: summary.roomId)
|
||||
let viewData = SpaceListItemViewData(spaceId: summary.roomId, title: summary.displayname,
|
||||
let viewData = SpaceListItemViewData(spaceId: summary.roomId, title: summary.displayName,
|
||||
avatarViewData: avatarViewData,
|
||||
isInvite: summary.membership == .invite,
|
||||
notificationCount: notificationState?.groupMissedDiscussionsCount ?? 0,
|
||||
|
||||
@@ -142,7 +142,7 @@ final class SpaceMemberListViewController: RoomParticipantsViewController {
|
||||
private func renderLoaded(space: MXSpace) {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
self.mxRoom = space.room
|
||||
if let spaceName = space.summary?.displayname {
|
||||
if let spaceName = space.summary?.displayName {
|
||||
self.titleView.breadcrumbView.breadcrumbs = [spaceName]
|
||||
} else {
|
||||
self.titleView.breadcrumbView.breadcrumbs = []
|
||||
|
||||
@@ -101,7 +101,7 @@ class SpaceMenuPresenter: NSObject {
|
||||
}
|
||||
|
||||
private func showLeaveSpace() {
|
||||
let name = session.spaceService.getSpace(withId: spaceId)?.summary?.displayname ?? VectorL10n.spaceTag
|
||||
let name = session.spaceService.getSpace(withId: spaceId)?.summary?.displayName ?? VectorL10n.spaceTag
|
||||
|
||||
let selectionHeader = MatrixItemChooserSelectionHeader(title: VectorL10n.leaveSpaceSelectionTitle,
|
||||
selectAllTitle: VectorL10n.leaveSpaceSelectionAllRooms,
|
||||
|
||||
@@ -140,9 +140,9 @@ class SpaceMenuViewController: UIViewController {
|
||||
return
|
||||
}
|
||||
|
||||
let avatarViewData = AvatarViewData(matrixItemId: summary.roomId, displayName: summary.displayname, avatarUrl: summary.avatar, mediaManager: self.session.mediaManager, fallbackImage: .matrixItem(summary.roomId, summary.displayname))
|
||||
let avatarViewData = AvatarViewData(matrixItemId: summary.roomId, displayName: summary.displayName, avatarUrl: summary.avatar, mediaManager: self.session.mediaManager, fallbackImage: .matrixItem(summary.roomId, summary.displayName))
|
||||
|
||||
self.titleLabel.text = space.summary?.displayname
|
||||
self.titleLabel.text = space.summary?.displayName
|
||||
// TODO: display members instead once done on android
|
||||
// self.subtitleLabel.text = space.membersId.count == 1 ? VectorL10n.roomTitleOneMember :
|
||||
// VectorL10n.roomTitleMembers("\(space.membersId.count)")
|
||||
|
||||
@@ -60,7 +60,7 @@ final class ExploreRoomCoordinator: NSObject, ExploreRoomCoordinatorType {
|
||||
|
||||
func start() {
|
||||
|
||||
let rootCoordinator = self.createShowSpaceExploreRoomCoordinator(session: self.session, spaceId: self.spaceId, spaceName: self.session.spaceService.getSpace(withId: self.spaceId)?.summary?.displayname)
|
||||
let rootCoordinator = self.createShowSpaceExploreRoomCoordinator(session: self.session, spaceId: self.spaceId, spaceName: self.session.spaceService.getSpace(withId: self.spaceId)?.summary?.displayName)
|
||||
|
||||
rootCoordinator.start()
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
UIBarButtonItem *cancelBarButtonItem;
|
||||
UIBarButtonItem *createBarButtonItem;
|
||||
|
||||
// SearchBar text
|
||||
NSString *currentSearch;
|
||||
|
||||
// HTTP Request
|
||||
MXHTTPOperation *roomCreationRequest;
|
||||
|
||||
@@ -45,10 +48,13 @@
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchBarHeader;
|
||||
@property (weak, nonatomic) IBOutlet UISearchBar *searchBarView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *searchBarHeaderBorder;
|
||||
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *searchBarHeaderHeightConstraint;
|
||||
|
||||
@property (nonatomic, strong) InviteFriendsPresenter *inviteFriendsPresenter;
|
||||
@property (nonatomic, weak) InviteFriendsHeaderView *inviteFriendsHeaderView;
|
||||
|
||||
@property (nonatomic, weak) UIView *onlyOneEmailInvitationView;
|
||||
|
||||
@end
|
||||
|
||||
@implementation StartChatViewController
|
||||
@@ -130,6 +136,11 @@
|
||||
|
||||
- (void)setupInviteFriendsHeaderView
|
||||
{
|
||||
if (self.inviteFriendsHeaderView)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!RiotSettings.shared.allowInviteExernalUsers)
|
||||
{
|
||||
self.contactsTableView.tableHeaderView = nil;
|
||||
@@ -152,7 +163,7 @@
|
||||
[self setupInviteFriendsHeaderView];
|
||||
}
|
||||
}
|
||||
else
|
||||
else if (self.inviteFriendsHeaderView != nil)
|
||||
{
|
||||
self.contactsTableView.tableHeaderView = nil;
|
||||
}
|
||||
@@ -304,6 +315,104 @@
|
||||
contactsDataSource.ignoredContactsByMatrixId[self.mainSession.myUser.userId] = userContact;
|
||||
}
|
||||
}
|
||||
|
||||
// hide the search bar if a participant is already invited by email
|
||||
BOOL hideSearchBar = [self participantsAlreadyContainAnEmail];
|
||||
self.searchBarHeader.alpha = hideSearchBar ? 0.0f : 1.0f;
|
||||
self.searchBarHeaderHeightConstraint.constant = hideSearchBar ? 0.0f : 50.0f;
|
||||
[UIView animateWithDuration:0.2f animations:^{
|
||||
[self.view layoutIfNeeded];
|
||||
}];
|
||||
}
|
||||
|
||||
- (BOOL)participantsAlreadyContainAnEmail
|
||||
{
|
||||
for (MXKContact* participant in participants)
|
||||
{
|
||||
// if it is not a matrix contact or a local contact with a MatrixID
|
||||
if (participant.matrixIdentifiers.count == 0 && ![MXTools isMatrixUserIdentifier:participant.displayName])
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)canAddParticipant: (MXKContact*) contact
|
||||
{
|
||||
if (!contact)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
// The following rules will be applied only if the resulting room is going to be encrypted
|
||||
if (![self.mainSession vc_homeserverConfiguration].encryption.isE2EEByDefaultEnabled)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
// If we have already invited an email, we cannot add another participant
|
||||
if ([self participantsAlreadyContainAnEmail])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
// if it is not a matrix contact, nor a local contact with a MatrixID, and if there is already at least one participant, another participant cannot be added.
|
||||
if ((contact.matrixIdentifiers.count == 0 && ![MXTools isMatrixUserIdentifier:contact.displayName]) && participants.count > 0)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Otherwise, we should be able to add this participant
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)showAllowOnlyOneInvitByEmailAllowedHeaderView:(BOOL)visible
|
||||
{
|
||||
if (visible)
|
||||
{
|
||||
if (!self.onlyOneEmailInvitationView)
|
||||
{
|
||||
UIView *headerView = [[UIView alloc] initWithFrame: CGRectZero];
|
||||
headerView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
UILabel *label = [[UILabel alloc] initWithFrame: CGRectZero];
|
||||
label.numberOfLines = 0;
|
||||
label.textColor = ThemeService.shared.theme.textSecondaryColor;
|
||||
label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightLight];
|
||||
label.adjustsFontSizeToFitWidth = YES;
|
||||
|
||||
label.text = VectorL10n.roomCreationOnlyOneEmailInvite;
|
||||
label.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
[headerView addSubview:label];
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[label.leadingAnchor constraintEqualToAnchor:headerView.leadingAnchor constant:16],
|
||||
[label.trailingAnchor constraintEqualToAnchor:headerView.trailingAnchor constant:-16],
|
||||
[label.topAnchor constraintEqualToAnchor:headerView.topAnchor constant:8],
|
||||
[label.bottomAnchor constraintEqualToAnchor:headerView.bottomAnchor constant:-8],
|
||||
]];
|
||||
[label setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
|
||||
[headerView setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
|
||||
|
||||
self.onlyOneEmailInvitationView = headerView;
|
||||
self.contactsTableView.tableHeaderView = self.onlyOneEmailInvitationView;
|
||||
|
||||
[NSLayoutConstraint activateConstraints:@[
|
||||
[headerView.leadingAnchor constraintEqualToAnchor:self.contactsTableView.safeAreaLayoutGuide.leadingAnchor],
|
||||
[headerView.trailingAnchor constraintEqualToAnchor:self.contactsTableView.safeAreaLayoutGuide.trailingAnchor]
|
||||
]];
|
||||
[self.contactsTableView.tableHeaderView layoutIfNeeded];
|
||||
}
|
||||
}
|
||||
else if (self.onlyOneEmailInvitationView != nil)
|
||||
{
|
||||
if (self.contactsTableView.tableHeaderView == self.onlyOneEmailInvitationView)
|
||||
{
|
||||
self.contactsTableView.tableHeaderView = nil;
|
||||
}
|
||||
self.onlyOneEmailInvitationView = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showInviteFriendsFromSourceView:(UIView*)sourceView
|
||||
@@ -367,6 +476,14 @@
|
||||
if (_isAddParticipantSearchBarEditing)
|
||||
{
|
||||
cell = [contactsDataSource tableView:tableView cellForRowAtIndexPath:indexPath];
|
||||
MXKContact* contact = [contactsDataSource contactAtIndexPath:indexPath];
|
||||
if (![self canAddParticipant:contact])
|
||||
{
|
||||
// Prevent to add it
|
||||
cell.contentView.alpha = 0.5;
|
||||
cell.userInteractionEnabled = NO;
|
||||
cell.accessoryView = nil;
|
||||
}
|
||||
}
|
||||
else if (indexPath.section == participantsSection)
|
||||
{
|
||||
@@ -533,7 +650,7 @@
|
||||
|
||||
// Prepare the invited participant data
|
||||
NSMutableArray *inviteArray = [NSMutableArray array];
|
||||
NSMutableArray *invite3PIDArray = [NSMutableArray array];
|
||||
NSMutableArray<MXInvite3PID *> *invite3PIDArray = [NSMutableArray array];
|
||||
|
||||
// Check whether some users must be invited
|
||||
for (MXKContact *contact in participants)
|
||||
@@ -594,7 +711,7 @@
|
||||
|
||||
// Is it a direct chat?
|
||||
BOOL isDirect = ((inviteArray.count + invite3PIDArray.count == 1) ? YES : NO);
|
||||
|
||||
|
||||
// In case of a direct chat with only one user id, we open the first available direct chat
|
||||
// or creates a new one (if it doesn't exist).
|
||||
if (isDirect && inviteArray.count)
|
||||
@@ -606,6 +723,19 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't want to create a new direct room for a 3rd party invite if we already have one
|
||||
NSString *first3rdPartyInvitee = invite3PIDArray.firstObject.address;
|
||||
if (isDirect && first3rdPartyInvitee)
|
||||
{
|
||||
MXRoom *existingRoom = [self.mainSession directJoinedRoomWithUserId:first3rdPartyInvitee];
|
||||
if (existingRoom)
|
||||
{
|
||||
[self stopActivityIndicator];
|
||||
[[AppDelegate theDelegate] showRoom:existingRoom.roomId andEventId:nil withMatrixSession:self.mainSession];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure direct chat are created with equal ops on both sides (the trusted_private_chat preset)
|
||||
MXRoomPreset preset = (isDirect ? kMXRoomPresetTrustedPrivateChat : nil);
|
||||
|
||||
@@ -635,7 +765,7 @@
|
||||
roomCreationParameters.isDirect = isDirect;
|
||||
roomCreationParameters.preset = preset;
|
||||
|
||||
if (canEnableE2E && roomCreationParameters.invite3PIDArray == nil)
|
||||
if (canEnableE2E)
|
||||
{
|
||||
roomCreationParameters.initialStateEvents = @[
|
||||
[MXRoomCreationParameters initialStateEventForEncryptionWithAlgorithm:kMXCryptoMegolmAlgorithm
|
||||
@@ -644,6 +774,9 @@
|
||||
|
||||
self->roomCreationRequest = [self.mainSession createRoomWithParameters:roomCreationParameters success:^(MXRoom *room) {
|
||||
|
||||
// Update the room summary
|
||||
[room.summary resetRoomStateData];
|
||||
|
||||
self->roomCreationRequest = nil;
|
||||
|
||||
[self stopActivityIndicator];
|
||||
@@ -702,8 +835,28 @@
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
|
||||
{
|
||||
[contactsDataSource searchWithPattern:searchText forceReset:NO];
|
||||
self->currentSearch = searchText;
|
||||
if (searchText != nil && searchText.length > 0)
|
||||
{
|
||||
MXKContact *contact = nil;
|
||||
if ([MXTools isMatrixUserIdentifier:searchText])
|
||||
{
|
||||
contact = [[MXKContact alloc] initMatrixContactWithDisplayName:searchText andMatrixID:searchText];
|
||||
|
||||
}
|
||||
else if ([MXTools isEmailAddress:searchText])
|
||||
{
|
||||
contact = [[MXKContact alloc] initContactWithDisplayName:searchText emails:nil phoneNumbers:nil andThumbnail:nil];
|
||||
}
|
||||
|
||||
[self showAllowOnlyOneInvitByEmailAllowedHeaderView: ![self canAddParticipant:contact]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self showAllowOnlyOneInvitByEmailAllowedHeaderView:NO];
|
||||
}
|
||||
|
||||
[contactsDataSource searchWithPattern:searchText forceReset:NO];
|
||||
self.contactsAreFilteredWithSearch = searchText.length ? YES : NO;
|
||||
}
|
||||
|
||||
@@ -718,6 +871,7 @@
|
||||
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar
|
||||
{
|
||||
searchBar.text = nil;
|
||||
self->currentSearch = nil;
|
||||
self.isAddParticipantSearchBarEditing = NO;
|
||||
|
||||
// Reset filtering
|
||||
@@ -725,6 +879,8 @@
|
||||
|
||||
// Leave search
|
||||
[searchBar resignFirstResponder];
|
||||
|
||||
[self showAllowOnlyOneInvitByEmailAllowedHeaderView:NO];
|
||||
}
|
||||
|
||||
#pragma mark - ContactsTableViewControllerDelegate
|
||||
@@ -763,14 +919,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (contact)
|
||||
if ([self canAddParticipant:contact])
|
||||
{
|
||||
// Update here the mutable list of participants
|
||||
[participants addObject:contact];
|
||||
|
||||
// Refresh display by leaving search session
|
||||
[self searchBarCancelButtonClicked:_searchBarView];
|
||||
}
|
||||
|
||||
// Refresh display by leaving search session
|
||||
[self searchBarCancelButtonClicked:_searchBarView];
|
||||
}
|
||||
|
||||
#pragma mark - InviteFriendsHeaderViewDelegate
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@@ -13,6 +13,7 @@
|
||||
<outlet property="contactsTableView" destination="kNf-Ll-jvH" id="PDi-bW-2CG"/>
|
||||
<outlet property="searchBarHeader" destination="Zm7-AB-ZtE" id="6ee-P0-twi"/>
|
||||
<outlet property="searchBarHeaderBorder" destination="gcy-W7-89G" id="tsy-SP-KaJ"/>
|
||||
<outlet property="searchBarHeaderHeightConstraint" destination="kSM-fg-IHB" id="nrd-MK-yrq"/>
|
||||
<outlet property="searchBarView" destination="bsq-3U-VjV" id="x3M-wX-RW8"/>
|
||||
<outlet property="view" destination="iN0-l3-epB" id="csR-cn-S4h"/>
|
||||
</connections>
|
||||
|
||||
@@ -607,14 +607,14 @@
|
||||
inMatrixSession:(MXSession*)mxSession
|
||||
{
|
||||
if (roomParentId) {
|
||||
NSString *parentName = [mxSession roomSummaryWithRoomId:roomParentId].displayname;
|
||||
NSString *parentName = [mxSession roomSummaryWithRoomId:roomParentId].displayName;
|
||||
NSMutableArray<NSString *> *breadcrumbs = [[NSMutableArray alloc] initWithObjects:parentName, nil];
|
||||
|
||||
MXSpace *firstRootAncestor = roomParentId ? [mxSession.spaceService firstRootAncestorForRoomWithId:roomParentId] : nil;
|
||||
NSString *rootName = nil;
|
||||
if (firstRootAncestor)
|
||||
{
|
||||
rootName = [mxSession roomSummaryWithRoomId:firstRootAncestor.spaceId].displayname;
|
||||
rootName = [mxSession roomSummaryWithRoomId:firstRootAncestor.spaceId].displayName;
|
||||
[breadcrumbs insertObject:rootName atIndex:0];
|
||||
}
|
||||
titleView.breadcrumbView.breadcrumbs = breadcrumbs;
|
||||
|
||||
@@ -39,6 +39,7 @@ targets:
|
||||
- target: RiotNSE
|
||||
- target: DesignKit
|
||||
- target: CommonKit
|
||||
- package: AnalyticsEvents
|
||||
- package: Mapbox
|
||||
- package: OrderedCollections
|
||||
- package: SwiftOGG
|
||||
|
||||
@@ -200,11 +200,14 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: BEFORE")
|
||||
self.logMemory()
|
||||
|
||||
NotificationService.backgroundSyncService = MXBackgroundSyncService(withCredentials: userAccount.mxCredentials, persistTokenDataHandler: { persistTokenDataHandler in
|
||||
MXKAccountManager.shared().readAndWriteCredentials(persistTokenDataHandler)
|
||||
}, unauthenticatedHandler: { error, softLogout, refreshTokenAuth, completion in
|
||||
userAccount.handleUnauthenticatedWithError(error, isSoftLogout: softLogout, isRefreshTokenAuth: refreshTokenAuth, andCompletion: completion)
|
||||
})
|
||||
NotificationService.backgroundSyncService = MXBackgroundSyncService(
|
||||
withCredentials: userAccount.mxCredentials,
|
||||
isCryptoSDKEnabled: isCryptoSDKEnabled,
|
||||
persistTokenDataHandler: { persistTokenDataHandler in
|
||||
MXKAccountManager.shared().readAndWriteCredentials(persistTokenDataHandler)
|
||||
}, unauthenticatedHandler: { error, softLogout, refreshTokenAuth, completion in
|
||||
userAccount.handleUnauthenticatedWithError(error, isSoftLogout: softLogout, isRefreshTokenAuth: refreshTokenAuth, andCompletion: completion)
|
||||
})
|
||||
MXLog.debug("[NotificationService] setup: MXBackgroundSyncService init: AFTER")
|
||||
self.logMemory()
|
||||
}
|
||||
@@ -219,10 +222,10 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
/// Determine whether we have switched from using crypto v1 to v2 or vice versa which will require
|
||||
/// rebuilding `MXBackgroundSyncService`
|
||||
private func hasChangedCryptoSDK() -> Bool {
|
||||
guard isCryptoSDKEnabled != RiotSettings.shared.enableCryptoSDK else {
|
||||
guard isCryptoSDKEnabled != MXSDKOptions.sharedInstance().enableCryptoSDK else {
|
||||
return false
|
||||
}
|
||||
isCryptoSDKEnabled = RiotSettings.shared.enableCryptoSDK
|
||||
isCryptoSDKEnabled = MXSDKOptions.sharedInstance().enableCryptoSDK
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -238,7 +241,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
// If a room summary is available, use the displayname for the best attempt title.
|
||||
guard let roomSummary = NotificationService.backgroundSyncService.roomSummary(forRoomId: roomId) else { return }
|
||||
guard let roomDisplayName = roomSummary.displayname else { return }
|
||||
guard let roomDisplayName = roomSummary.displayName else { return }
|
||||
bestAttemptContents[eventId]?.title = roomDisplayName
|
||||
|
||||
// At this stage we don't know the message type, so leave the body as set in didReceive.
|
||||
@@ -380,7 +383,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
var ignoreBadgeUpdate = false
|
||||
var threadIdentifier: String? = roomId
|
||||
let currentUserId = account.mxCredentials.userId
|
||||
let roomDisplayName = roomSummary?.displayname
|
||||
let roomDisplayName = roomSummary?.displayName
|
||||
let pushRule = NotificationService.backgroundSyncService.pushRule(matching: event, roomState: roomState)
|
||||
|
||||
// if the push rule must not be notified we complete and return
|
||||
|
||||
@@ -32,6 +32,7 @@ targets:
|
||||
type: app-extension
|
||||
|
||||
dependencies:
|
||||
- package: AnalyticsEvents
|
||||
- package: DeviceKit
|
||||
- package: DTCoreText
|
||||
|
||||
@@ -45,6 +46,7 @@ targets:
|
||||
- path: ../Config/BuildSettings.swift
|
||||
- path: ../Riot/Utils/DataProtectionHelper.swift
|
||||
- path: ../Config/CommonConfiguration.swift
|
||||
- path: ../Riot/Experiments/
|
||||
- path: ../Riot/Managers/PushNotification/PushNotificationStore.swift
|
||||
- path: ../Riot/Modules/SetPinCode/PinCodePreferences.swift
|
||||
- path: ../Riot/Managers/KeyValueStorage/Extensions/Keychain.swift
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
{
|
||||
for (NSString* pattern in patternsList)
|
||||
{
|
||||
if (cellData.roomSummary.displayname && [cellData.roomSummary.displayname rangeOfString:pattern options:NSCaseInsensitiveSearch].location != NSNotFound)
|
||||
if (cellData.roomSummary.displayName && [cellData.roomSummary.displayName rangeOfString:pattern options:NSCaseInsensitiveSearch].location != NSNotFound)
|
||||
{
|
||||
[self.visibleRoomCellDatas addObject:cellData];
|
||||
break;
|
||||
|
||||
@@ -32,6 +32,7 @@ targets:
|
||||
type: app-extension
|
||||
|
||||
dependencies:
|
||||
- package: AnalyticsEvents
|
||||
- package: DeviceKit
|
||||
- package: DTCoreText
|
||||
|
||||
@@ -52,6 +53,7 @@ targets:
|
||||
- path: ../Riot/Categories/MXRoom+Riot.m
|
||||
- path: ../Config/Configurable.swift
|
||||
- path: ../Config/CommonConfiguration.swift
|
||||
- path: ../Riot/Experiments/
|
||||
- path: ../Riot/Utils/UserNameColorGenerator.swift
|
||||
- path: ../Riot/Categories/MXRoomSummary+Riot.m
|
||||
- path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
|
||||
|
||||
@@ -25,6 +25,6 @@ extension MXRoom: Avatarable {
|
||||
}
|
||||
|
||||
var displayName: String? {
|
||||
summary.displayname
|
||||
summary.displayName
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@ struct LiveLocationSharingViewerViewState: BindableState {
|
||||
/// Live location list items
|
||||
var listItemsViewData: [LiveLocationListItemViewData]
|
||||
|
||||
var showsUserLocation = false
|
||||
/// Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
||||
var showsUserLocationMode: ShowUserLocationMode = .hide
|
||||
|
||||
var isCurrentUserShared: Bool {
|
||||
listItemsViewData.contains { $0.isCurrentUser }
|
||||
|
||||
+9
-1
@@ -211,6 +211,14 @@ class LiveLocationSharingViewerViewModel: LiveLocationSharingViewerViewModelType
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
if the map is currently following the current user's location,
|
||||
we want to switch back to only showing the marker,
|
||||
so the the highlighted shared location can be centered
|
||||
*/
|
||||
if state.showsUserLocationMode == .follow {
|
||||
state.showsUserLocationMode = .show
|
||||
}
|
||||
state.highlightedAnnotation = foundUserAnnotation
|
||||
}
|
||||
|
||||
@@ -234,7 +242,7 @@ class LiveLocationSharingViewerViewModel: LiveLocationSharingViewerViewModelType
|
||||
|
||||
private func showsCurrentUserLocation() {
|
||||
if liveLocationSharingViewerService.requestAuthorizationIfNeeded() {
|
||||
state.showsUserLocation = true
|
||||
state.showsUserLocationMode = .follow
|
||||
} else {
|
||||
state.errorSubject.send(.invalidLocationAuthorization)
|
||||
}
|
||||
|
||||
+4
-2
@@ -40,8 +40,10 @@ class LiveLocationSharingViewerViewModelTests: XCTestCase {
|
||||
let service = MockLiveLocationSharingViewerService(currentUserSharingLocation: false)
|
||||
let viewModel = LiveLocationSharingViewerViewModel(mapStyleURL: BuildSettings.defaultTileServerMapStyleURL, service: service)
|
||||
XCTAssertFalse(viewModel.context.viewState.isCurrentUserShared)
|
||||
XCTAssertFalse(viewModel.context.viewState.showsUserLocation)
|
||||
XCTAssertEqual(viewModel.context.viewState.showsUserLocationMode, .hide)
|
||||
viewModel.context.send(viewAction: .showUserLocation)
|
||||
XCTAssertTrue(viewModel.context.viewState.showsUserLocation)
|
||||
XCTAssertEqual(viewModel.context.viewState.showsUserLocationMode, .follow)
|
||||
viewModel.context.send(viewAction: .tapListItem("@bob:matrix.org"))
|
||||
XCTAssertEqual(viewModel.context.viewState.showsUserLocationMode, .show)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ struct LiveLocationSharingViewer: View {
|
||||
annotations: viewModel.viewState.annotations,
|
||||
highlightedAnnotation: viewModel.viewState.highlightedAnnotation,
|
||||
userAvatarData: nil,
|
||||
showsUserLocation: viewModel.viewState.showsUserLocation,
|
||||
showsUserLocationMode: viewModel.viewState.showsUserLocationMode,
|
||||
userAnnotationCanShowCallout: true,
|
||||
userLocation: Binding.constant(nil),
|
||||
mapCenterCoordinate: Binding.constant(nil),
|
||||
|
||||
@@ -18,6 +18,15 @@ import Combine
|
||||
import Mapbox
|
||||
import SwiftUI
|
||||
|
||||
/*
|
||||
Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
||||
*/
|
||||
enum ShowUserLocationMode {
|
||||
case follow
|
||||
case show
|
||||
case hide
|
||||
}
|
||||
|
||||
struct LocationSharingMapView: UIViewRepresentable {
|
||||
// MARK: - Constants
|
||||
|
||||
@@ -39,8 +48,8 @@ struct LocationSharingMapView: UIViewRepresentable {
|
||||
/// Current user avatar data, used to replace current location annotation view with the user avatar
|
||||
let userAvatarData: AvatarInputProtocol?
|
||||
|
||||
/// True to indicate to show and follow current user location
|
||||
var showsUserLocation = false
|
||||
/// Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
||||
var showsUserLocationMode: ShowUserLocationMode = .hide
|
||||
|
||||
/// True to indicate that a touch on user annotation can show a callout
|
||||
var userAnnotationCanShowCallout = false
|
||||
@@ -75,14 +84,23 @@ struct LocationSharingMapView: UIViewRepresentable {
|
||||
mapView.vc_removeAllAnnotations()
|
||||
mapView.addAnnotations(annotations)
|
||||
|
||||
if let highlightedAnnotation = highlightedAnnotation, !showsUserLocation {
|
||||
mapView.setCenter(highlightedAnnotation.coordinate, zoomLevel: Constants.mapZoomLevel, animated: false)
|
||||
/*
|
||||
if there is an highlighted annotation,
|
||||
and the current user's location it's either hidden or only shown,
|
||||
we can center to the highlighted annotation
|
||||
*/
|
||||
if let highlightedAnnotation = highlightedAnnotation, showsUserLocationMode != .follow {
|
||||
mapView.setCenter(highlightedAnnotation.coordinate, zoomLevel: Constants.mapZoomLevel, animated: true)
|
||||
}
|
||||
|
||||
if showsUserLocation {
|
||||
switch showsUserLocationMode {
|
||||
case .follow:
|
||||
mapView.showsUserLocation = true
|
||||
mapView.userTrackingMode = .follow
|
||||
} else {
|
||||
case .show:
|
||||
mapView.showsUserLocation = true
|
||||
mapView.userTrackingMode = .none
|
||||
case .hide:
|
||||
mapView.showsUserLocation = false
|
||||
mapView.userTrackingMode = .none
|
||||
}
|
||||
|
||||
+2
-2
@@ -80,8 +80,8 @@ struct LocationSharingViewState: BindableState {
|
||||
|
||||
var showLoadingIndicator = false
|
||||
|
||||
/// True to indicate to show and follow current user location
|
||||
var showsUserLocation = false
|
||||
/// Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
||||
var showsUserLocationMode: ShowUserLocationMode = .hide
|
||||
|
||||
/// Used to hide live location sharing features
|
||||
var isLiveLocationSharingEnabled = false
|
||||
|
||||
+3
-3
@@ -40,7 +40,7 @@ class LocationSharingViewModel: LocationSharingViewModelType, LocationSharingVie
|
||||
userAvatarData: avatarData,
|
||||
annotations: [],
|
||||
highlightedAnnotation: nil,
|
||||
showsUserLocation: true,
|
||||
showsUserLocationMode: .follow,
|
||||
isLiveLocationSharingEnabled: isLiveLocationSharingEnabled)
|
||||
|
||||
super.init(initialViewState: viewState)
|
||||
@@ -73,7 +73,7 @@ class LocationSharingViewModel: LocationSharingViewModelType, LocationSharingVie
|
||||
|
||||
completion?(.share(latitude: pinLocation.latitude, longitude: pinLocation.longitude, coordinateType: .pin))
|
||||
case .goToUserLocation:
|
||||
state.showsUserLocation = true
|
||||
state.showsUserLocationMode = .follow
|
||||
state.isPinDropSharing = false
|
||||
case .startLiveSharing:
|
||||
startLiveLocationSharing()
|
||||
@@ -81,7 +81,7 @@ class LocationSharingViewModel: LocationSharingViewModelType, LocationSharingVie
|
||||
state.bindings.showingTimerSelector = false
|
||||
completion?(.shareLiveLocation(timeout: timeout.rawValue))
|
||||
case .userDidPan:
|
||||
state.showsUserLocation = false
|
||||
state.showsUserLocationMode = .hide
|
||||
state.isPinDropSharing = true
|
||||
case .mapCreditsDidTap:
|
||||
state.bindings.showMapCreditsSheet.toggle()
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ struct LocationSharingView: View {
|
||||
annotations: context.viewState.annotations,
|
||||
highlightedAnnotation: context.viewState.highlightedAnnotation,
|
||||
userAvatarData: context.viewState.userAvatarData,
|
||||
showsUserLocation: context.viewState.showsUserLocation,
|
||||
showsUserLocationMode: context.viewState.showsUserLocationMode,
|
||||
userLocation: $context.userLocation,
|
||||
mapCenterCoordinate: $context.pinLocation,
|
||||
errorSubject: context.viewState.errorSubject,
|
||||
|
||||
+2
-1
@@ -43,7 +43,8 @@ struct StaticLocationViewingViewState: BindableState {
|
||||
/// Shared annotation to display existing location
|
||||
let sharedAnnotation: LocationAnnotation
|
||||
|
||||
var showsUserLocation = false
|
||||
/// Behavior mode of the current user's location, can be hidden, only shown and shown following the user
|
||||
var showsUserLocationMode: ShowUserLocationMode = .hide
|
||||
|
||||
var showLoadingIndicator = false
|
||||
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ class StaticLocationViewingViewModel: StaticLocationViewingViewModelType, Static
|
||||
|
||||
private func showsCurrentUserLocation() {
|
||||
if staticLocationSharingViewerService.requestAuthorizationIfNeeded() {
|
||||
state.showsUserLocation = true
|
||||
state.showsUserLocationMode = .follow
|
||||
} else {
|
||||
state.errorSubject.send(.invalidLocationAuthorization)
|
||||
}
|
||||
|
||||
+2
-2
@@ -81,9 +81,9 @@ class StaticLocationViewingViewModelTests: XCTestCase {
|
||||
|
||||
func testToggleShowUserLocation() {
|
||||
let viewModel = buildViewModel()
|
||||
XCTAssertFalse(viewModel.context.viewState.showsUserLocation)
|
||||
XCTAssertEqual(viewModel.context.viewState.showsUserLocationMode, .hide)
|
||||
viewModel.context.send(viewAction: .showUserLocation)
|
||||
XCTAssertTrue(viewModel.context.viewState.showsUserLocation)
|
||||
XCTAssertEqual(viewModel.context.viewState.showsUserLocationMode, .follow)
|
||||
}
|
||||
|
||||
private func buildViewModel() -> StaticLocationViewingViewModel {
|
||||
|
||||
+1
-1
@@ -34,7 +34,7 @@ struct StaticLocationView: View {
|
||||
annotations: [viewModel.viewState.sharedAnnotation],
|
||||
highlightedAnnotation: viewModel.viewState.sharedAnnotation,
|
||||
userAvatarData: nil,
|
||||
showsUserLocation: viewModel.viewState.showsUserLocation,
|
||||
showsUserLocationMode: viewModel.viewState.showsUserLocationMode,
|
||||
userLocation: Binding.constant(nil),
|
||||
mapCenterCoordinate: Binding.constant(nil),
|
||||
errorSubject: viewModel.viewState.errorSubject)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user