Merge pull request #7414 from vector-im/release/1.10.4/release

Release 1.10.4
This commit is contained in:
Doug
2023-03-07 13:43:49 +00:00
committed by GitHub
118 changed files with 1619 additions and 596 deletions
+22
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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
+2 -8
View File
@@ -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? {
-42
View File
@@ -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")
}
}
+2 -4
View File
@@ -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
View File
@@ -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"
}
}
+13 -1
View File
@@ -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";
+1 -1
View File
@@ -27,7 +27,7 @@
{
[mxkImageView vc_setRoomAvatarImageWith:self.avatar
roomId:self.roomId
displayName:self.displayname
displayName:self.displayName
mediaManager:self.mxSession.mediaManager];
}
+97
View File
@@ -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)
}
}
+50
View File
@@ -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
}
+43 -3
View File
@@ -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")
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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];;
+13 -17
View File
@@ -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
}
}
+1 -1
View File
@@ -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];
+1 -1
View File
@@ -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];
}
+1 -1
View File
@@ -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()
@@ -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"/>
@@ -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
@@ -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"/>
@@ -97,7 +97,6 @@ final class KeyVerificationVerifiedViewController: UIViewController {
informationText = VectorL10n.keyVerificationVerifiedUserInformation
}
self.title = self.verificationKind.verificationTitle
self.titleLabel.text = bodyTitle
self.informationLabel.text = informationText
@@ -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"/>
@@ -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
@@ -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"/>
@@ -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))
}
@@ -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))
@@ -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"/>
@@ -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
@@ -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)
{
@@ -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? {
+3 -3
View File
@@ -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,
+2
View File
@@ -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.
+70 -7
View File
@@ -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
+12 -18
View File
@@ -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>
+2 -2
View File
@@ -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;
+1
View File
@@ -39,6 +39,7 @@ targets:
- target: RiotNSE
- target: DesignKit
- target: CommonKit
- package: AnalyticsEvents
- package: Mapbox
- package: OrderedCollections
- package: SwiftOGG
+12 -9
View File
@@ -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
+2
View File
@@ -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
+1 -1
View File
@@ -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;
+2
View File
@@ -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
}
}
@@ -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 }
@@ -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)
}
@@ -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)
}
}
@@ -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
}
@@ -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
@@ -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()
@@ -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,
@@ -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
@@ -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)
}
@@ -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 {
@@ -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