mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge branch 'develop' into element_4014
# Conflicts: # Riot.xcodeproj/project.pbxproj # Riot/Modules/Room/DataSources/RoomDataSource.m
This commit is contained in:
commit
419efd886a
76 changed files with 9801 additions and 8280 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -27,7 +27,8 @@ out/
|
|||
#
|
||||
Pods/
|
||||
|
||||
# Do not track our workspace since it is created by CocoaPods
|
||||
## Ignore project files as we generate them with xcodegen (https://github.com/yonaskolb/XcodeGen)
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
|
||||
# Fastlane
|
||||
|
|
31
CHANGES.rst
31
CHANGES.rst
|
@ -22,6 +22,37 @@ Changes to be released in next version
|
|||
Others
|
||||
*
|
||||
|
||||
Changes in 1.2.2 (2021-02-24)
|
||||
=================================================
|
||||
|
||||
✨ Features
|
||||
* Enable encryption for accounts, contacts and keys in the crypto database (#3867).
|
||||
|
||||
🙌 Improvements
|
||||
* Home: Show room directory on join room action (#3775).
|
||||
* RoomVC: Add quick actions in timeline on room creation (#3776).
|
||||
|
||||
🐛 Bugfix
|
||||
*
|
||||
|
||||
⚠️ API Changes
|
||||
*
|
||||
|
||||
🗣 Translations
|
||||
*
|
||||
|
||||
🧱 Build
|
||||
* XcodeGen: .xcodeproj files are now built from readable yml file: [New Build instructions](README.md#build-instructions) (#3812).
|
||||
* Podfile: Use MatrixKit for all targets and remove MatrixKit/AppExtension.
|
||||
* Fastlane: Use the "New Build System" to build releases.
|
||||
* Fastlane: Re-enable parallelised builds.
|
||||
|
||||
Others
|
||||
*
|
||||
|
||||
Improvements:
|
||||
* Upgrade MatrixKit version ([v0.14.2](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.14.2)).
|
||||
|
||||
Changes in 1.2.1 (2021-02-12)
|
||||
=================================================
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
//
|
||||
// Copyright 2020 Vector Creations 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 MatrixKit
|
||||
|
||||
/// AppConfig is Config plus configurations dedicated to the app
|
||||
class AppConfiguration: Config {
|
||||
|
||||
// MARK: - Global settings
|
||||
|
||||
override func setupSettings() {
|
||||
super.setupSettings()
|
||||
setupAppSettings()
|
||||
}
|
||||
|
||||
private func setupAppSettings() {
|
||||
// Enable long press on event in bubble cells
|
||||
MXKRoomBubbleTableViewCell.disableLongPressGesture(onEvent: false)
|
||||
|
||||
// Each room member will be considered as a potential contact.
|
||||
MXKContactManager.shared().contactManagerMXRoomSource = MXKContactManagerMXRoomSource.all
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Per matrix session settings
|
||||
|
||||
override func setupSettings(for matrixSession: MXSession) {
|
||||
super.setupSettings(for: matrixSession)
|
||||
setupWidgetReadReceipts(for: matrixSession)
|
||||
}
|
||||
|
||||
private func setupWidgetReadReceipts(for matrixSession: MXSession) {
|
||||
var acknowledgableEventTypes = matrixSession.acknowledgableEventTypes ?? []
|
||||
acknowledgableEventTypes.append(kWidgetMatrixEventTypeString)
|
||||
acknowledgableEventTypes.append(kWidgetModularEventTypeString)
|
||||
|
||||
matrixSession.acknowledgableEventTypes = acknowledgableEventTypes
|
||||
}
|
||||
|
||||
}
|
43
Config/AppIdentifiers.xcconfig
Normal file
43
Config/AppIdentifiers.xcconfig
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Copyright 2021 Vector Creations 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.
|
||||
//
|
||||
|
||||
|
||||
// App identity
|
||||
BUNDLE_DISPLAY_NAME = Element
|
||||
BASE_BUNDLE_IDENTIFIER = im.vector.app
|
||||
APPLICATION_GROUP_IDENTIFIER = group.im.vector
|
||||
|
||||
// Version
|
||||
MARKETING_VERSION = 1.2.3
|
||||
CURRENT_PROJECT_VERSION = 1.2.3
|
||||
|
||||
|
||||
// Team
|
||||
DEVELOPMENT_TEAM = 7J4U792NQT
|
||||
|
||||
|
||||
// Provisioning profiles
|
||||
RIOT_PROVISIONING_PROFILE_SPECIFIER = Vector App Store
|
||||
RIOT_PROVISIONING_PROFILE = f65e7447-b8a3-46cc-8fba-fa60e55e2511
|
||||
|
||||
NSE_PROVISIONING_PROFILE_SPECIFIER = "Vector NSE: App Store"
|
||||
NSE_PROVISIONING_PROFILE = 31dc9316-e029-47fd-81f5-778db07d76a2
|
||||
|
||||
SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER = "Vector Share Extension: App Store"
|
||||
SHARE_EXTENSION_PROVISIONING_PROFILE = 1a3be143-50c7-4ae2-834e-00596a053141
|
||||
|
||||
SIRI_INTENTS_PROVISIONING_PROFILE_SPECIFIER = "Vector Siri Intents: App Store"
|
||||
SIRI_INTENTS_PROVISIONING_PROFILE = 18a66f93-ffe1-4008-b343-58350cc65023
|
|
@ -72,6 +72,9 @@ class CommonConfiguration: NSObject, Configurable {
|
|||
|
||||
// Disable key backup on common
|
||||
sdkOptions.enableKeyBackupWhenStartingMXCrypto = false
|
||||
|
||||
// Configure key provider delegate
|
||||
MXKeyProvider.sharedInstance().delegate = EncryptionKeyManager.shared
|
||||
}
|
||||
|
||||
|
||||
|
|
39
Config/Project-Debug.xcconfig
Normal file
39
Config/Project-Debug.xcconfig
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright 2021 Vector Creations 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.
|
||||
//
|
||||
|
||||
// This file defines project settings for Debug.
|
||||
// Targets (App, extensions) xcconfig files automatically include it for Debug build.
|
||||
|
||||
#include "Project.xcconfig"
|
||||
#include "Project-Warnings.xcconfig"
|
||||
|
||||
ONLY_ACTIVE_ARCH = YES
|
||||
COPY_PHASE_STRIP = NO
|
||||
ENABLE_TESTABILITY = YES
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE
|
||||
|
||||
SWIFT_OPTIMIZATION_LEVEL = -Onone
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG
|
||||
|
||||
GCC_OPTIMIZATION_LEVEL = 0
|
||||
GCC_DYNAMIC_NO_PIC = NO
|
||||
GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited)
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO
|
||||
|
||||
|
||||
// Code signing: Use the development team for all targets
|
||||
CODE_SIGN_IDENTITY = iPhone Developer
|
||||
CODE_SIGN_STYLE = Automatic
|
33
Config/Project-Release.xcconfig
Normal file
33
Config/Project-Release.xcconfig
Normal file
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Copyright 2021 Vector Creations 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.
|
||||
//
|
||||
|
||||
// This file defines project settings for Release.
|
||||
// Targets (App, extensions) xcconfig files automatically include it for Release build.
|
||||
|
||||
#include "Project.xcconfig"
|
||||
#include "Project-Warnings.xcconfig"
|
||||
|
||||
COPY_PHASE_STRIP = YES
|
||||
ENABLE_NS_ASSERTIONS = NO
|
||||
MTL_ENABLE_DEBUG_INFO = NO
|
||||
VALIDATE_PRODUCT = YES
|
||||
|
||||
SWIFT_COMPILATION_MODE = wholemodule
|
||||
|
||||
|
||||
// Code signing: Manual
|
||||
CODE_SIGN_IDENTITY = iPhone Distribution
|
||||
CODE_SIGN_STYLE = Manual
|
48
Config/Project-Warnings.xcconfig
Normal file
48
Config/Project-Warnings.xcconfig
Normal file
|
@ -0,0 +1,48 @@
|
|||
|
||||
//
|
||||
// Copyright 2021 Vector Creations 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.
|
||||
//
|
||||
|
||||
// This file defines warning declarations for the base project.
|
||||
// Targets (App, extensions) xcconfig files automatically include it.
|
||||
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES
|
||||
CLANG_WARN_BOOL_CONVERSION = YES
|
||||
CLANG_WARN_COMMA = YES
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES
|
||||
CLANG_WARN_EMPTY_BODY = YES
|
||||
CLANG_WARN_ENUM_CONVERSION = YES
|
||||
CLANG_WARN_INFINITE_RECURSION = YES
|
||||
CLANG_WARN_INT_CONVERSION = YES
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
|
||||
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE
|
||||
GCC_WARN_UNUSED_FUNCTION = YES
|
||||
GCC_WARN_UNUSED_VARIABLE = YES
|
48
Config/Project.xcconfig
Normal file
48
Config/Project.xcconfig
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Copyright 2020 Vector Creations 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.
|
||||
//
|
||||
|
||||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
|
||||
// This file defines base project settings.
|
||||
// Targets (App, extensions) xcconfig files automatically include it.
|
||||
|
||||
// Application constants
|
||||
KEYCHAIN_ACCESS_GROUP = $(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER).keychain.shared
|
||||
|
||||
// Build settings
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0
|
||||
SDKROOT = iphoneos
|
||||
TARGETED_DEVICE_FAMILY = 1,2
|
||||
SWIFT_VERSION = 5.3.1
|
||||
ENABLE_BITCODE = NO
|
||||
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks
|
||||
ALWAYS_SEARCH_USER_PATHS = NO
|
||||
DEFINES_MODULE = YES
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES
|
||||
GCC_NO_COMMON_BLOCKS = YES
|
||||
|
||||
// Make Xcode 12 and fastlane(xcodebuild) happy while some pods are not updated
|
||||
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
|
||||
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES
|
||||
CLANG_ANALYZER_NONNULL = YES
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE
|
||||
|
||||
CLANG_ENABLE_MODULES = YES
|
||||
CLANG_ENABLE_OBJC_ARC = YES
|
|
@ -4,7 +4,7 @@
|
|||
<dict>
|
||||
<key>FILEHEADER</key>
|
||||
<string>
|
||||
// Copyright 2020 New Vector Ltd
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
45
INSTALL.md
45
INSTALL.md
|
@ -7,6 +7,7 @@ To build Element iOS project you need:
|
|||
- Xcode 12.1+.
|
||||
- [Ruby](https://www.ruby-lang.org/), a dynamic programming language used by several build tools.
|
||||
- [CocoaPods](https://cocoapods.org), library dependencies manager for Xcode projects.
|
||||
- [XcodeGen](https://github.com/yonaskolb/XcodeGen), an Xcode project generator.
|
||||
- [bundler](https://bundler.io/) (optional), is also a dependency manager used to manage build tools dependency (CocoaPods, Fastlane).
|
||||
|
||||
### Install Ruby
|
||||
|
@ -20,17 +21,25 @@ If you do not want to grant the ruby package manager, [RubyGems](https://rubygem
|
|||
To install CocoaPods you can grab the right version by using `bundler` (recommended) or you can directly install it with RubyGems:
|
||||
|
||||
```
|
||||
gem install cocoapods
|
||||
$ gem install cocoapods
|
||||
```
|
||||
|
||||
In the last case please ensure that you are using the same version as indicated at the end of the `Podfile.lock` file.
|
||||
|
||||
### Install XcodeGen
|
||||
|
||||
You can directly install XcodeGen with [Homebrew](https://brew.sh) or RubyGems:
|
||||
|
||||
```
|
||||
$ brew install xcodegen
|
||||
```
|
||||
|
||||
### Install bundler (optional)
|
||||
|
||||
By using `bundler` you will ensure to use the right versions of build tools used to build and deliver the project. You can find dependency definitions in the `Gemfile`. To install `bundler`:
|
||||
|
||||
```
|
||||
gem install bundler
|
||||
$ gem install bundler
|
||||
```
|
||||
|
||||
## Choose Matrix SDKs version to build
|
||||
|
@ -51,32 +60,39 @@ If you want to build last development code you have to checkout the develop bran
|
|||
|
||||
- **Build specific branch of Kit and SDK and modify Element project only**
|
||||
|
||||
If you want to build a specific branch for the MatrixKit and the MatrixSDK you have to indicate them using a dictionary like this: `$matrixKitVersion = {'kit branch name' => 'sdk branch name'}`.
|
||||
If you want to build a specific branch for the MatrixKit and the MatrixSDK you have to indicate them using a dictionary like this: `$matrixKitVersion = {'kit_branch_name' => 'sdk_branch_name'}`.
|
||||
|
||||
- **Build any branch and be able to modify MatrixKit and MatrixSDK locally**
|
||||
|
||||
If you want to modify MatrixKit and/or MatrixSDK locally and see the result in Element project you have to uncommment `$matrixKitVersion = :local` in the `Podfile`.
|
||||
But before you have to checkout [MatrixKit](https://github.com/matrix-org/matrix-ios-kit) repository in `../matrix-ios-kit` and [MatrixSDK](https://github.com/matrix-org/matrix-ios-sdk) in `../matrix-ios-sdk` locally relatively to your Element iOS project folder.
|
||||
Be sure to use compatible branches for Element iOS, MatrixKit and MatrixSDK. For example if you want to modify Element iOS from develop branch use MatrixKit and MatrixSDK develop branches and then make your modifications.
|
||||
Be sure to use compatible branches for Element iOS, MatrixKit and MatrixSDK. For example, if you want to modify Element iOS from develop branch, use MatrixKit and MatrixSDK develop branches and then make your modifications.
|
||||
|
||||
**Important**: By working with local pods (development pods) you will need to use legacy build system in Xcode, to have your local changes taken into account. To enable it go to Xcode menu and select `File > Workspace Settings… > Build System` and then choose `Legacy Build System`.
|
||||
|
||||
### Modify `$matrixKitVersion` after installation of dependencies
|
||||
### `$matrixKitVersion` Modification
|
||||
|
||||
Assuming you have already completed the **Install dependencies** instructions from **Build** section below.
|
||||
Every time you change the `$matrixKitVersion` variable in the `Podfile`, you have to run the `pod install` command again.
|
||||
|
||||
Each time you edit `$matrixKitVersion` variable in the `Podfile` you will have to run the `pod install` command.
|
||||
|
||||
## Build
|
||||
|
||||
## Generate Xcode project
|
||||
|
||||
In order to get rid of git conflicts, the `Riot.xcodeproj` is not pushed into the git repository anymore but generated using `XcodeGen`. To generate the `xcodeproj` file simply run the following command line from the root folder :
|
||||
|
||||
```
|
||||
$ xcodegen
|
||||
```
|
||||
|
||||
|
||||
### Install dependencies
|
||||
|
||||
Before opening the Element Xcode workspace, you need to install dependencies via CocoaPods.
|
||||
Then, before opening the Element Xcode workspace, you need to install dependencies via CocoaPods.
|
||||
|
||||
To be sure to use the right CocoaPods version you can use `bundler`:
|
||||
|
||||
```
|
||||
$ cd Riot
|
||||
$ bundle install
|
||||
$ bundle exec pod install
|
||||
```
|
||||
|
@ -84,12 +100,12 @@ $ bundle exec pod install
|
|||
Or if you prefer to use directly CocoaPods:
|
||||
|
||||
```
|
||||
$ cd Riot
|
||||
$ pod install
|
||||
```
|
||||
|
||||
This will load all dependencies for the Element source code, including [MatrixKit](https://github.com/matrix-org/matrix-ios-kit)
|
||||
and [MatrixSDK](https://github.com/matrix-org/matrix-ios-sdk).
|
||||
and [MatrixSDK](https://github.com/matrix-org/matrix-ios-sdk).
|
||||
|
||||
|
||||
### Open workspace
|
||||
|
||||
|
@ -103,7 +119,10 @@ $ open Riot.xcworkspace
|
|||
|
||||
### Configure project
|
||||
|
||||
You may need to change the bundle identifier and app group identifier to be unique to get Xcode to build the app. Make sure to change the bundle identifier, application group identifier and app name in the `Config/Common.xcconfig` file to your new identifiers.
|
||||
You may need to change the bundle identifier and app group identifier to be unique to get Xcode to build the app. Make sure to change the bundle identifier, application group identifier and app name in the `project.yml` file to your new identifiers.
|
||||
|
||||
Each target has its own YAML file in the folder Targets folder.
|
||||
|
||||
|
||||
## Generate IPA
|
||||
|
||||
|
@ -142,5 +161,3 @@ Or you can use the wrapper script located at `/Tools/Release/buildRelease.sh`. F
|
|||
And then indicate a branch or a tag like this:
|
||||
|
||||
`$ ./buildRelease.sh <tag or branch>`
|
||||
|
||||
|
||||
|
|
19
Podfile
19
Podfile
|
@ -11,7 +11,7 @@ use_frameworks!
|
|||
# - `{ {kit spec hash} => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for each repo. Used by Fastfile during CI
|
||||
#
|
||||
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
|
||||
$matrixKitVersion = '= 0.14.1'
|
||||
$matrixKitVersion = '= 0.14.2'
|
||||
# $matrixKitVersion = :local
|
||||
# $matrixKitVersion = {'develop' => 'develop'}
|
||||
|
||||
|
@ -32,19 +32,13 @@ $matrixKitVersionSpec = $matrixKitVersion
|
|||
$matrixSDKVersionSpec = {}
|
||||
end
|
||||
|
||||
# Method to import the right MatrixKit flavour
|
||||
# Method to import the MatrixKit
|
||||
def import_MatrixKit
|
||||
pod 'MatrixSDK', $matrixSDKVersionSpec
|
||||
pod 'MatrixSDK/JingleCallStack', $matrixSDKVersionSpec
|
||||
pod 'MatrixKit', $matrixKitVersionSpec
|
||||
end
|
||||
|
||||
# Method to import the right MatrixKit/AppExtension flavour
|
||||
def import_MatrixKitAppExtension
|
||||
pod 'MatrixSDK', $matrixSDKVersionSpec
|
||||
pod 'MatrixKit/AppExtension', $matrixKitVersionSpec
|
||||
end
|
||||
|
||||
########################################
|
||||
|
||||
abstract_target 'RiotPods' do
|
||||
|
@ -81,15 +75,15 @@ abstract_target 'RiotPods' do
|
|||
end
|
||||
|
||||
target "RiotShareExtension" do
|
||||
import_MatrixKitAppExtension
|
||||
import_MatrixKit
|
||||
end
|
||||
|
||||
target "SiriIntents" do
|
||||
import_MatrixKitAppExtension
|
||||
import_MatrixKit
|
||||
end
|
||||
|
||||
target "RiotNSE" do
|
||||
import_MatrixKitAppExtension
|
||||
import_MatrixKit
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -111,6 +105,9 @@ post_install do |installer|
|
|||
if target.name.include? 'ReadMoreTextView'
|
||||
config.build_settings['SWIFT_VERSION'] = '5.2'
|
||||
end
|
||||
|
||||
# Stop Xcode 12 complaining about old IPHONEOS_DEPLOYMENT_TARGET from pods
|
||||
config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
59
Podfile.lock
59
Podfile.lock
|
@ -18,7 +18,7 @@ PODS:
|
|||
- BlueECC (1.2.5)
|
||||
- BlueRSA (1.0.34)
|
||||
- DGCollectionViewLeftAlignFlowLayout (1.0.4)
|
||||
- Down (0.9.4)
|
||||
- Down (0.9.5)
|
||||
- DTCoreText (1.6.25):
|
||||
- DTCoreText/Core (= 1.6.25)
|
||||
- DTFoundation/Core (~> 1.7.5)
|
||||
|
@ -30,11 +30,6 @@ PODS:
|
|||
- DTFoundation/DTAnimatedGIF (~> 1.7.5)
|
||||
- DTFoundation/DTHTMLParser (~> 1.7.5)
|
||||
- DTFoundation/UIKit (~> 1.7.5)
|
||||
- DTCoreText/Extension (1.6.25):
|
||||
- DTFoundation/Core (~> 1.7.5)
|
||||
- DTFoundation/DTAnimatedGIF (~> 1.7.5)
|
||||
- DTFoundation/DTHTMLParser (~> 1.7.5)
|
||||
- DTFoundation/UIKit (~> 1.7.5)
|
||||
- DTFoundation/Core (1.7.16)
|
||||
- DTFoundation/DTAnimatedGIF (1.7.16)
|
||||
- DTFoundation/DTHTMLParser (1.7.16):
|
||||
|
@ -60,42 +55,35 @@ PODS:
|
|||
- MatomoTracker (7.2.2):
|
||||
- MatomoTracker/Core (= 7.2.2)
|
||||
- MatomoTracker/Core (7.2.2)
|
||||
- MatrixKit (0.14.1):
|
||||
- MatrixKit (0.14.2):
|
||||
- Down (~> 0.9.3)
|
||||
- DTCoreText (~> 1.6.23)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixKit/Core (= 0.14.1)
|
||||
- MatrixSDK (= 0.18.1)
|
||||
- MatrixKit/AppExtension (0.14.1):
|
||||
- Down (~> 0.9.3)
|
||||
- DTCoreText (~> 1.6.23)
|
||||
- DTCoreText/Extension
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.18.1)
|
||||
- MatrixKit/Core (0.14.1):
|
||||
- MatrixKit/Core (= 0.14.2)
|
||||
- MatrixSDK (= 0.18.2)
|
||||
- MatrixKit/Core (0.14.2):
|
||||
- Down (~> 0.9.3)
|
||||
- DTCoreText (~> 1.6.23)
|
||||
- HPGrowingTextView (~> 1.1)
|
||||
- libPhoneNumber-iOS (~> 0.9.13)
|
||||
- MatrixSDK (= 0.18.1)
|
||||
- MatrixSDK (0.18.1):
|
||||
- MatrixSDK/Core (= 0.18.1)
|
||||
- MatrixSDK/Core (0.18.1):
|
||||
- MatrixSDK (= 0.18.2)
|
||||
- MatrixSDK (0.18.2):
|
||||
- MatrixSDK/Core (= 0.18.2)
|
||||
- MatrixSDK/Core (0.18.2):
|
||||
- AFNetworking (~> 4.0.0)
|
||||
- GZIP (~> 1.3.0)
|
||||
- libbase58 (~> 0.1.4)
|
||||
- OLMKit (~> 3.1.0)
|
||||
- OLMKit (~> 3.2.2)
|
||||
- Realm (= 10.1.4)
|
||||
- MatrixSDK/JingleCallStack (0.18.1):
|
||||
- MatrixSDK/JingleCallStack (0.18.2):
|
||||
- JitsiMeetSDK (= 3.1.0)
|
||||
- MatrixSDK/Core
|
||||
- OLMKit (3.1.0):
|
||||
- OLMKit/olmc (= 3.1.0)
|
||||
- OLMKit/olmcpp (= 3.1.0)
|
||||
- OLMKit/olmc (3.1.0)
|
||||
- OLMKit/olmcpp (3.1.0)
|
||||
- OLMKit (3.2.2):
|
||||
- OLMKit/olmc (= 3.2.2)
|
||||
- OLMKit/olmcpp (= 3.2.2)
|
||||
- OLMKit/olmc (3.2.2)
|
||||
- OLMKit/olmcpp (3.2.2)
|
||||
- ReadMoreTextView (3.0.1)
|
||||
- Realm (10.1.4):
|
||||
- Realm/Headers (= 10.1.4)
|
||||
|
@ -127,8 +115,7 @@ DEPENDENCIES:
|
|||
- KeychainAccess (~> 4.2.1)
|
||||
- KTCenterFlowLayout (~> 1.3.1)
|
||||
- MatomoTracker (~> 7.2.2)
|
||||
- MatrixKit (= 0.14.1)
|
||||
- MatrixKit/AppExtension (= 0.14.1)
|
||||
- MatrixKit (= 0.14.2)
|
||||
- MatrixSDK
|
||||
- MatrixSDK/JingleCallStack
|
||||
- OLMKit
|
||||
|
@ -183,7 +170,7 @@ SPEC CHECKSUMS:
|
|||
BlueECC: 0d18e93347d3ec6d41416de21c1ffa4d4cd3c2cc
|
||||
BlueRSA: 6f9776d62d9773502415a7db3bcbb2bbb3f71fc3
|
||||
DGCollectionViewLeftAlignFlowLayout: a0fa58797373ded039cafba8133e79373d048399
|
||||
Down: 276f2c3eeeaf30345873bdad25f44b2640fcfa3a
|
||||
Down: 7321a72d0747ed0061dce948bcff518fcb6df2bd
|
||||
DTCoreText: e92f4cf6b36d9d71ce4436d12cf089d74ab0596b
|
||||
DTFoundation: e7781d9fd2f202bfd451fbbf8cab71ce83b46498
|
||||
FlowCommoniOS: e9353819a19764c8cafd3fa7efb98b00c9f68e7e
|
||||
|
@ -199,9 +186,9 @@ SPEC CHECKSUMS:
|
|||
LoggerAPI: ad9c4a6f1e32f518fdb43a1347ac14d765ab5e3d
|
||||
Logging: beeb016c9c80cf77042d62e83495816847ef108b
|
||||
MatomoTracker: a59ec4da0f580be57bdc6baa708a71a86532a832
|
||||
MatrixKit: 267c3235abae2e3878e41a57bda32ec4899118e6
|
||||
MatrixSDK: 7d5faf810eab02a189df64aef28583c8bed81f5c
|
||||
OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639
|
||||
MatrixKit: fe2cb13d657005c04c681e85a20566fe50681edf
|
||||
MatrixSDK: b37cb20c40c77d1f64af2427f3c547611f8b5ec8
|
||||
OLMKit: 20d1c564033a1ae7148f8f599378d4c798363905
|
||||
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
|
||||
Realm: 80f4fb2971ccb9adc27a47d0955ae8e533a7030b
|
||||
Reusable: 53a9acf5c536f229b31b5865782414b508252ddb
|
||||
|
@ -212,6 +199,6 @@ SPEC CHECKSUMS:
|
|||
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
|
||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||
|
||||
PODFILE CHECKSUM: 951b047e23b1755baf9e33e5fcbda13272f6becc
|
||||
PODFILE CHECKSUM: ccdf4fe07649c999bdcab6812125f610580edb47
|
||||
|
||||
COCOAPODS: 1.10.0
|
||||
COCOAPODS: 1.10.1
|
||||
|
|
14
README.md
14
README.md
|
@ -26,11 +26,19 @@ You can try last beta build by accessing our [TestFlight Public Link](https://te
|
|||
|
||||
## Build instructions
|
||||
|
||||
To build the application please refer to the [installation guide](./INSTALL.md).
|
||||
If you have already everything installed, opening the project workspace in Xcode should be as easy as:
|
||||
|
||||
```
|
||||
$ xcodegen # Create the xcodeproj with all project source files
|
||||
$ pod install # Create the xcworkspace with all project dependencies
|
||||
$ open Riot.xcworkspace # Open Xcode
|
||||
```
|
||||
|
||||
Else, you can visit our [installation guide](./INSTALL.md). This guide also offers more details and advanced usage like using [MatrixSDK](https://github.com/matrix-org/matrix-ios-sdk) and [MatrixKit](https://github.com/matrix-org/matrix-ios-kit) in their development versions.
|
||||
|
||||
## Contributing
|
||||
|
||||
If you want to contribute to Element iOS code or translations please refer to the [contribution guide](CONTRIBUTING.md).
|
||||
If you want to contribute to Element iOS code or translations, go to the [contribution guide](CONTRIBUTING.md).
|
||||
|
||||
## Support
|
||||
|
||||
|
@ -42,7 +50,7 @@ If after your research you still have a question, ask at [#element-ios:matrix.or
|
|||
|
||||
Copyright (c) 2014-2017 OpenMarket Ltd
|
||||
Copyright (c) 2017 Vector Creations Ltd
|
||||
Copyright (c) 2017-2020 New Vector Ltd
|
||||
Copyright (c) 2017-2021 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License in the [LICENSE](LICENSE) file, or at:
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0940"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "NO"
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
|
@ -14,39 +14,27 @@
|
|||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F094A9A11B78D8F000B1FBBF"
|
||||
BlueprintIdentifier = "57B13CC4586E9D43ED24DE1E"
|
||||
BuildableName = "Riot.app"
|
||||
BlueprintName = "Riot"
|
||||
ReferencedContainer = "container:Riot.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F094A9BD1B78D8F000B1FBBF"
|
||||
BuildableName = "RiotTests.xctest"
|
||||
BlueprintName = "RiotTests"
|
||||
ReferencedContainer = "container:Riot.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
onlyGenerateCoverageForSpecifiedTargets = "NO"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
disableMainThreadChecker = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F094A9BD1B78D8F000B1FBBF"
|
||||
BlueprintIdentifier = "287703423C2C302524E92C03"
|
||||
BuildableName = "RiotTests.xctest"
|
||||
BlueprintName = "RiotTests"
|
||||
ReferencedContainer = "container:Riot.xcodeproj">
|
||||
|
@ -56,14 +44,14 @@
|
|||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F094A9A11B78D8F000B1FBBF"
|
||||
BlueprintIdentifier = "57B13CC4586E9D43ED24DE1E"
|
||||
BuildableName = "Riot.app"
|
||||
BlueprintName = "Riot"
|
||||
ReferencedContainer = "container:Riot.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<CommandLineArguments>
|
||||
</CommandLineArguments>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
|
@ -74,19 +62,20 @@
|
|||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
allowLocationSimulation = "YES"
|
||||
disableMainThreadChecker = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F094A9A11B78D8F000B1FBBF"
|
||||
BlueprintIdentifier = "57B13CC4586E9D43ED24DE1E"
|
||||
BuildableName = "Riot.app"
|
||||
BlueprintName = "Riot"
|
||||
ReferencedContainer = "container:Riot.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<CommandLineArguments>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
|
@ -98,12 +87,14 @@
|
|||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "F094A9A11B78D8F000B1FBBF"
|
||||
BlueprintIdentifier = "57B13CC4586E9D43ED24DE1E"
|
||||
BuildableName = "Riot.app"
|
||||
BlueprintName = "Riot"
|
||||
ReferencedContainer = "container:Riot.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
</CommandLineArguments>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
23
Riot/Assets/Images.xcassets/Room/add_participants.imageset/Contents.json
vendored
Normal file
23
Riot/Assets/Images.xcassets/Room/add_participants.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "add_participants.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "add_participants@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "add_participants@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
BIN
Riot/Assets/Images.xcassets/Room/add_participants.imageset/add_participants.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Room/add_participants.imageset/add_participants.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 770 B |
BIN
Riot/Assets/Images.xcassets/Room/add_participants.imageset/add_participants@2x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Room/add_participants.imageset/add_participants@2x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
BIN
Riot/Assets/Images.xcassets/Room/add_participants.imageset/add_participants@3x.png
vendored
Normal file
BIN
Riot/Assets/Images.xcassets/Room/add_participants.imageset/add_participants@3x.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
|
@ -1581,3 +1581,25 @@ Tap the + to start adding people.";
|
|||
|
||||
"invite_friends_action" = "Invite friends to %@";
|
||||
"invite_friends_share_text" = "Hey, talk to me on %@: %@";
|
||||
|
||||
// Mark: - Room avatar view
|
||||
|
||||
"room_avatar_view_accessibility_label" = "avatar";
|
||||
"room_avatar_view_accessibility_hint" = "Change room avatar";
|
||||
|
||||
// Mark: - Room creation introduction cell
|
||||
|
||||
"room_intro_cell_add_participants_action" = "Add people";
|
||||
|
||||
"room_intro_cell_information_room_sentence1_part1" = "This is the begining of ";
|
||||
"room_intro_cell_information_room_sentence1_part3" = ". ";
|
||||
|
||||
"room_intro_cell_information_room_with_topic_sentence2" = "Topic: %@";
|
||||
"room_intro_cell_information_room_without_topic_sentence2_part1" = "Add a topic";
|
||||
"room_intro_cell_information_room_without_topic_sentence2_part2" = " to let people know what this room is about.";
|
||||
|
||||
"room_intro_cell_information_dm_sentence1_part1" = "This is the begining of your direct message with ";
|
||||
"room_intro_cell_information_dm_sentence1_part3" = ". ";
|
||||
|
||||
"room_intro_cell_information_dm_sentence2" = "Only the two of you are in this conversation, no one else can join.";
|
||||
"room_intro_cell_information_multiple_dm_sentence2" = "Only you are in this conversation, unless any of you invites someone to join.";
|
||||
|
|
|
@ -50,3 +50,10 @@ extension String {
|
|||
return components(separatedBy: .whitespaces).joined()
|
||||
}
|
||||
}
|
||||
|
||||
extension Optional where Wrapped == String {
|
||||
|
||||
var isEmptyOrNil: Bool {
|
||||
return self?.isEmpty ?? true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,4 +54,14 @@ extension UIView {
|
|||
shake.timingFunction = CAMediaTimingFunction(name: .easeOut)
|
||||
layer.add(shake, forKey: "position")
|
||||
}
|
||||
|
||||
@objc func vc_setupAccessibilityTraitsButton(withTitle title: String, hint: String, isEnabled: Bool) {
|
||||
self.isAccessibilityElement = true
|
||||
self.accessibilityLabel = title
|
||||
self.accessibilityHint = hint
|
||||
self.accessibilityTraits = .button
|
||||
if !isEnabled {
|
||||
self.accessibilityTraits.insert(.notEnabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
BUNDLE_DISPLAY_NAME = Element
|
||||
#include "Config/AppIdentifiers.xcconfig"
|
||||
|
||||
APPLICATION_GROUP_IDENTIFIER = group.im.vector
|
||||
PRODUCT_NAME = Riot
|
||||
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER)
|
||||
|
||||
BASE_BUNDLE_IDENTIFIER = im.vector.app
|
||||
INFOPLIST_FILE = Riot/SupportingFiles/Info.plist
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
|
||||
|
||||
KEYCHAIN_ACCESS_GROUP = $(AppIdentifierPrefix)$(BASE_BUNDLE_IDENTIFIER).keychain.shared
|
||||
CODE_SIGN_ENTITLEMENTS = Riot/SupportingFiles/Riot.entitlements
|
||||
|
||||
//Make Xcode 12 and fastlane(xcodebuild) happy while some pods are not updated
|
||||
EXCLUDED_ARCHS[sdk=iphonesimulator*] = arm64
|
||||
|
||||
APPLICATION_SCHEME = element
|
||||
SWIFT_OBJC_BRIDGING_HEADER = $(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/Riot-Bridging-Header.h
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks
|
|
@ -17,5 +17,5 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "App-Common.xcconfig"
|
||||
#include "Common.xcconfig"
|
||||
#include "Pods/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot.debug.xcconfig"
|
|
@ -112,6 +112,7 @@ internal enum Asset {
|
|||
internal static let voiceCallHangupIcon = ImageAsset(name: "voice_call_hangup_icon")
|
||||
internal static let addMemberFloatingAction = ImageAsset(name: "add_member_floating_action")
|
||||
internal static let addParticipant = ImageAsset(name: "add_participant")
|
||||
internal static let addParticipants = ImageAsset(name: "add_participants")
|
||||
internal static let detailsIcon = ImageAsset(name: "details_icon")
|
||||
internal static let editIcon = ImageAsset(name: "edit_icon")
|
||||
internal static let integrationsIcon = ImageAsset(name: "integrations_icon")
|
||||
|
|
|
@ -2318,6 +2318,14 @@ internal enum VectorL10n {
|
|||
internal static var roomActionSendSticker: String {
|
||||
return VectorL10n.tr("Vector", "room_action_send_sticker")
|
||||
}
|
||||
/// Change room avatar
|
||||
internal static var roomAvatarViewAccessibilityHint: String {
|
||||
return VectorL10n.tr("Vector", "room_avatar_view_accessibility_hint")
|
||||
}
|
||||
/// avatar
|
||||
internal static var roomAvatarViewAccessibilityLabel: String {
|
||||
return VectorL10n.tr("Vector", "room_avatar_view_accessibility_label")
|
||||
}
|
||||
/// You need permission to manage conference call in this room
|
||||
internal static var roomConferenceCallNoPower: String {
|
||||
return VectorL10n.tr("Vector", "room_conference_call_no_power")
|
||||
|
@ -2806,6 +2814,46 @@ internal enum VectorL10n {
|
|||
internal static func roomInfoListSeveralMembers(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "room_info_list_several_members", p1)
|
||||
}
|
||||
/// Add people
|
||||
internal static var roomIntroCellAddParticipantsAction: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_add_participants_action")
|
||||
}
|
||||
/// This is the begining of your direct message with
|
||||
internal static var roomIntroCellInformationDmSentence1Part1: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_dm_sentence1_part1")
|
||||
}
|
||||
/// .
|
||||
internal static var roomIntroCellInformationDmSentence1Part3: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_dm_sentence1_part3")
|
||||
}
|
||||
/// Only the two of you are in this conversation, no one else can join.
|
||||
internal static var roomIntroCellInformationDmSentence2: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_dm_sentence2")
|
||||
}
|
||||
/// Only you are in this conversation, unless any of you invites someone to join.
|
||||
internal static var roomIntroCellInformationMultipleDmSentence2: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_multiple_dm_sentence2")
|
||||
}
|
||||
/// This is the begining of
|
||||
internal static var roomIntroCellInformationRoomSentence1Part1: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_room_sentence1_part1")
|
||||
}
|
||||
/// .
|
||||
internal static var roomIntroCellInformationRoomSentence1Part3: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_room_sentence1_part3")
|
||||
}
|
||||
/// Topic: %@
|
||||
internal static func roomIntroCellInformationRoomWithTopicSentence2(_ p1: String) -> String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_room_with_topic_sentence2", p1)
|
||||
}
|
||||
/// Add a topic
|
||||
internal static var roomIntroCellInformationRoomWithoutTopicSentence2Part1: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_room_without_topic_sentence2_part1")
|
||||
}
|
||||
/// to let people know what this room is about.
|
||||
internal static var roomIntroCellInformationRoomWithoutTopicSentence2Part2: String {
|
||||
return VectorL10n.tr("Vector", "room_intro_cell_information_room_without_topic_sentence2_part2")
|
||||
}
|
||||
/// Jump to first unread message
|
||||
internal static var roomJumpToFirstUnread: String {
|
||||
return VectorL10n.tr("Vector", "room_jump_to_first_unread")
|
||||
|
|
128
Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
Normal file
128
Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
Normal file
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// Copyright 2020 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 KeychainAccess
|
||||
import MatrixKit
|
||||
import CommonCrypto
|
||||
|
||||
@objcMembers
|
||||
class EncryptionKeyManager: NSObject, MXKeyProviderDelegate {
|
||||
static let shared = EncryptionKeyManager()
|
||||
|
||||
private static let keychainService: String = BuildSettings.baseBundleIdentifier + ".encryption-manager-service"
|
||||
private static let contactsIv: KeyValueStoreKey = "contactsIv"
|
||||
private static let contactsAesKey: KeyValueStoreKey = "contactsAesKey"
|
||||
private static let accountIv: KeyValueStoreKey = "accountIv"
|
||||
private static let accountAesKey: KeyValueStoreKey = "accountAesKey"
|
||||
private static let cryptoOlmPickleKey: KeyValueStoreKey = "cryptoOlmPickleKey"
|
||||
|
||||
private let keychainStore: KeyValueStore = KeychainStore(withKeychain: Keychain(service: keychainService, accessGroup: BuildSettings.keychainAccessGroup))
|
||||
|
||||
private override init() {
|
||||
super.init()
|
||||
initKeys()
|
||||
}
|
||||
|
||||
private func initKeys() {
|
||||
generateIvIfNotExists(forKey: EncryptionKeyManager.accountIv)
|
||||
generateAesKeyIfNotExists(forKey: EncryptionKeyManager.accountAesKey)
|
||||
generateIvIfNotExists(forKey: EncryptionKeyManager.contactsIv)
|
||||
generateAesKeyIfNotExists(forKey: EncryptionKeyManager.contactsAesKey)
|
||||
generateKeyIfNotExists(forKey: EncryptionKeyManager.cryptoOlmPickleKey, size: 32)
|
||||
|
||||
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.contactsIv), "[EncryptionKeyManager] initKeys: Failed to generate IV for acount")
|
||||
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.contactsAesKey), "[EncryptionKeyManager] initKeys: Failed to generate AES Key for acount")
|
||||
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.contactsIv), "[EncryptionKeyManager] initKeys: Failed to generate IV for contacts")
|
||||
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.contactsAesKey), "[EncryptionKeyManager] initKeys: Failed to generate AES Key for contacts")
|
||||
assert(keychainStore.containsObject(forKey: EncryptionKeyManager.cryptoOlmPickleKey), "[EncryptionKeyManager] initKeys: Failed to generate Key for olm pickle key")
|
||||
}
|
||||
|
||||
// MARK: - MXKeyProviderDelegate
|
||||
|
||||
func isEncryptionAvailableForData(ofType dataType: String) -> Bool {
|
||||
return dataType == MXKContactManagerDataType
|
||||
|| dataType == MXKAccountManagerDataType
|
||||
|| dataType == MXCryptoOlmPickleKeyDataType
|
||||
}
|
||||
|
||||
func hasKeyForData(ofType dataType: String) -> Bool {
|
||||
switch dataType {
|
||||
case MXKContactManagerDataType:
|
||||
return keychainStore.containsObject(forKey: EncryptionKeyManager.contactsIv) && keychainStore.containsObject(forKey: EncryptionKeyManager.contactsAesKey)
|
||||
case MXKAccountManagerDataType:
|
||||
return keychainStore.containsObject(forKey: EncryptionKeyManager.accountIv) && keychainStore.containsObject(forKey: EncryptionKeyManager.accountAesKey)
|
||||
case MXCryptoOlmPickleKeyDataType:
|
||||
return keychainStore.containsObject(forKey: EncryptionKeyManager.cryptoOlmPickleKey)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func keyDataForData(ofType dataType: String) -> MXKeyData? {
|
||||
switch dataType {
|
||||
case MXKContactManagerDataType:
|
||||
if let ivKey = try? keychainStore.data(forKey: EncryptionKeyManager.contactsIv),
|
||||
let aesKey = try? keychainStore.data(forKey: EncryptionKeyManager.contactsAesKey) {
|
||||
return MXAesKeyData(iv: ivKey, key: aesKey)
|
||||
}
|
||||
case MXKAccountManagerDataType:
|
||||
if let ivKey = try? keychainStore.data(forKey: EncryptionKeyManager.accountIv),
|
||||
let aesKey = try? keychainStore.data(forKey: EncryptionKeyManager.accountAesKey) {
|
||||
return MXAesKeyData(iv: ivKey, key: aesKey)
|
||||
}
|
||||
case MXCryptoOlmPickleKeyDataType:
|
||||
if let key = try? keychainStore.data(forKey: EncryptionKeyManager.cryptoOlmPickleKey) {
|
||||
return MXRawDataKey(key: key)
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
|
||||
private func generateIvIfNotExists(forKey key: String) {
|
||||
guard !keychainStore.containsObject(forKey: key) else {
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try keychainStore.set(MXAes.iv(), forKey: key)
|
||||
} catch {
|
||||
NSLog("[EncryptionKeyManager] initKeys: Failed to generate IV: %@", error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
private func generateAesKeyIfNotExists(forKey key: String) {
|
||||
generateKeyIfNotExists(forKey: key, size: kCCKeySizeAES256)
|
||||
}
|
||||
|
||||
private func generateKeyIfNotExists(forKey key: String, size: Int) {
|
||||
guard !keychainStore.containsObject(forKey: key) else {
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
var keyBytes = [UInt8](repeating: 0, count: size)
|
||||
_ = SecRandomCopyBytes(kSecRandomDefault, size, &keyBytes)
|
||||
try keychainStore.set(Data(bytes: keyBytes, count: size), forKey: key)
|
||||
} catch {
|
||||
NSLog("[EncryptionKeyManager] initKeys: Failed to generate Key[%@]: %@", key, error.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -84,6 +84,8 @@ import UIKit
|
|||
|
||||
|
||||
// MARK: - Colors not defined in the design palette
|
||||
|
||||
var secondaryCircleButtonBackgroundColor: UIColor { get }
|
||||
|
||||
/// fading behind dialog modals
|
||||
var overlayBackgroundColor: UIColor { get }
|
||||
|
|
|
@ -82,6 +82,7 @@ class DarkTheme: NSObject, Theme {
|
|||
var callScreenButtonTintColor: UIColor = UIColor(rgb: 0xFFFFFF)
|
||||
var overlayBackgroundColor: UIColor = UIColor(white: 0.7, alpha: 0.5)
|
||||
var matrixSearchBackgroundImageTintColor: UIColor = UIColor(rgb: 0x7E7E7E)
|
||||
var secondaryCircleButtonBackgroundColor: UIColor = UIColor(rgb: 0xE3E8F0)
|
||||
|
||||
var messageTickColor: UIColor = .white
|
||||
|
||||
|
|
|
@ -94,6 +94,8 @@ class DefaultTheme: NSObject, Theme {
|
|||
var overlayBackgroundColor: UIColor = UIColor(white: 0.7, alpha: 0.5)
|
||||
var matrixSearchBackgroundImageTintColor: UIColor = UIColor(rgb: 0xE7E7E7)
|
||||
|
||||
var secondaryCircleButtonBackgroundColor: UIColor = UIColor(rgb: 0xE3E8F0)
|
||||
|
||||
func applyStyle(onTabBar tabBar: UITabBar) {
|
||||
tabBar.unselectedItemTintColor = self.tabBarUnselectedItemTintColor
|
||||
tabBar.tintColor = self.tintColor
|
||||
|
|
|
@ -165,6 +165,16 @@
|
|||
*/
|
||||
- (void)muteEditedRoomNotifications:(BOOL)mute;
|
||||
|
||||
/**
|
||||
Show room directory.
|
||||
*/
|
||||
- (void)showRoomDirectory;
|
||||
|
||||
/**
|
||||
Show a public room.
|
||||
*/
|
||||
- (void)openPublicRoom:(MXPublicRoom *)publicRoom;
|
||||
|
||||
#pragma mark - Scrolling
|
||||
|
||||
/**
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface RecentsViewController () <CreateRoomCoordinatorBridgePresenterDelegate>
|
||||
@interface RecentsViewController () <CreateRoomCoordinatorBridgePresenterDelegate, RoomsDirectoryCoordinatorBridgePresenterDelegate>
|
||||
{
|
||||
// Tell whether a recents refresh is pending (suspended during editing mode).
|
||||
BOOL isRefreshPending;
|
||||
|
@ -68,6 +68,8 @@
|
|||
|
||||
@property (nonatomic, strong) CreateRoomCoordinatorBridgePresenter *createRoomCoordinatorBridgePresenter;
|
||||
|
||||
@property (nonatomic, strong) RoomsDirectoryCoordinatorBridgePresenter *roomsDirectoryCoordinatorBridgePresenter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RecentsViewController
|
||||
|
@ -856,6 +858,18 @@
|
|||
self.view.userInteractionEnabled = userInteractionEnabled;
|
||||
}
|
||||
|
||||
- (RecentsDataSource*)recentsDataSource
|
||||
{
|
||||
RecentsDataSource* recentsDataSource = nil;
|
||||
|
||||
if ([self.dataSource isKindOfClass:[RecentsDataSource class]])
|
||||
{
|
||||
recentsDataSource = (RecentsDataSource*)self.dataSource;
|
||||
}
|
||||
|
||||
return recentsDataSource;
|
||||
}
|
||||
|
||||
#pragma mark - MXKDataSourceDelegate
|
||||
|
||||
- (Class<MXKCellRendering>)cellViewClassForCellData:(MXKCellData*)cellData
|
||||
|
@ -1783,73 +1797,58 @@
|
|||
|
||||
- (void)joinARoom
|
||||
{
|
||||
[currentAlert dismissViewControllerAnimated:NO completion:nil];
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
|
||||
// Prompt the user to type a room id or room alias
|
||||
currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_recents_join_room_title", @"Vector", nil)
|
||||
message:NSLocalizedStringFromTable(@"room_recents_join_room_prompt", @"Vector", nil)
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
|
||||
[currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
|
||||
|
||||
textField.secureTextEntry = NO;
|
||||
textField.placeholder = nil;
|
||||
textField.keyboardType = UIKeyboardTypeDefault;
|
||||
}];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"]
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
self->currentAlert = nil;
|
||||
}
|
||||
|
||||
}]];
|
||||
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"join", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
NSString *roomAliasOrId = [self->currentAlert textFields].firstObject.text;
|
||||
|
||||
self->currentAlert = nil;
|
||||
|
||||
[self.activityIndicator startAnimating];
|
||||
[self showRoomDirectory];
|
||||
}
|
||||
|
||||
// TODO
|
||||
self->currentRequest = [self.mainSession joinRoom:roomAliasOrId viaServers:nil success:^(MXRoom *room) {
|
||||
|
||||
self->currentRequest = nil;
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
// Show the room
|
||||
[[AppDelegate theDelegate] showRoom:room.roomId andEventId:nil withMatrixSession:self.mainSession];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
NSLog(@"[RecentsViewController] Join joinARoom (%@) failed", roomAliasOrId);
|
||||
|
||||
self->currentRequest = nil;
|
||||
[self.activityIndicator stopAnimating];
|
||||
|
||||
// Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
}
|
||||
|
||||
}]];
|
||||
- (void)showRoomDirectory
|
||||
{
|
||||
if (!self.self.mainSession)
|
||||
{
|
||||
NSLog(@"[RecentsViewController] Fail to show room directory, session is nil");
|
||||
return;
|
||||
}
|
||||
|
||||
[currentAlert mxk_setAccessibilityIdentifier:@"RecentsVCJoinARoomAlert"];
|
||||
[self presentViewController:currentAlert animated:YES completion:nil];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter = [[RoomsDirectoryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession dataSource:[self.recentsDataSource.publicRoomsDirectoryDataSource copy]];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter.delegate = self;
|
||||
[self.roomsDirectoryCoordinatorBridgePresenter presentFrom:self animated:YES];
|
||||
}
|
||||
|
||||
- (void)openPublicRoom:(MXPublicRoom *)publicRoom
|
||||
{
|
||||
if (!self.recentsDataSource)
|
||||
{
|
||||
NSLog(@"[RecentsViewController] Fail to open public room, dataSource is not kind of class MXKRecentsDataSource");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether the user has already joined the selected public room
|
||||
if ([self.recentsDataSource.publicRoomsDirectoryDataSource.mxSession roomWithRoomId:publicRoom.roomId])
|
||||
{
|
||||
// Open the public room
|
||||
[[AppDelegate theDelegate] showRoom:publicRoom.roomId andEventId:nil withMatrixSession:self.recentsDataSource.publicRoomsDirectoryDataSource.mxSession restoreInitialDisplay:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Preview the public room
|
||||
if (publicRoom.worldReadable)
|
||||
{
|
||||
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithPublicRoom:publicRoom andSession:self.recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
|
||||
|
||||
[self startActivityIndicator];
|
||||
|
||||
// Try to get more information about the room before opening its preview
|
||||
[roomPreviewData peekInRoom:^(BOOL succeeded) {
|
||||
[self stopActivityIndicator];
|
||||
|
||||
[[AppDelegate theDelegate].masterTabBarController showRoomPreview:roomPreviewData];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithPublicRoom:publicRoom andSession:self.recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
|
||||
[[AppDelegate theDelegate].masterTabBarController showRoomPreview:roomPreviewData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Table view scrolling
|
||||
|
@ -2052,4 +2051,28 @@
|
|||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - RoomsDirectoryCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)roomsDirectoryCoordinatorBridgePresenterDelegateDidComplete:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)roomsDirectoryCoordinatorBridgePresenterDelegate:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectRoom:(MXPublicRoom *)room
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
[self openPublicRoom:room];
|
||||
}];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)roomsDirectoryCoordinatorBridgePresenterDelegateDidTapCreateNewRoom:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
[self createNewRoom];
|
||||
}];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
// File created from ScreenTemplate
|
||||
// $ createScreen.sh KeyBackup/Recover/Loading KeyBackupRecoverDataLoading
|
||||
/*
|
||||
Copyright 2020 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol KeyBackupRecoverDataLoadingViewModelViewDelegate: class {
|
||||
func keyBackupRecoverDataLoadingViewModel(_ viewModel: KeyBackupRecoverDataLoadingViewModelType, didUpdateViewState viewSate: KeyBackupRecoverDataLoadingViewState)
|
||||
}
|
||||
|
||||
protocol KeyBackupRecoverDataLoadingViewModelCoordinatorDelegate: class {
|
||||
func keyBackupRecoverDataLoadingViewModelDidRecover(_ viewModel: KeyBackupRecoverDataLoadingViewModelType)
|
||||
func keyBackupRecoverDataLoadingViewModelDidCancel(_ viewModel: KeyBackupRecoverDataLoadingViewModelType)
|
||||
}
|
||||
|
||||
/// Protocol describing the view model used by `KeyBackupRecoverDataLoadingViewController`
|
||||
protocol KeyBackupRecoverDataLoadingViewModelType {
|
||||
|
||||
var viewDelegate: KeyBackupRecoverDataLoadingViewModelViewDelegate? { get set }
|
||||
var coordinatorDelegate: KeyBackupRecoverDataLoadingViewModelCoordinatorDelegate? { get set }
|
||||
|
||||
func process(viewAction: KeyBackupRecoverDataLoadingViewAction)
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
final class KeyBackupSetupRecoveryKeyViewController: UIViewController {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let animationDuration: TimeInterval = 0.3
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var recoveryKeyBackgroundView: UIView!
|
||||
|
||||
@IBOutlet private weak var recoveryKeyTitleLabel: UILabel!
|
||||
@IBOutlet private weak var recoveryKeyLabel: UILabel!
|
||||
|
||||
@IBOutlet private weak var separatorView: UIView!
|
||||
|
||||
@IBOutlet private weak var makeCopyButton: UIButton!
|
||||
|
||||
@IBOutlet private weak var madeCopyButtonBackgroundView: UIView!
|
||||
@IBOutlet private weak var madeCopyButton: UIButton!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
private var hasMadeACopy: Bool = false
|
||||
private var viewModel: KeyBackupSetupRecoveryKeyViewModelType!
|
||||
private var errorPresenter: MXKErrorPresentation!
|
||||
private var activityPresenter: ActivityIndicatorPresenter!
|
||||
private weak var skipAlertController: UIAlertController?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with viewModel: KeyBackupSetupRecoveryKeyViewModelType) -> KeyBackupSetupRecoveryKeyViewController {
|
||||
let viewController = StoryboardScene.KeyBackupSetupRecoveryKeyViewController.initialScene.instantiate()
|
||||
viewController.viewModel = viewModel
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.title = VectorL10n.keyBackupSetupTitle
|
||||
|
||||
self.activityPresenter = ActivityIndicatorPresenter()
|
||||
self.errorPresenter = MXKErrorAlertPresentation()
|
||||
|
||||
self.setupViews()
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
|
||||
self.viewModel.viewDelegate = self
|
||||
}
|
||||
|
||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||
return self.theme.statusBarStyle
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.view.backgroundColor = theme.headerBackgroundColor
|
||||
|
||||
if let navigationBar = self.navigationController?.navigationBar {
|
||||
theme.applyStyle(onNavigationBar: navigationBar)
|
||||
}
|
||||
|
||||
self.informationLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.recoveryKeyBackgroundView.backgroundColor = theme.backgroundColor
|
||||
|
||||
self.recoveryKeyTitleLabel.textColor = theme.textPrimaryColor
|
||||
self.recoveryKeyLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.separatorView.backgroundColor = theme.lineBreakColor
|
||||
|
||||
theme.applyStyle(onButton: self.makeCopyButton)
|
||||
|
||||
self.madeCopyButtonBackgroundView.backgroundColor = theme.backgroundColor
|
||||
theme.applyStyle(onButton: self.madeCopyButton)
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
|
||||
private func setupViews() {
|
||||
let skipBarButtonItem = MXKBarButtonItem(title: VectorL10n.keyBackupSetupSkipAction, style: .plain) { [weak self] in
|
||||
self?.skipButtonAction()
|
||||
}
|
||||
self.navigationItem.rightBarButtonItem = skipBarButtonItem
|
||||
|
||||
self.informationLabel.text = VectorL10n.keyBackupSetupRecoveryKeyInfo
|
||||
self.recoveryKeyTitleLabel.text = VectorL10n.keyBackupSetupRecoveryKeyRecoveryKeyTitle
|
||||
self.recoveryKeyLabel.text = self.viewModel.recoveryKey
|
||||
|
||||
self.makeCopyButton.setTitle(VectorL10n.keyBackupSetupRecoveryKeyMakeCopyAction, for: .normal)
|
||||
self.madeCopyButton.setTitle(VectorL10n.keyBackupSetupRecoveryKeyMadeCopyAction, for: .normal)
|
||||
|
||||
self.updateMadeCopyButton()
|
||||
}
|
||||
|
||||
private func shareRecoveryKey() {
|
||||
|
||||
// Set up activity view controller
|
||||
let activityItems: [Any] = [ self.viewModel.recoveryKey ]
|
||||
let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
|
||||
|
||||
activityViewController.completionWithItemsHandler = { (activityType, completed, returnedItems, error) in
|
||||
|
||||
// Enable made copy button only if user has selected an activity item
|
||||
if completed {
|
||||
self.hasMadeACopy = true
|
||||
self.updateMadeCopyButton()
|
||||
}
|
||||
}
|
||||
|
||||
// Configure source view when activity view controller is presented with a popover
|
||||
if let popoverPresentationController = activityViewController.popoverPresentationController {
|
||||
popoverPresentationController.sourceView = self.makeCopyButton
|
||||
popoverPresentationController.sourceRect = self.makeCopyButton.bounds
|
||||
popoverPresentationController.permittedArrowDirections = [.down, .up]
|
||||
}
|
||||
|
||||
self.present(activityViewController, animated: true)
|
||||
}
|
||||
|
||||
private func updateMadeCopyButton() {
|
||||
self.madeCopyButton.isEnabled = self.hasMadeACopy
|
||||
}
|
||||
|
||||
private func render(viewState: KeyBackupSetupRecoveryKeyViewState) {
|
||||
switch viewState {
|
||||
case .loading:
|
||||
self.renderLoading()
|
||||
case .loaded:
|
||||
self.renderLoaded()
|
||||
case .error(let error):
|
||||
self.render(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func renderLoading() {
|
||||
self.view.endEditing(true)
|
||||
self.activityPresenter.presentActivityIndicator(on: self.view, animated: true)
|
||||
}
|
||||
|
||||
private func renderLoaded() {
|
||||
self.activityPresenter.removeCurrentActivityIndicator(animated: true)
|
||||
}
|
||||
|
||||
private func render(error: Error) {
|
||||
self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil)
|
||||
}
|
||||
|
||||
private func showSkipAlert() {
|
||||
guard self.skipAlertController == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
let alertController = UIAlertController(title: VectorL10n.keyBackupSetupSkipAlertTitle,
|
||||
message: VectorL10n.keyBackupSetupSkipAlertMessage,
|
||||
preferredStyle: .alert)
|
||||
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.continue, style: .cancel, handler: { action in
|
||||
self.viewModel.process(viewAction: .skipAlertContinue)
|
||||
}))
|
||||
|
||||
alertController.addAction(UIAlertAction(title: VectorL10n.keyBackupSetupSkipAlertSkipAction, style: .default, handler: { action in
|
||||
self.viewModel.process(viewAction: .skipAlertSkip)
|
||||
}))
|
||||
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
self.skipAlertController = alertController
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@IBAction private func makeCopyButtonAction(_ sender: Any) {
|
||||
self.shareRecoveryKey()
|
||||
}
|
||||
|
||||
@IBAction private func madeCopyButtonAction(_ sender: Any) {
|
||||
self.viewModel.process(viewAction: .madeCopy)
|
||||
}
|
||||
|
||||
private func skipButtonAction() {
|
||||
self.viewModel.process(viewAction: .skip)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - KeyBackupSetupRecoveryKeyViewModelViewDelegate
|
||||
extension KeyBackupSetupRecoveryKeyViewController: KeyBackupSetupRecoveryKeyViewModelViewDelegate {
|
||||
func keyBackupSetupRecoveryKeyViewModel(_ viewModel: KeyBackupSetupRecoveryKeyViewModelType, didUpdateViewState viewSate: KeyBackupSetupRecoveryKeyViewState) {
|
||||
self.render(viewState: viewSate)
|
||||
}
|
||||
|
||||
func keyBackupSetupPassphraseViewModelShowSkipAlert(_ viewModel: KeyBackupSetupRecoveryKeyViewModelType) {
|
||||
self.showSkipAlert()
|
||||
}
|
||||
}
|
|
@ -27,7 +27,8 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag)
|
|||
RoomBubbleCellDataTagKeyVerificationRequestIncomingApproval,
|
||||
RoomBubbleCellDataTagKeyVerificationRequest,
|
||||
RoomBubbleCellDataTagKeyVerificationConclusion,
|
||||
RoomBubbleCellDataTagCall
|
||||
RoomBubbleCellDataTagCall,
|
||||
RoomBubbleCellDataTagRoomCreationIntro
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -217,6 +217,11 @@ static NSAttributedString *timestampVerticalWhitespace = nil;
|
|||
return YES;
|
||||
}
|
||||
|
||||
if (self.tag == RoomBubbleCellDataTagRoomCreationIntro)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [super hasNoDisplay];
|
||||
}
|
||||
|
||||
|
|
|
@ -80,6 +80,10 @@
|
|||
|
||||
@property (nonatomic, strong) CellDataComponentIndexPair *sentCell;
|
||||
|
||||
@property (nonatomic) RoomBubbleCellData *roomCreationCellData;
|
||||
|
||||
@property (nonatomic) BOOL showRoomCreationCell;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomDataSource
|
||||
|
@ -237,6 +241,7 @@
|
|||
}
|
||||
|
||||
[self fetchEncryptionTrustedLevel];
|
||||
[self enableRoomCreationIntroCellDisplayIfNeeded];
|
||||
}
|
||||
|
||||
- (void)fetchEncryptionTrustedLevel
|
||||
|
@ -245,6 +250,10 @@
|
|||
[self.roomDataSourceDelegate roomDataSource:self didUpdateEncryptionTrustLevel:self.encryptionTrustLevel];
|
||||
}
|
||||
|
||||
- (void)roomDidSet
|
||||
{
|
||||
[self enableRoomCreationIntroCellDisplayIfNeeded];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
@ -257,6 +266,8 @@
|
|||
// Enable the containsLastMessage flag for the cell data which contains the last message.
|
||||
@synchronized(bubbles)
|
||||
{
|
||||
[self insertRoomCreationIntroCellDataIfNeeded];
|
||||
|
||||
// Reset first all cell data
|
||||
for (RoomBubbleCellData *cellData in bubbles)
|
||||
{
|
||||
|
@ -1139,4 +1150,81 @@
|
|||
[cell.contentView addSubview:tickView];
|
||||
}
|
||||
|
||||
#pragma mark - Room creation intro cell
|
||||
|
||||
- (BOOL)canShowRoomCreationIntroCell
|
||||
{
|
||||
NSString* userId = self.mxSession.myUser.userId;
|
||||
|
||||
if (!userId || !self.isLive || self.isPeeking)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Room creation cell is only shown for the creator
|
||||
return [self.room.summary.creatorUserId isEqualToString:userId];
|
||||
}
|
||||
|
||||
- (void)enableRoomCreationIntroCellDisplayIfNeeded
|
||||
{
|
||||
self.showRoomCreationCell = [self canShowRoomCreationIntroCell];
|
||||
}
|
||||
|
||||
// Insert the room creation intro cell at the begining
|
||||
- (void)insertRoomCreationIntroCellDataIfNeeded
|
||||
{
|
||||
@synchronized(bubbles)
|
||||
{
|
||||
NSUInteger existingRoomCreationCellDataIndex = [self roomBubbleDataIndexWithTag:RoomBubbleCellDataTagRoomCreationIntro];
|
||||
|
||||
if (existingRoomCreationCellDataIndex != NSNotFound)
|
||||
{
|
||||
[bubbles removeObjectAtIndex:existingRoomCreationCellDataIndex];
|
||||
}
|
||||
|
||||
if (self.showRoomCreationCell)
|
||||
{
|
||||
NSUInteger roomCreationConfigCellDataIndex = [self roomBubbleDataIndexWithTag:RoomBubbleCellDataTagRoomCreateConfiguration];
|
||||
|
||||
// Only add room creation intro cell if `bubbles` array contains the room creation event
|
||||
if (roomCreationConfigCellDataIndex != NSNotFound)
|
||||
{
|
||||
if (!self.roomCreationCellData)
|
||||
{
|
||||
MXEvent *event = [MXEvent new];
|
||||
MXRoomState *roomState = [MXRoomState new];
|
||||
RoomBubbleCellData *roomBubbleCellData = [[RoomBubbleCellData alloc] initWithEvent:event andRoomState:roomState andRoomDataSource:self];
|
||||
roomBubbleCellData.tag = RoomBubbleCellDataTagRoomCreationIntro;
|
||||
|
||||
self.roomCreationCellData = roomBubbleCellData;
|
||||
}
|
||||
|
||||
[bubbles insertObject:self.roomCreationCellData atIndex:0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.roomCreationCellData = nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)roomBubbleDataIndexWithTag:(RoomBubbleCellDataTag)tag
|
||||
{
|
||||
@synchronized(bubbles)
|
||||
{
|
||||
return [bubbles indexOfObjectPassingTest:^BOOL(id<MXKRoomBubbleCellDataStoring> _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
if ([obj isKindOfClass:RoomBubbleCellData.class])
|
||||
{
|
||||
RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)obj;
|
||||
if (roomBubbleCellData.tag == tag)
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -21,4 +21,6 @@ limitations under the License.
|
|||
*/
|
||||
@interface RoomFilesViewController : MXKRoomViewController
|
||||
|
||||
@property (nonatomic) BOOL showCancelBarButtonItem;
|
||||
|
||||
@end
|
||||
|
|
|
@ -159,8 +159,21 @@
|
|||
{
|
||||
// Check whether the view controller is currently displayed inside a segmented view controller or not.
|
||||
UIViewController* topViewController = ((self.parentViewController) ? self.parentViewController : self);
|
||||
topViewController.navigationItem.rightBarButtonItem = nil;
|
||||
topViewController.navigationItem.leftBarButtonItem = nil;
|
||||
topViewController.navigationItem.rightBarButtonItem = nil;
|
||||
|
||||
if (self.showCancelBarButtonItem)
|
||||
{
|
||||
topViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(onCancel:)];
|
||||
}
|
||||
else
|
||||
{
|
||||
topViewController.navigationItem.leftBarButtonItem = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onCancel:(id)sender
|
||||
{
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark - MXKDataSourceDelegate
|
||||
|
|
|
@ -84,6 +84,8 @@
|
|||
*/
|
||||
@property (nonatomic) BOOL enableMention;
|
||||
|
||||
@property (nonatomic) BOOL showCancelBarButtonItem;
|
||||
|
||||
/**
|
||||
The delegate for the view controller.
|
||||
*/
|
||||
|
|
|
@ -520,7 +520,15 @@
|
|||
// Check whether the view controller is currently displayed inside a segmented view controller or not.
|
||||
UIViewController* topViewController = ((self.parentViewController) ? self.parentViewController : self);
|
||||
topViewController.navigationItem.rightBarButtonItem = nil;
|
||||
topViewController.navigationItem.leftBarButtonItem = nil;
|
||||
|
||||
if (self.showCancelBarButtonItem)
|
||||
{
|
||||
topViewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(onCancel:)];
|
||||
}
|
||||
else
|
||||
{
|
||||
topViewController.navigationItem.leftBarButtonItem = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onAddParticipantButtonPressed
|
||||
|
@ -1463,6 +1471,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)onCancel:(id)sender
|
||||
{
|
||||
[self withdrawViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (void)didSelectInvitableContact:(MXKContact*)contact
|
||||
|
|
|
@ -28,6 +28,8 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
|||
private let navigationRouter: NavigationRouterType
|
||||
private let session: MXSession
|
||||
private let room: MXRoom
|
||||
private let initialSection: RoomInfoSection
|
||||
private weak var roomSettingsViewController: RoomSettingsViewController?
|
||||
|
||||
private lazy var segmentedViewController: SegmentedViewController = {
|
||||
let controller = SegmentedViewController()
|
||||
|
@ -68,6 +70,8 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
|||
], defaultSelected: 0)
|
||||
controller.addMatrixSession(self.session)
|
||||
|
||||
self.roomSettingsViewController = settings
|
||||
|
||||
_ = controller.view
|
||||
|
||||
return controller
|
||||
|
@ -82,10 +86,11 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
|||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, room: MXRoom) {
|
||||
init(parameters: RoomInfoCoordinatorParameters) {
|
||||
self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController())
|
||||
self.session = session
|
||||
self.room = room
|
||||
self.session = parameters.session
|
||||
self.room = parameters.room
|
||||
self.initialSection = parameters.initialSection
|
||||
}
|
||||
|
||||
// MARK: - Public methods
|
||||
|
@ -96,8 +101,19 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
|||
rootCoordinator.start()
|
||||
|
||||
self.add(childCoordinator: rootCoordinator)
|
||||
|
||||
|
||||
self.navigationRouter.setRootModule(rootCoordinator)
|
||||
|
||||
switch initialSection {
|
||||
case .addParticipants:
|
||||
self.showRoomDetails(with: .members, animated: false)
|
||||
case .changeAvatar:
|
||||
self.showRoomDetails(with: .settings(RoomSettingsViewControllerFieldAvatar), animated: false)
|
||||
case .changeTopic:
|
||||
self.showRoomDetails(with: .settings(RoomSettingsViewControllerFieldTopic), animated: false)
|
||||
case .none:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
|
@ -111,14 +127,23 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
|
|||
coordinator.delegate = self
|
||||
return coordinator
|
||||
}
|
||||
|
||||
private func showRoomDetails(with target: RoomInfoListTarget, animated: Bool) {
|
||||
segmentedViewController.selectedIndex = target.tabIndex
|
||||
|
||||
if case .settings(let roomSettingsField) = target {
|
||||
roomSettingsViewController?.selectedRoomSettingsField = roomSettingsField
|
||||
}
|
||||
|
||||
navigationRouter.push(segmentedViewController, animated: animated, popCompletion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - RoomInfoListCoordinatorDelegate
|
||||
extension RoomInfoCoordinator: RoomInfoListCoordinatorDelegate {
|
||||
|
||||
func roomInfoListCoordinator(_ coordinator: RoomInfoListCoordinatorType, wantsToNavigateTo target: RoomInfoListTarget) {
|
||||
segmentedViewController.selectedIndex = target.rawValue
|
||||
navigationRouter.push(segmentedViewController, animated: true, popCompletion: nil)
|
||||
self.showRoomDetails(with: target, animated: true)
|
||||
}
|
||||
|
||||
func roomInfoListCoordinatorDidCancel(_ coordinator: RoomInfoListCoordinatorType) {
|
||||
|
|
|
@ -31,8 +31,7 @@ final class RoomInfoCoordinatorBridgePresenter: NSObject {
|
|||
|
||||
// MARK: Private
|
||||
|
||||
private let session: MXSession
|
||||
private let room: MXRoom
|
||||
private let coordinatorParameters: RoomInfoCoordinatorParameters
|
||||
private var coordinator: RoomInfoCoordinator?
|
||||
|
||||
// MARK: Public
|
||||
|
@ -41,9 +40,8 @@ final class RoomInfoCoordinatorBridgePresenter: NSObject {
|
|||
|
||||
// MARK: - Setup
|
||||
|
||||
init(session: MXSession, room: MXRoom) {
|
||||
self.session = session
|
||||
self.room = room
|
||||
init(parameters: RoomInfoCoordinatorParameters) {
|
||||
self.coordinatorParameters = parameters
|
||||
super.init()
|
||||
}
|
||||
|
||||
|
@ -55,7 +53,7 @@ final class RoomInfoCoordinatorBridgePresenter: NSObject {
|
|||
// }
|
||||
|
||||
func present(from viewController: UIViewController, animated: Bool) {
|
||||
let roomInfoCoordinator = RoomInfoCoordinator(session: self.session, room: room)
|
||||
let roomInfoCoordinator = RoomInfoCoordinator(parameters: self.coordinatorParameters)
|
||||
roomInfoCoordinator.delegate = self
|
||||
let presentable = roomInfoCoordinator.toPresentable()
|
||||
presentable.presentationController?.delegate = self
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
enum RoomInfoSection: Int {
|
||||
case none
|
||||
case addParticipants
|
||||
case changeAvatar
|
||||
case changeTopic
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
class RoomInfoCoordinatorParameters: NSObject {
|
||||
|
||||
let session: MXSession
|
||||
let room: MXRoom
|
||||
let initialSection: RoomInfoSection
|
||||
|
||||
init(session: MXSession, room: MXRoom, initialSection: RoomInfoSection) {
|
||||
self.session = session
|
||||
self.room = room
|
||||
self.initialSection = initialSection
|
||||
super.init()
|
||||
}
|
||||
|
||||
convenience init(session: MXSession, room: MXRoom) {
|
||||
self.init(session: session, room: room, initialSection: .none)
|
||||
}
|
||||
}
|
|
@ -18,10 +18,25 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
enum RoomInfoListTarget: UInt {
|
||||
case settings = 2
|
||||
case members = 0
|
||||
case uploads = 1
|
||||
enum RoomInfoListTarget {
|
||||
case settings(_ field: RoomSettingsViewControllerField = RoomSettingsViewControllerFieldNone)
|
||||
case members
|
||||
case uploads
|
||||
|
||||
var tabIndex: UInt {
|
||||
let tabIndex: UInt
|
||||
|
||||
switch self {
|
||||
case .members:
|
||||
tabIndex = 0
|
||||
case .uploads:
|
||||
tabIndex = 1
|
||||
case .settings:
|
||||
tabIndex = 2
|
||||
}
|
||||
|
||||
return tabIndex
|
||||
}
|
||||
}
|
||||
|
||||
/// RoomInfoListViewController view actions exposed to view model
|
||||
|
|
|
@ -148,7 +148,7 @@ final class RoomInfoListViewController: UIViewController {
|
|||
var tmpSections: [Section] = []
|
||||
|
||||
let rowSettings = Row(type: .default, icon: Asset.Images.settingsIcon.image, text: VectorL10n.roomDetailsSettings, accessoryType: .disclosureIndicator) {
|
||||
self.viewModel.process(viewAction: .navigate(target: .settings))
|
||||
self.viewModel.process(viewAction: .navigate(target: .settings()))
|
||||
}
|
||||
let text = viewData.numberOfMembers == 1 ? VectorL10n.roomInfoListOneMember : VectorL10n.roomInfoListSeveralMembers(String(viewData.numberOfMembers))
|
||||
let rowMembers = Row(type: .default, icon: Asset.Images.userIcon.image, text: text, accessoryType: .disclosureIndicator) {
|
||||
|
|
|
@ -371,6 +371,8 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo
|
|||
// call cells
|
||||
[self.bubblesTableView registerClass:RoomDirectCallStatusBubbleCell.class forCellReuseIdentifier:RoomDirectCallStatusBubbleCell.defaultReuseIdentifier];
|
||||
|
||||
[self.bubblesTableView registerClass:RoomCreationIntroCell.class forCellReuseIdentifier:RoomCreationIntroCell.defaultReuseIdentifier];
|
||||
|
||||
[self vc_removeBackTitle];
|
||||
|
||||
// Replace the default input toolbar view.
|
||||
|
@ -1662,6 +1664,36 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo
|
|||
[self.navigationController pushViewController:memberViewController animated:YES];
|
||||
}
|
||||
|
||||
- (void)showRoomAvatarChange
|
||||
{
|
||||
[self showRoomInfoWithInitialSection:RoomInfoSectionChangeAvatar];
|
||||
}
|
||||
|
||||
- (void)showAddParticipants
|
||||
{
|
||||
[self showRoomInfoWithInitialSection:RoomInfoSectionAddParticipants];
|
||||
}
|
||||
|
||||
- (void)showRoomTopicChange
|
||||
{
|
||||
[self showRoomInfoWithInitialSection:RoomInfoSectionChangeTopic];
|
||||
}
|
||||
|
||||
- (void)showRoomInfo
|
||||
{
|
||||
[self showRoomInfoWithInitialSection:RoomInfoSectionNone];
|
||||
}
|
||||
|
||||
- (void)showRoomInfoWithInitialSection:(RoomInfoSection)roomInfoSection
|
||||
{
|
||||
RoomInfoCoordinatorParameters *parameters = [[RoomInfoCoordinatorParameters alloc] initWithSession:self.roomDataSource.mxSession room:self.roomDataSource.room initialSection:roomInfoSection];
|
||||
|
||||
self.roomInfoCoordinatorBridgePresenter = [[RoomInfoCoordinatorBridgePresenter alloc] initWithParameters:parameters];
|
||||
|
||||
self.roomInfoCoordinatorBridgePresenter.delegate = self;
|
||||
[self.roomInfoCoordinatorBridgePresenter presentFrom:self animated:YES];
|
||||
}
|
||||
|
||||
#pragma mark - Dialpad
|
||||
|
||||
- (void)openDialpad
|
||||
|
@ -1988,6 +2020,10 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo
|
|||
{
|
||||
cellViewClass = RoomEmptyBubbleCell.class;
|
||||
}
|
||||
else if (bubbleData.tag == RoomBubbleCellDataTagRoomCreationIntro)
|
||||
{
|
||||
cellViewClass = RoomCreationIntroCell.class;
|
||||
}
|
||||
else if (bubbleData.tag == RoomBubbleCellDataTagRoomCreateWithPredecessor)
|
||||
{
|
||||
cellViewClass = RoomPredecessorBubbleCell.class;
|
||||
|
@ -2354,6 +2390,18 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo
|
|||
|
||||
[self roomInputToolbarView:self.inputToolbarView placeCallWithVideo2:eventContent.isVideoCall];
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:RoomCreationIntroCell.tapOnAvatarView])
|
||||
{
|
||||
[self showRoomAvatarChange];
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:RoomCreationIntroCell.tapOnAddParticipants])
|
||||
{
|
||||
[self showAddParticipants];
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:RoomCreationIntroCell.tapOnAddTopic])
|
||||
{
|
||||
[self showRoomTopicChange];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep default implementation for other actions
|
||||
|
@ -3844,9 +3892,7 @@ NSNotificationName const RoomCallTileTappedNotification = @"RoomCallTileTappedNo
|
|||
|
||||
if (tappedView == titleView.titleMask)
|
||||
{
|
||||
self.roomInfoCoordinatorBridgePresenter = [[RoomInfoCoordinatorBridgePresenter alloc] initWithSession:self.roomDataSource.mxSession room:self.roomDataSource.room];
|
||||
self.roomInfoCoordinatorBridgePresenter.delegate = self;
|
||||
[self.roomInfoCoordinatorBridgePresenter presentFrom:self animated:YES];
|
||||
[self showRoomInfo];
|
||||
}
|
||||
else if (tappedView == previewHeader.rightButton)
|
||||
{
|
||||
|
|
149
Riot/Modules/Room/Views/Avatar/RoomAvatarView.swift
Normal file
149
Riot/Modules/Room/Views/Avatar/RoomAvatarView.swift
Normal file
|
@ -0,0 +1,149 @@
|
|||
//
|
||||
// Copyright 2020 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class RoomAvatarView: UIView, NibOwnerLoadable, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var avatarImageView: MXKImageView!
|
||||
@IBOutlet private weak var cameraBadgeContainerView: UIView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme?
|
||||
|
||||
private var isHighlighted: Bool = false {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var action: (() -> Void)?
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
private func commonInit() {
|
||||
self.setupAvatarImageView()
|
||||
self.setupGestureRecognizer()
|
||||
self.vc_setupAccessibilityTraitsButton(withTitle: VectorL10n.roomAvatarViewAccessibilityLabel, hint: VectorL10n.roomAvatarViewAccessibilityHint, isEnabled: true)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.loadNibContent()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.loadNibContent()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
// MARK: - Lifecycle
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.avatarImageView.layer.cornerRadius = self.avatarImageView.bounds.height/2
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(with viewData: RoomAvatarViewData) {
|
||||
self.updateAvatarImageView(with: viewData)
|
||||
|
||||
// Fix layoutSubviews not triggered issue
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
self.setNeedsLayout()
|
||||
}
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupGestureRecognizer() {
|
||||
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(buttonAction(_:)))
|
||||
gestureRecognizer.minimumPressDuration = 0
|
||||
self.addGestureRecognizer(gestureRecognizer)
|
||||
}
|
||||
|
||||
private func setupAvatarImageView() {
|
||||
self.avatarImageView.defaultBackgroundColor = UIColor.clear
|
||||
self.avatarImageView.enableInMemoryCache = true
|
||||
self.avatarImageView.layer.masksToBounds = true
|
||||
}
|
||||
|
||||
private func updateAvatarImageView(with viewData: RoomAvatarViewData) {
|
||||
guard let avatarImageView = self.avatarImageView else {
|
||||
return
|
||||
}
|
||||
|
||||
let defaultavatarImage = AvatarGenerator.generateAvatar(forMatrixItem: viewData.roomId, withDisplayName: viewData.roomDisplayName)
|
||||
|
||||
if let avatarUrl = viewData.avatarUrl {
|
||||
avatarImageView.setImageURI(avatarUrl,
|
||||
withType: nil,
|
||||
andImageOrientation: .up,
|
||||
toFitViewSize: avatarImageView.frame.size,
|
||||
with: MXThumbnailingMethodScale,
|
||||
previewImage: defaultavatarImage,
|
||||
mediaManager: viewData.mediaManager)
|
||||
} else {
|
||||
avatarImageView.image = defaultavatarImage
|
||||
}
|
||||
|
||||
avatarImageView.contentMode = .scaleAspectFill
|
||||
|
||||
self.cameraBadgeContainerView.isHidden = viewData.avatarUrl != nil
|
||||
}
|
||||
|
||||
private func updateView() {
|
||||
// TODO: Handle highlighted state
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc private func buttonAction(_ sender: UILongPressGestureRecognizer) {
|
||||
|
||||
let isBackgroundViewTouched = sender.vc_isTouchingInside()
|
||||
|
||||
switch sender.state {
|
||||
case .began, .changed:
|
||||
self.isHighlighted = isBackgroundViewTouched
|
||||
case .ended:
|
||||
self.isHighlighted = false
|
||||
|
||||
if isBackgroundViewTouched {
|
||||
self.action?()
|
||||
}
|
||||
case .cancelled:
|
||||
self.isHighlighted = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
75
Riot/Modules/Room/Views/Avatar/RoomAvatarView.xib
Normal file
75
Riot/Modules/Room/Views/Avatar/RoomAvatarView.xib
Normal file
|
@ -0,0 +1,75 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="RoomAvatarView" customModule="Riot" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="avatarImageView" destination="ln9-Sd-GKd" id="9Zd-LM-hgl"/>
|
||||
<outlet property="cameraBadgeContainerView" destination="0YT-CK-WjK" id="T31-nq-8PB"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iK1-yG-fEu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ln9-Sd-GKd" customClass="MXKImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="80" height="80"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0YT-CK-WjK">
|
||||
<rect key="frame" x="56" y="56" width="24" height="24"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="capture_avatar" translatesAutoresizingMaskIntoConstraints="NO" id="vGE-Mx-xPX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="24" height="24"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="24" id="AIT-k4-SJZ"/>
|
||||
<constraint firstItem="vGE-Mx-xPX" firstAttribute="leading" secondItem="0YT-CK-WjK" secondAttribute="leading" id="IoL-FC-x3t"/>
|
||||
<constraint firstAttribute="trailing" secondItem="vGE-Mx-xPX" secondAttribute="trailing" id="KDL-CV-LNm"/>
|
||||
<constraint firstItem="vGE-Mx-xPX" firstAttribute="top" secondItem="0YT-CK-WjK" secondAttribute="top" id="PLP-FV-9fe"/>
|
||||
<constraint firstAttribute="bottom" secondItem="vGE-Mx-xPX" secondAttribute="bottom" id="bIo-ge-7Ud"/>
|
||||
<constraint firstAttribute="width" constant="24" id="ugV-gr-fbC"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="0YT-CK-WjK" secondAttribute="trailing" id="8HT-uc-Dd2"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ln9-Sd-GKd" secondAttribute="trailing" id="ABF-Wz-ZZy"/>
|
||||
<constraint firstAttribute="height" constant="80" id="GdE-dy-bxN"/>
|
||||
<constraint firstAttribute="width" constant="80" id="NCu-Xg-4p3"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ln9-Sd-GKd" secondAttribute="bottom" id="NZp-ao-R0Q"/>
|
||||
<constraint firstItem="ln9-Sd-GKd" firstAttribute="top" secondItem="iK1-yG-fEu" secondAttribute="top" id="VZt-Uu-toa"/>
|
||||
<constraint firstItem="ln9-Sd-GKd" firstAttribute="leading" secondItem="iK1-yG-fEu" secondAttribute="leading" id="jEs-Ac-ock"/>
|
||||
<constraint firstAttribute="bottom" secondItem="0YT-CK-WjK" secondAttribute="bottom" id="wIN-TR-RZm"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="iK1-yG-fEu" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="GZx-C7-FF8"/>
|
||||
<constraint firstAttribute="trailing" secondItem="iK1-yG-fEu" secondAttribute="trailing" id="UPO-MZ-XUZ"/>
|
||||
<constraint firstAttribute="bottom" secondItem="iK1-yG-fEu" secondAttribute="bottom" id="g7y-SL-f3s"/>
|
||||
<constraint firstItem="iK1-yG-fEu" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="imn-uW-TTP"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="-433" y="-802"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="capture_avatar" width="25" height="25"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright 2020 Vector Creations Ltd
|
||||
// Copyright 2020 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.
|
||||
|
@ -14,9 +14,11 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
import Foundation
|
||||
|
||||
#include "Config/Common.xcconfig"
|
||||
|
||||
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER)
|
||||
struct RoomAvatarViewData {
|
||||
let roomId: String
|
||||
let roomDisplayName: String?
|
||||
let avatarUrl: String?
|
||||
let mediaManager: MXMediaManager
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
//
|
||||
// Copyright 2020 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objcMembers
|
||||
class RoomCreationIntroCell: MXKRoomBubbleTableViewCell {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Sizing {
|
||||
static var sizes = Set<SizingViewHeight>()
|
||||
static let view: RoomCreationIntroCell = RoomCreationIntroCell(style: .default, reuseIdentifier: nil)
|
||||
}
|
||||
|
||||
static let tapOnAvatarView = "RoomCreationIntroCellTapOnAvatarView"
|
||||
static let tapOnAddTopic = "RoomCreationIntroCellTapOnAddTopic"
|
||||
static let tapOnAddParticipants = "RoomCreationIntroCellTapOnAddParticipants"
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
private weak var bubbleCellContentView: RoomCreationIntroCellContentView?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
|
||||
self.selectionStyle = .none
|
||||
self.setupContentView()
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
|
||||
super.setupViews()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.bubbleCellContentView?.update(theme: theme)
|
||||
}
|
||||
|
||||
// MARK: - Overrides
|
||||
|
||||
override class func defaultReuseIdentifier() -> String! {
|
||||
return String(describing: self)
|
||||
}
|
||||
|
||||
override class func height(for cellData: MXKCellData!, withMaximumWidth maxWidth: CGFloat) -> CGFloat {
|
||||
guard let cellData = cellData else {
|
||||
return 0
|
||||
}
|
||||
|
||||
guard let roomBubbleCellData = cellData as? MXKRoomBubbleCellData else {
|
||||
return 0
|
||||
}
|
||||
|
||||
let height: CGFloat
|
||||
|
||||
let sizingViewHeight = self.findOrCreateSizingViewHeight(from: roomBubbleCellData)
|
||||
|
||||
if let cachedHeight = sizingViewHeight.heights[maxWidth] {
|
||||
height = cachedHeight
|
||||
} else {
|
||||
height = self.contentViewHeight(for: roomBubbleCellData, fitting: maxWidth)
|
||||
sizingViewHeight.heights[maxWidth] = height
|
||||
}
|
||||
|
||||
return height
|
||||
}
|
||||
|
||||
override func render(_ cellData: MXKCellData!) {
|
||||
super.render(cellData)
|
||||
|
||||
guard let bubbleCellContentView = self.bubbleCellContentView else {
|
||||
NSLog("[RoomCreationIntroCell] Fail to load content view")
|
||||
return
|
||||
}
|
||||
|
||||
guard let bubbleData = self.bubbleData,
|
||||
let viewData = self.viewData(from: bubbleData) else {
|
||||
NSLog("[RoomCreationIntroCell] Fail to render \(String(describing: cellData))")
|
||||
return
|
||||
}
|
||||
|
||||
bubbleCellContentView.fill(with: viewData)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private static func findOrCreateSizingViewHeight(from bubbleData: MXKRoomBubbleCellData) -> SizingViewHeight {
|
||||
|
||||
let sizingViewHeight: SizingViewHeight
|
||||
let bubbleDataHashValue = bubbleData.hashValue
|
||||
|
||||
if let foundSizingViewHeight = self.Sizing.sizes.first(where: { (sizingViewHeight) -> Bool in
|
||||
return sizingViewHeight.uniqueIdentifier == bubbleDataHashValue
|
||||
}) {
|
||||
sizingViewHeight = foundSizingViewHeight
|
||||
} else {
|
||||
sizingViewHeight = SizingViewHeight(uniqueIdentifier: bubbleDataHashValue)
|
||||
}
|
||||
|
||||
return sizingViewHeight
|
||||
}
|
||||
|
||||
private class func sizingView() -> RoomCreationIntroCell {
|
||||
return self.Sizing.view
|
||||
}
|
||||
|
||||
private static func contentViewHeight(for cellData: MXKCellData, fitting width: CGFloat) -> CGFloat {
|
||||
let sizingView = self.sizingView()
|
||||
|
||||
sizingView.render(cellData)
|
||||
sizingView.layoutIfNeeded()
|
||||
|
||||
let fittingSize = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height)
|
||||
|
||||
return sizingView.systemLayoutSizeFitting(fittingSize).height
|
||||
}
|
||||
|
||||
private func setupContentView() {
|
||||
guard self.bubbleCellContentView == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
let bubbleCellContentView = RoomCreationIntroCellContentView.instantiate()
|
||||
self.contentView.vc_addSubViewMatchingParent(bubbleCellContentView)
|
||||
self.bubbleCellContentView = bubbleCellContentView
|
||||
|
||||
bubbleCellContentView.roomAvatarView?.action = { [weak self] in
|
||||
self?.notifyDelegate(with: RoomCreationIntroCell.tapOnAvatarView)
|
||||
}
|
||||
|
||||
bubbleCellContentView.didTapTopic = { [weak self] in
|
||||
self?.notifyDelegate(with: RoomCreationIntroCell.tapOnAddTopic)
|
||||
}
|
||||
|
||||
bubbleCellContentView.didTapAddParticipants = { [weak self] in
|
||||
self?.notifyDelegate(with: RoomCreationIntroCell.tapOnAddParticipants)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func viewData(from bubbleData: MXKRoomBubbleCellData) -> RoomCreationIntroViewData? {
|
||||
|
||||
guard let session = bubbleData.mxSession, let roomId = bubbleData.roomId, let room = session.room(withRoomId: roomId) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard let roomSummary = room.summary else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let discussionType: DiscussionType
|
||||
|
||||
if roomSummary.isDirect {
|
||||
if roomSummary.membersCount.members > 2 {
|
||||
discussionType = .directMessage
|
||||
} else {
|
||||
discussionType = .multipleDirectMessage
|
||||
}
|
||||
} else {
|
||||
discussionType = .room(topic: roomSummary.topic)
|
||||
}
|
||||
|
||||
let displayName = roomSummary.displayname ?? ""
|
||||
|
||||
let roomAvatarViewData = RoomAvatarViewData(roomId: roomId,
|
||||
roomDisplayName: displayName,
|
||||
avatarUrl: room.summary.avatar, mediaManager: session.mediaManager)
|
||||
|
||||
return RoomCreationIntroViewData(dicussionType: discussionType, roomDisplayName: displayName, avatarViewData: roomAvatarViewData)
|
||||
}
|
||||
|
||||
private func notifyDelegate(with actionIdentifier: String) {
|
||||
self.delegate.cell(self, didRecognizeAction: actionIdentifier, userInfo: nil)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
//
|
||||
// Copyright 2020 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Reusable
|
||||
|
||||
final class RoomCreationIntroCellContentView: UIView, NibLoadable, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet weak var roomAvatarView: RoomAvatarView!
|
||||
@IBOutlet weak var titleLabel: UILabel!
|
||||
@IBOutlet weak var informationLabel: UILabel!
|
||||
|
||||
@IBOutlet weak var addParticipantsContainerView: UIView!
|
||||
@IBOutlet weak var addParticipantsButton: UIButton!
|
||||
@IBOutlet weak var addParticipantsLabel: UILabel!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
private var viewData: RoomCreationIntroViewData?
|
||||
|
||||
private var informationTextDefaultAttributes: [NSAttributedString.Key: Any] {
|
||||
return [.foregroundColor: self.theme.textSecondaryColor]
|
||||
}
|
||||
|
||||
private var informationTextBoldAttributes: [NSAttributedString.Key: Any] {
|
||||
return [.foregroundColor: self.theme.textSecondaryColor,
|
||||
.font: UIFont.boldSystemFont(ofSize: self.informationLabel.font.pointSize)
|
||||
]
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var didTapTopic: (() -> Void)?
|
||||
var didTapAddParticipants: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
static func instantiate() -> RoomCreationIntroCellContentView {
|
||||
let view = RoomCreationIntroCellContentView.loadFromNib()
|
||||
view.theme = ThemeService.shared().theme
|
||||
return view
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
|
||||
self.setupInformationTextTapGestureRecognizer()
|
||||
|
||||
self.addParticipantsButton.layer.masksToBounds = true
|
||||
self.addParticipantsButton.addTarget(self, action: #selector(socialButtonAction(_:)), for: .touchUpInside)
|
||||
|
||||
self.addParticipantsLabel.text = VectorL10n.roomIntroCellAddParticipantsAction
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
|
||||
self.addParticipantsButton.layer.cornerRadius = self.addParticipantsButton.bounds.height/2.0
|
||||
|
||||
// Fix RoomAvatarView layoutSubviews not triggered issue
|
||||
self.roomAvatarView.setNeedsLayout()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(with viewData: RoomCreationIntroViewData) {
|
||||
self.viewData = viewData
|
||||
self.titleLabel.text = viewData.roomDisplayName
|
||||
self.informationLabel.attributedText = self.buildInformationText()
|
||||
|
||||
let hideAddParticipants: Bool
|
||||
|
||||
switch viewData.dicussionType {
|
||||
case .room(topic: _):
|
||||
hideAddParticipants = false
|
||||
default:
|
||||
hideAddParticipants = true
|
||||
}
|
||||
|
||||
self.addParticipantsContainerView.isHidden = hideAddParticipants
|
||||
|
||||
self.roomAvatarView?.fill(with: viewData.avatarViewData)
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.titleLabel.textColor = theme.textPrimaryColor
|
||||
|
||||
self.informationLabel.attributedText = self.buildInformationText()
|
||||
|
||||
self.roomAvatarView?.update(theme: theme)
|
||||
|
||||
self.addParticipantsButton.vc_setBackgroundColor(theme.secondaryCircleButtonBackgroundColor, for: .normal)
|
||||
|
||||
self.addParticipantsLabel.textColor = theme.textPrimaryColor
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func buildInformationText() -> NSAttributedString? {
|
||||
guard let viewData = self.viewData else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let informationAttributedText: NSAttributedString
|
||||
|
||||
switch viewData.dicussionType {
|
||||
case .room(topic: let topic):
|
||||
informationAttributedText = self.buildRoomInformationText(with: viewData.roomDisplayName, topic: topic)
|
||||
case .directMessage:
|
||||
informationAttributedText = self.buildDMInformationText(with: viewData.roomDisplayName, isDirect: true)
|
||||
case .multipleDirectMessage:
|
||||
informationAttributedText = self.buildDMInformationText(with: viewData.roomDisplayName, isDirect: false)
|
||||
}
|
||||
|
||||
return informationAttributedText
|
||||
}
|
||||
|
||||
private func buildRoomInformationText(with roomName: String, topic: String?) -> NSAttributedString {
|
||||
|
||||
let attributedString = NSMutableAttributedString()
|
||||
|
||||
let firstSentencePart1 = NSAttributedString(string: VectorL10n.roomIntroCellInformationRoomSentence1Part1, attributes: informationTextDefaultAttributes)
|
||||
let firstSentencePart2 = NSAttributedString(string: roomName, attributes: informationTextBoldAttributes)
|
||||
let firstSentencePart3 = NSAttributedString(string: VectorL10n.roomIntroCellInformationRoomSentence1Part3, attributes: informationTextDefaultAttributes)
|
||||
|
||||
attributedString.append(firstSentencePart1)
|
||||
attributedString.append(firstSentencePart2)
|
||||
attributedString.append(firstSentencePart3)
|
||||
|
||||
if let topic = topic, topic.isEmpty == false {
|
||||
attributedString.append(NSAttributedString(string: VectorL10n.roomIntroCellInformationRoomWithTopicSentence2(topic), attributes: informationTextDefaultAttributes))
|
||||
} else {
|
||||
let secondSentencePart1 = NSAttributedString(string: VectorL10n.roomIntroCellInformationRoomWithoutTopicSentence2Part1, attributes: [.foregroundColor: self.theme.tintColor])
|
||||
let secondSentencePart2 = NSAttributedString(string: VectorL10n.roomIntroCellInformationRoomWithoutTopicSentence2Part2, attributes: informationTextDefaultAttributes)
|
||||
attributedString.append(secondSentencePart1)
|
||||
attributedString.append(secondSentencePart2)
|
||||
}
|
||||
|
||||
return attributedString
|
||||
}
|
||||
|
||||
private func buildDMInformationText(with roomName: String, isDirect: Bool) -> NSAttributedString {
|
||||
|
||||
let attributedString = NSMutableAttributedString()
|
||||
|
||||
let firstSentencePart1 = NSAttributedString(string: VectorL10n.roomIntroCellInformationDmSentence1Part1)
|
||||
let firstSentencePart2 = NSAttributedString(string: roomName, attributes: informationTextBoldAttributes)
|
||||
let firstSentencePart3 = NSAttributedString(string: VectorL10n.roomIntroCellInformationDmSentence1Part3)
|
||||
|
||||
attributedString.append(firstSentencePart1)
|
||||
attributedString.append(firstSentencePart2)
|
||||
attributedString.append(firstSentencePart3)
|
||||
|
||||
if isDirect {
|
||||
attributedString.append(NSAttributedString(string: VectorL10n.roomIntroCellInformationDmSentence2, attributes: informationTextDefaultAttributes))
|
||||
} else {
|
||||
attributedString.append(NSAttributedString(string: VectorL10n.roomIntroCellInformationMultipleDmSentence2, attributes: informationTextDefaultAttributes))
|
||||
}
|
||||
|
||||
return attributedString
|
||||
}
|
||||
|
||||
private func setupInformationTextTapGestureRecognizer() {
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleInformationTextTap(_:)))
|
||||
self.informationLabel.isUserInteractionEnabled = true
|
||||
self.informationLabel.addGestureRecognizer(tapGestureRecognizer)
|
||||
}
|
||||
|
||||
@objc private func handleInformationTextTap(_ gestureRecognizer: UITapGestureRecognizer) {
|
||||
guard let viewData = self.viewData else {
|
||||
return
|
||||
}
|
||||
|
||||
if case DiscussionType.room(topic: let topic) = viewData.dicussionType {
|
||||
// There is no topic defined
|
||||
if topic.isEmptyOrNil {
|
||||
self.didTapTopic?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func socialButtonAction(_ sender: UIButton) {
|
||||
self.didTapAddParticipants?()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="xPm-ze-qk2" customClass="RoomCreationIntroCellContentView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="344"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jkt-jp-37F">
|
||||
<rect key="frame" x="0.0" y="44" width="414" height="300"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wtp-15-hS6" customClass="RoomAvatarView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="20" y="20" width="60" height="60"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="60" id="Aln-Di-uTw"/>
|
||||
<constraint firstAttribute="width" secondItem="wtp-15-hS6" secondAttribute="height" multiplier="1:1" id="Top-ny-sdA"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Claire and Rok" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wqj-EU-qUt">
|
||||
<rect key="frame" x="20" y="100" width="374" height="48"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="22"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="751" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TDp-rA-Avj">
|
||||
<rect key="frame" x="20" y="168" width="374" height="19.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="K9n-ob-cYq">
|
||||
<rect key="frame" x="0.0" y="197.5" width="414" height="102.5"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="vqr-rO-xig">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="102.5"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cNh-oK-dtK">
|
||||
<rect key="frame" x="35" y="15" width="48" height="48"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="cNh-oK-dtK" secondAttribute="height" multiplier="1:1" id="p7E-P4-ufe"/>
|
||||
<constraint firstAttribute="height" constant="48" id="ylD-7Z-ahX"/>
|
||||
</constraints>
|
||||
<state key="normal" image="add_participants"/>
|
||||
</button>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add people" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OkY-wc-0Qp">
|
||||
<rect key="frame" x="20" y="73" width="394" height="19.5"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="medium" pointSize="16"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="250" id="3Ba-V0-jSh"/>
|
||||
<constraint firstItem="cNh-oK-dtK" firstAttribute="top" secondItem="vqr-rO-xig" secondAttribute="top" constant="15" id="5N6-GX-lYn"/>
|
||||
<constraint firstItem="OkY-wc-0Qp" firstAttribute="leading" secondItem="vqr-rO-xig" secondAttribute="leading" constant="20" id="EI3-rH-kQK"/>
|
||||
<constraint firstAttribute="trailing" secondItem="OkY-wc-0Qp" secondAttribute="trailing" id="WwI-zv-K0q"/>
|
||||
<constraint firstItem="OkY-wc-0Qp" firstAttribute="top" secondItem="cNh-oK-dtK" secondAttribute="bottom" constant="10" id="jIX-g5-wmI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="OkY-wc-0Qp" secondAttribute="bottom" constant="10" id="lvA-bq-nIM"/>
|
||||
<constraint firstItem="cNh-oK-dtK" firstAttribute="leading" secondItem="vqr-rO-xig" secondAttribute="leading" constant="35" id="sRa-ZU-hTI"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="vqr-rO-xig" firstAttribute="width" secondItem="K9n-ob-cYq" secondAttribute="width" id="1CW-sN-7xK"/>
|
||||
</constraints>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="wqj-EU-qUt" firstAttribute="top" secondItem="wtp-15-hS6" secondAttribute="bottom" constant="20" id="0y6-2h-aFM"/>
|
||||
<constraint firstItem="TDp-rA-Avj" firstAttribute="trailing" secondItem="wqj-EU-qUt" secondAttribute="trailing" id="1ZO-o5-2A4"/>
|
||||
<constraint firstAttribute="trailing" secondItem="K9n-ob-cYq" secondAttribute="trailing" id="5gP-FU-fRl"/>
|
||||
<constraint firstItem="K9n-ob-cYq" firstAttribute="top" secondItem="TDp-rA-Avj" secondAttribute="bottom" constant="10" id="B9v-Dr-Q9q"/>
|
||||
<constraint firstItem="wqj-EU-qUt" firstAttribute="leading" secondItem="jkt-jp-37F" secondAttribute="leading" constant="20" id="GHr-FH-49S"/>
|
||||
<constraint firstItem="TDp-rA-Avj" firstAttribute="leading" secondItem="wqj-EU-qUt" secondAttribute="leading" id="JJX-QI-iJ8"/>
|
||||
<constraint firstItem="wtp-15-hS6" firstAttribute="leading" secondItem="jkt-jp-37F" secondAttribute="leading" constant="20" id="Qqo-k9-TXG"/>
|
||||
<constraint firstItem="TDp-rA-Avj" firstAttribute="top" secondItem="wqj-EU-qUt" secondAttribute="bottom" constant="20" id="YPX-0Q-m34"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wqj-EU-qUt" secondAttribute="trailing" constant="20" id="ZC9-Dk-wCD"/>
|
||||
<constraint firstItem="K9n-ob-cYq" firstAttribute="leading" secondItem="jkt-jp-37F" secondAttribute="leading" id="fgQ-ad-PuY"/>
|
||||
<constraint firstAttribute="bottom" secondItem="K9n-ob-cYq" secondAttribute="bottom" id="hgy-wg-apj"/>
|
||||
<constraint firstItem="wtp-15-hS6" firstAttribute="top" secondItem="jkt-jp-37F" secondAttribute="top" constant="20" id="nDc-kc-mWW"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="zdS-aq-AJK"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstItem="zdS-aq-AJK" firstAttribute="bottom" secondItem="jkt-jp-37F" secondAttribute="bottom" id="3pz-3Z-ATi"/>
|
||||
<constraint firstItem="jkt-jp-37F" firstAttribute="leading" secondItem="xPm-ze-qk2" secondAttribute="leading" id="OMN-cg-0q1"/>
|
||||
<constraint firstItem="jkt-jp-37F" firstAttribute="top" secondItem="zdS-aq-AJK" secondAttribute="top" id="nAD-3n-pxw"/>
|
||||
<constraint firstAttribute="trailing" secondItem="jkt-jp-37F" secondAttribute="trailing" id="uQA-MV-xX8"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<connections>
|
||||
<outlet property="addParticipantsButton" destination="cNh-oK-dtK" id="7VI-Ie-gha"/>
|
||||
<outlet property="addParticipantsContainerView" destination="vqr-rO-xig" id="6Po-Pm-UCJ"/>
|
||||
<outlet property="addParticipantsLabel" destination="OkY-wc-0Qp" id="a5c-6A-iwk"/>
|
||||
<outlet property="informationLabel" destination="TDp-rA-Avj" id="W02-sT-BeC"/>
|
||||
<outlet property="roomAvatarView" destination="wtp-15-hS6" id="PMd-6Q-Efa"/>
|
||||
<outlet property="titleLabel" destination="wqj-EU-qUt" id="0KA-uz-4hC"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="-380" y="-155"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="add_participants" width="24" height="24"/>
|
||||
<systemColor name="systemBackgroundColor">
|
||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
</resources>
|
||||
</document>
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// Copyright 2020 Vector Creations Ltd
|
||||
// Copyright 2020 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.
|
||||
|
@ -14,9 +14,16 @@
|
|||
// limitations under the License.
|
||||
//
|
||||
|
||||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
import Foundation
|
||||
|
||||
#include "Config/Common.xcconfig"
|
||||
enum DiscussionType {
|
||||
case directMessage
|
||||
case multipleDirectMessage
|
||||
case room(topic: String?)
|
||||
}
|
||||
|
||||
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).shareExtension
|
||||
struct RoomCreationIntroViewData {
|
||||
let dicussionType: DiscussionType
|
||||
let roomDisplayName: String
|
||||
let avatarViewData: RoomAvatarViewData
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface RoomsViewController ()<RoomsDirectoryCoordinatorBridgePresenterDelegate>
|
||||
@interface RoomsViewController ()
|
||||
{
|
||||
RecentsDataSource *recentsDataSource;
|
||||
|
||||
|
@ -30,8 +30,6 @@
|
|||
UIView* footerSpinnerView;
|
||||
}
|
||||
|
||||
@property (nonatomic, strong) RoomsDirectoryCoordinatorBridgePresenter *roomsDirectoryCoordinatorBridgePresenter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomsViewController
|
||||
|
@ -128,9 +126,7 @@
|
|||
|
||||
- (void)onPlusButtonPressed
|
||||
{
|
||||
self.roomsDirectoryCoordinatorBridgePresenter = [[RoomsDirectoryCoordinatorBridgePresenter alloc] initWithSession:self.mainSession dataSource:[recentsDataSource.publicRoomsDirectoryDataSource copy]];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter.delegate = self;
|
||||
[self.roomsDirectoryCoordinatorBridgePresenter presentFrom:self animated:YES];
|
||||
[self showRoomDirectory];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
@ -263,38 +259,6 @@
|
|||
[self openPublicRoom:publicRoom];
|
||||
}
|
||||
|
||||
- (void)openPublicRoom:(MXPublicRoom *)publicRoom
|
||||
{
|
||||
// Check whether the user has already joined the selected public room
|
||||
if ([recentsDataSource.publicRoomsDirectoryDataSource.mxSession roomWithRoomId:publicRoom.roomId])
|
||||
{
|
||||
// Open the public room
|
||||
[[AppDelegate theDelegate] showRoom:publicRoom.roomId andEventId:nil withMatrixSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession restoreInitialDisplay:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Preview the public room
|
||||
if (publicRoom.worldReadable)
|
||||
{
|
||||
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithPublicRoom:publicRoom andSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
|
||||
|
||||
[self startActivityIndicator];
|
||||
|
||||
// Try to get more information about the room before opening its preview
|
||||
[roomPreviewData peekInRoom:^(BOOL succeeded) {
|
||||
[self stopActivityIndicator];
|
||||
|
||||
[[AppDelegate theDelegate].masterTabBarController showRoomPreview:roomPreviewData];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
RoomPreviewData *roomPreviewData = [[RoomPreviewData alloc] initWithPublicRoom:publicRoom andSession:recentsDataSource.publicRoomsDirectoryDataSource.mxSession];
|
||||
[[AppDelegate theDelegate].masterTabBarController showRoomPreview:roomPreviewData];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)triggerDirectoryPagination
|
||||
{
|
||||
if (!recentsDataSource
|
||||
|
@ -351,30 +315,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - RoomsDirectoryCoordinatorBridgePresenterDelegate
|
||||
|
||||
- (void)roomsDirectoryCoordinatorBridgePresenterDelegateDidComplete:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:nil];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)roomsDirectoryCoordinatorBridgePresenterDelegate:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter didSelectRoom:(MXPublicRoom *)room
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
[self openPublicRoom:room];
|
||||
}];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
- (void)roomsDirectoryCoordinatorBridgePresenterDelegateDidTapCreateNewRoom:(RoomsDirectoryCoordinatorBridgePresenter *)coordinatorBridgePresenter
|
||||
{
|
||||
[coordinatorBridgePresenter dismissWithAnimated:YES completion:^{
|
||||
[self createNewRoom];
|
||||
}];
|
||||
self.roomsDirectoryCoordinatorBridgePresenter = nil;
|
||||
}
|
||||
|
||||
#pragma mark - Empty view management
|
||||
|
||||
- (void)updateEmptyView
|
||||
|
|
|
@ -17,5 +17,8 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "App-Common.xcconfig"
|
||||
#include "Common.xcconfig"
|
||||
#include "Pods/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot.release.xcconfig"
|
||||
|
||||
PROVISIONING_PROFILE = $(RIOT_PROVISIONING_PROFILE)
|
||||
PROVISIONING_PROFILE_SPECIFIER = $(RIOT_PROVISIONING_PROFILE_SPECIFIER)
|
123
Riot/target.yml
Normal file
123
Riot/target.yml
Normal file
|
@ -0,0 +1,123 @@
|
|||
name: Riot
|
||||
|
||||
schemes:
|
||||
Riot:
|
||||
analyze:
|
||||
config: Debug
|
||||
archive:
|
||||
config: Release
|
||||
build:
|
||||
targets:
|
||||
Riot:
|
||||
- running
|
||||
- testing
|
||||
- profiling
|
||||
- analyzing
|
||||
- archiving
|
||||
profile:
|
||||
config: Release
|
||||
run:
|
||||
config: Debug
|
||||
disableMainThreadChecker: true
|
||||
test:
|
||||
config: Debug
|
||||
disableMainThreadChecker: true
|
||||
targets:
|
||||
- RiotTests
|
||||
|
||||
targets:
|
||||
Riot:
|
||||
type: application
|
||||
platform: iOS
|
||||
|
||||
dependencies:
|
||||
- target: RiotShareExtension
|
||||
- target: SiriIntents
|
||||
- target: RiotNSE
|
||||
|
||||
configFiles:
|
||||
Debug: Debug.xcconfig
|
||||
Release: Release.xcconfig
|
||||
|
||||
preBuildScripts:
|
||||
- name: ⚠️ SwiftLint
|
||||
runOnlyWhenInstalling: false
|
||||
shell: /bin/sh
|
||||
script: "${PODS_ROOT}/SwiftLint/swiftlint\n"
|
||||
- name: 🛠 SwiftGen
|
||||
runOnlyWhenInstalling: false
|
||||
shell: /bin/sh
|
||||
script: "${PODS_ROOT}/SwiftGen/bin/swiftgen config run --config Tools/SwiftGen/swiftgen-config.yml\n"
|
||||
|
||||
sources:
|
||||
- path: ../Tools
|
||||
excludes:
|
||||
- "Logs"
|
||||
- "Release"
|
||||
- "Templates/*.sh"
|
||||
- path: ../Config
|
||||
- path: .
|
||||
excludes:
|
||||
- "Modules/Room/EmojiPicker/Data/EmojiMart/EmojiJSONStore.swift"
|
||||
- "**/*.strings" # Exclude all strings files
|
||||
|
||||
# Add separately localizable files
|
||||
# Once a language has enough translations (>80%), it must be declared here
|
||||
- path: Assets/en.lproj/InfoPlist.strings
|
||||
- path: Assets/en.lproj/Localizable.strings
|
||||
- path: Assets/en.lproj/Vector.strings
|
||||
- path: Assets/fr.lproj/InfoPlist.strings
|
||||
- path: Assets/fr.lproj/Localizable.strings
|
||||
- path: Assets/fr.lproj/Vector.strings
|
||||
- path: Assets/de.lproj/InfoPlist.strings
|
||||
- path: Assets/de.lproj/Localizable.strings
|
||||
- path: Assets/de.lproj/Vector.strings
|
||||
- path: Assets/sq.lproj/InfoPlist.strings
|
||||
- path: Assets/sq.lproj/Localizable.strings
|
||||
- path: Assets/sq.lproj/Vector.strings
|
||||
- path: Assets/vi.lproj/InfoPlist.strings
|
||||
- path: Assets/vi.lproj/Localizable.strings
|
||||
- path: Assets/vi.lproj/Vector.strings
|
||||
- path: Assets/eu.lproj/InfoPlist.strings
|
||||
- path: Assets/eu.lproj/Localizable.strings
|
||||
- path: Assets/eu.lproj/Vector.strings
|
||||
- path: Assets/bg.lproj/InfoPlist.strings
|
||||
- path: Assets/bg.lproj/Localizable.strings
|
||||
- path: Assets/bg.lproj/Vector.strings
|
||||
- path: Assets/nl.lproj/InfoPlist.strings
|
||||
- path: Assets/nl.lproj/Localizable.strings
|
||||
- path: Assets/nl.lproj/Vector.strings
|
||||
- path: Assets/ca.lproj/InfoPlist.strings
|
||||
- path: Assets/ca.lproj/Localizable.strings
|
||||
- path: Assets/ca.lproj/Vector.strings
|
||||
- path: Assets/zh_Hans.lproj/InfoPlist.strings
|
||||
- path: Assets/zh_Hans.lproj/Localizable.strings
|
||||
- path: Assets/zh_Hans.lproj/Vector.strings
|
||||
- path: Assets/ru.lproj/InfoPlist.strings
|
||||
- path: Assets/ru.lproj/Localizable.strings
|
||||
- path: Assets/ru.lproj/Vector.strings
|
||||
- path: Assets/zh_Hant.lproj/InfoPlist.strings
|
||||
- path: Assets/zh_Hant.lproj/Localizable.strings
|
||||
- path: Assets/zh_Hant.lproj/Vector.strings
|
||||
- path: Assets/es.lproj/InfoPlist.strings
|
||||
- path: Assets/es.lproj/Localizable.strings
|
||||
- path: Assets/es.lproj/Vector.strings
|
||||
- path: Assets/ja.lproj/InfoPlist.strings
|
||||
- path: Assets/ja.lproj/Localizable.strings
|
||||
- path: Assets/ja.lproj/Vector.strings
|
||||
- path: Assets/hu.lproj/InfoPlist.strings
|
||||
- path: Assets/hu.lproj/Localizable.strings
|
||||
- path: Assets/hu.lproj/Vector.strings
|
||||
- path: Assets/pl.lproj/InfoPlist.strings
|
||||
- path: Assets/pl.lproj/Localizable.strings
|
||||
- path: Assets/pl.lproj/Vector.strings
|
||||
- path: Assets/cy.lproj/InfoPlist.strings
|
||||
- path: Assets/cy.lproj/Localizable.strings
|
||||
- path: Assets/cy.lproj/Vector.strings
|
||||
- path: Assets/it.lproj/InfoPlist.strings
|
||||
- path: Assets/it.lproj/Localizable.strings
|
||||
- path: Assets/it.lproj/Vector.strings
|
||||
- path: Assets/et.lproj/InfoPlist.strings
|
||||
- path: Assets/et.lproj/Localizable.strings
|
||||
- path: Assets/et.lproj/Vector.strings
|
||||
- path: Assets/is.lproj/Vector.strings
|
|
@ -17,6 +17,13 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "Config/Common.xcconfig"
|
||||
#include "Config/AppIdentifiers.xcconfig"
|
||||
|
||||
PRODUCT_NAME = RiotNSE
|
||||
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).nse
|
||||
|
||||
INFOPLIST_FILE = RiotNSE/Info.plist
|
||||
|
||||
CODE_SIGN_ENTITLEMENTS = RiotNSE/RiotNSE.entitlements
|
||||
|
||||
SKIP_INSTALL = YES
|
|
@ -17,5 +17,5 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "NSE-Common.xcconfig"
|
||||
#include "Common.xcconfig"
|
||||
#include "Pods/Target Support Files/Pods-RiotPods-RiotNSE/Pods-RiotPods-RiotNSE.debug.xcconfig"
|
|
@ -17,5 +17,10 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "NSE-Common.xcconfig"
|
||||
#include "Common.xcconfig"
|
||||
#include "Pods/Target Support Files/Pods-RiotPods-RiotNSE/Pods-RiotPods-RiotNSE.release.xcconfig"
|
||||
|
||||
PROVISIONING_PROFILE = $(NSE_PROVISIONING_PROFILE)
|
||||
PROVISIONING_PROFILE_SPECIFIER = $(NSE_PROVISIONING_PROFILE_SPECIFIER)
|
||||
|
||||
COPY_PHASE_STRIP = NO
|
60
RiotNSE/target.yml
Normal file
60
RiotNSE/target.yml
Normal file
|
@ -0,0 +1,60 @@
|
|||
name: RiotNSE
|
||||
|
||||
schemes:
|
||||
RiotNSE:
|
||||
analyze:
|
||||
config: Debug
|
||||
archive:
|
||||
config: Release
|
||||
build:
|
||||
targets:
|
||||
RiotNSE:
|
||||
- running
|
||||
- testing
|
||||
- profiling
|
||||
- analyzing
|
||||
- archiving
|
||||
profile:
|
||||
config: Release
|
||||
run:
|
||||
askForAppToLaunch: true
|
||||
config: Debug
|
||||
debugEnabled: false
|
||||
disableMainThreadChecker: true
|
||||
launchAutomaticallySubstyle: 2
|
||||
test:
|
||||
config: Debug
|
||||
disableMainThreadChecker: true
|
||||
|
||||
targets:
|
||||
RiotNSE:
|
||||
platform: iOS
|
||||
type: app-extension
|
||||
|
||||
configFiles:
|
||||
Debug: Debug.xcconfig
|
||||
Release: Release.xcconfig
|
||||
|
||||
sources:
|
||||
- path: .
|
||||
- path: ../Riot/Managers/Settings/RiotSettings.swift
|
||||
- path: ../Config/BuildSettings.swift
|
||||
- path: ../Riot/Utils/DataProtectionHelper.swift
|
||||
- path: ../Config/CommonConfiguration.swift
|
||||
- path: ../Riot/Managers/PushNotification/PushNotificationStore.swift
|
||||
- path: ../Riot/Modules/SetPinCode/PinCodePreferences.swift
|
||||
- path: ../Riot/Generated/InfoPlist.swift
|
||||
- path: ../Riot/Managers/KeyValueStorage/Extensions/Keychain.swift
|
||||
- path: ../Riot/Modules/SetPinCode/SetupBiometrics/BiometricsAuthenticationPresenter.swift
|
||||
- path: ../Riot/Categories/UNUserNotificationCenter.swift
|
||||
- path: ../Riot/Managers/KeyValueStorage/KeyValueStore.swift
|
||||
- path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
|
||||
- path: ../Riot/Categories/Bundle.swift
|
||||
- path: ../Riot/Generated/Strings.swift
|
||||
- path: ../Riot/Generated/Images.swift
|
||||
- path: ../Riot/Managers/KeyValueStorage/KeychainStore.swift
|
||||
- path: ../Riot/Managers/LocalAuthentication/LocalAuthenticationService.swift
|
||||
- path: ../Config/Configurable.swift
|
||||
- path: ../Riot/Utils/Constants.swift
|
||||
- path: ../Riot/Categories/String.swift
|
||||
- path: ../Riot/Categories/Character.swift
|
34
RiotShareExtension/Common.xcconfig
Normal file
34
RiotShareExtension/Common.xcconfig
Normal file
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// Copyright 2020 Vector Creations 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.
|
||||
//
|
||||
|
||||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "Config/AppIdentifiers.xcconfig"
|
||||
|
||||
PRODUCT_NAME = RiotShareExtension
|
||||
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).shareExtension
|
||||
|
||||
INFOPLIST_FILE = RiotShareExtension/SupportingFiles/Info.plist
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
|
||||
|
||||
CODE_SIGN_ENTITLEMENTS = RiotShareExtension/SupportingFiles/RiotShareExtension.entitlements
|
||||
|
||||
APPLICATION_EXTENSION_API_ONLY = YES
|
||||
SKIP_INSTALL = YES
|
||||
SWIFT_OBJC_BRIDGING_HEADER = $(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/RiotShareExtension-Bridging-Header.h
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) IS_SHARE_EXTENSION=1
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks
|
|
@ -17,5 +17,5 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "ShareExtension-Common.xcconfig"
|
||||
#include "Common.xcconfig"
|
||||
#include "Pods/Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension.debug.xcconfig"
|
|
@ -17,5 +17,10 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "ShareExtension-Common.xcconfig"
|
||||
#include "Common.xcconfig"
|
||||
#include "Pods/Target Support Files/Pods-RiotPods-RiotShareExtension/Pods-RiotPods-RiotShareExtension.release.xcconfig"
|
||||
|
||||
PROVISIONING_PROFILE = $(SHARE_EXTENSION_PROVISIONING_PROFILE)
|
||||
PROVISIONING_PROFILE_SPECIFIER = $(SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER)
|
||||
|
||||
COPY_PHASE_STRIP = NO
|
64
RiotShareExtension/target.yml
Normal file
64
RiotShareExtension/target.yml
Normal file
|
@ -0,0 +1,64 @@
|
|||
name: RiotShareExtension
|
||||
|
||||
schemes:
|
||||
RiotShareExtension:
|
||||
analyze:
|
||||
config: Debug
|
||||
archive:
|
||||
config: Release
|
||||
build:
|
||||
targets:
|
||||
RiotShareExtension:
|
||||
- running
|
||||
- testing
|
||||
- profiling
|
||||
- analyzing
|
||||
- archiving
|
||||
profile:
|
||||
config: Release
|
||||
run:
|
||||
askForAppToLaunch: true
|
||||
config: Debug
|
||||
debugEnabled: false
|
||||
disableMainThreadChecker: true
|
||||
launchAutomaticallySubstyle: 2
|
||||
test:
|
||||
config: Debug
|
||||
disableMainThreadChecker: true
|
||||
|
||||
targets:
|
||||
RiotShareExtension:
|
||||
platform: iOS
|
||||
type: app-extension
|
||||
|
||||
configFiles:
|
||||
Debug: Debug.xcconfig
|
||||
Release: Release.xcconfig
|
||||
|
||||
sources:
|
||||
- path: .
|
||||
- path: ../Riot/Modules/Common/SegmentedViewController/SegmentedViewController.m
|
||||
- path: ../Riot/Categories/Bundle.swift
|
||||
- path: ../Riot/Managers/Theme/
|
||||
- path: ../Riot/Utils/AvatarGenerator.m
|
||||
- path: ../Riot/Generated/InfoPlist.swift
|
||||
- path: ../Config/BuildSettings.swift
|
||||
- path: ../Riot/Categories/Character.swift
|
||||
- path: ../Riot/Categories/MXRoom+Riot.m
|
||||
- path: ../Config/Configurable.swift
|
||||
- path: ../Config/CommonConfiguration.swift
|
||||
- path: ../Riot/Utils/UserNameColorGenerator.swift
|
||||
- path: ../Riot/Categories/MXRoomSummary+Riot.m
|
||||
- path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
|
||||
- path: ../Riot/Managers/KeyValueStorage
|
||||
- path: ../Riot/Managers/Settings/RiotSettings.swift
|
||||
- path: ../Riot/Categories/UIColor.swift
|
||||
- path: ../Riot/Categories/UISearchBar.swift
|
||||
- path: ../Riot/Categories/String.swift
|
||||
- path: ../Riot/Modules/Common/Recents/CellData/RecentCellData.m
|
||||
- path: ../Riot/Modules/Common/SegmentedViewController/SegmentedViewController.xib
|
||||
buildPhase: resources
|
||||
- path: ../Riot/Assets/en.lproj/Vector.strings
|
||||
buildPhase: resources
|
||||
- path: ../Riot/Assets/SharedImages.xcassets
|
||||
buildPhase: resources
|
52
RiotTests/target.yml
Normal file
52
RiotTests/target.yml
Normal file
|
@ -0,0 +1,52 @@
|
|||
name: RiotTests
|
||||
|
||||
schemes:
|
||||
RiotTests:
|
||||
analyze:
|
||||
config: Debug
|
||||
archive:
|
||||
config: Release
|
||||
build:
|
||||
targets:
|
||||
RiotTests:
|
||||
- running
|
||||
- testing
|
||||
- profiling
|
||||
- analyzing
|
||||
- archiving
|
||||
profile:
|
||||
config: Release
|
||||
run:
|
||||
config: Debug
|
||||
disableMainThreadChecker: true
|
||||
test:
|
||||
config: Debug
|
||||
disableMainThreadChecker: true
|
||||
targets:
|
||||
- RiotTests
|
||||
|
||||
targets:
|
||||
RiotTests:
|
||||
type: bundle.unit-test
|
||||
platform: iOS
|
||||
|
||||
dependencies:
|
||||
- target: Riot
|
||||
|
||||
settings:
|
||||
base:
|
||||
BUNDLE_LOADER: $(TEST_HOST)
|
||||
FRAMEWORK_SEARCH_PATHS: $(SDKROOT)/Developer/Library/Frameworks $(inherited)
|
||||
INFOPLIST_FILE: Info.plist
|
||||
LD_RUNPATH_SEARCH_PATHS: $(inherited) @executable_path/Frameworks @loader_path/Frameworks
|
||||
PRODUCT_BUNDLE_IDENTIFIER: org.matrix.$(PRODUCT_NAME:rfc1034identifier)
|
||||
PRODUCT_NAME: RiotTests
|
||||
SWIFT_OBJC_BRIDGING_HEADER: RiotTests-Bridging-Header.h
|
||||
TEST_HOST: $(BUILT_PRODUCTS_DIR)/Riot.app/Riot
|
||||
configs:
|
||||
Debug:
|
||||
Release:
|
||||
PROVISIONING_PROFILE: $(RIOT_PROVISIONING_PROFILE)
|
||||
PROVISIONING_PROFILE_SPECIFIER: $(RIOT_PROVISIONING_PROFILE_SPECIFIER)
|
||||
|
||||
sources: .
|
|
@ -17,6 +17,14 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "Config/Common.xcconfig"
|
||||
#include "Config/AppIdentifiers.xcconfig"
|
||||
|
||||
PRODUCT_NAME= SiriIntents
|
||||
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).SiriIntents
|
||||
|
||||
INFOPLIST_FILE = SiriIntents/Info.plist
|
||||
|
||||
CODE_SIGN_ENTITLEMENTS = SiriIntents/SiriIntents.entitlements
|
||||
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/Frameworks @executable_path/../../Frameworks
|
||||
SKIP_INSTALL = YES
|
|
@ -17,5 +17,5 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "SiriIntents-Common.xcconfig"
|
||||
#include "Common.xcconfig"
|
||||
#include "Pods/Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents.debug.xcconfig"
|
|
@ -17,5 +17,10 @@
|
|||
// Configuration settings file format documentation can be found at:
|
||||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
#include "SiriIntents-Common.xcconfig"
|
||||
#include "Common.xcconfig"
|
||||
#include "Pods/Target Support Files/Pods-RiotPods-SiriIntents/Pods-RiotPods-SiriIntents.release.xcconfig"
|
||||
|
||||
PROVISIONING_PROFILE = $(SIRI_INTENTS_PROVISIONING_PROFILE)
|
||||
PROVISIONING_PROFILE_SPECIFIER = $(SIRI_INTENTS_PROVISIONING_PROFILE_SPECIFIER)
|
||||
|
||||
COPY_PHASE_STRIP = NO
|
50
SiriIntents/target.yml
Normal file
50
SiriIntents/target.yml
Normal file
|
@ -0,0 +1,50 @@
|
|||
name: SiriIntents
|
||||
|
||||
schemes:
|
||||
SiriIntents:
|
||||
analyze:
|
||||
config: Debug
|
||||
archive:
|
||||
config: Release
|
||||
build:
|
||||
targets:
|
||||
SiriIntents:
|
||||
- running
|
||||
- testing
|
||||
- profiling
|
||||
- analyzing
|
||||
- archiving
|
||||
profile:
|
||||
config: Release
|
||||
run:
|
||||
askForAppToLaunch: true
|
||||
config: Debug
|
||||
debugEnabled: false
|
||||
disableMainThreadChecker: true
|
||||
launchAutomaticallySubstyle: 2
|
||||
test:
|
||||
config: Debug
|
||||
disableMainThreadChecker: true
|
||||
|
||||
targets:
|
||||
SiriIntents:
|
||||
platform: iOS
|
||||
type: app-extension
|
||||
|
||||
dependencies:
|
||||
- sdk: Intents.framework
|
||||
|
||||
configFiles:
|
||||
Debug: Debug.xcconfig
|
||||
Release: Release.xcconfig
|
||||
|
||||
sources:
|
||||
- path: .
|
||||
- path: ../Riot/Generated/InfoPlist.swift
|
||||
- path: ../Riot/Categories/Bundle.swift
|
||||
- path: ../Config/CommonConfiguration.swift
|
||||
- path: ../Config/BuildSettings.swift
|
||||
- path: ../Config/Configurable.swift
|
||||
- path: ../Riot/Managers/Settings/RiotSettings.swift
|
||||
- path: ../Riot/Managers/EncryptionKeyManager/EncryptionKeyManager.swift
|
||||
- path: ../Riot/Managers/KeyValueStorage
|
5
Tools/XcodeGen/postGenCommand.sh
Normal file
5
Tools/XcodeGen/postGenCommand.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
|
||||
# This script is invoked by xcodegen for running post commands
|
||||
|
||||
cp IDETemplateMacros.plist Riot.xcodeproj/xcshareddata/
|
|
@ -64,6 +64,7 @@ platform :ios do
|
|||
|
||||
desc "Build the app for simulator to ensure it compiles"
|
||||
lane :build do |options|
|
||||
xcodegen(spec: "project.yml")
|
||||
cocoapods
|
||||
|
||||
app_name = "Riot"
|
||||
|
@ -109,11 +110,12 @@ platform :ios do
|
|||
# Generate xcodebuild additional arguments
|
||||
xcargs_hash = {
|
||||
"GCC_PREPROCESSOR_DEFINITIONS" => "$(GCC_PREPROCESSOR_DEFINITIONS) #{additional_preprocessor_definitions}",
|
||||
"-UseNewBuildSystem" => "NO",
|
||||
}
|
||||
|
||||
xcargs = xcargs_hash.map { |k, v| "#{k}=#{v.shellescape}" }.join(" ")
|
||||
|
||||
xcodegen(spec: "project.yml")
|
||||
|
||||
# Clear derived data
|
||||
clear_derived_data(derived_data_path: ENV["DERIVED_DATA_PATH"])
|
||||
|
||||
|
@ -125,11 +127,6 @@ platform :ios do
|
|||
# Update build number
|
||||
update_build_number(build_number: build_number)
|
||||
|
||||
# On Xcode 10 with 'Parallelize Build' option on, archive randomly fails with error title "** ARCHIVE FAILED **" for various reasons.
|
||||
# Errors only occur on CocoaPods frameworks and the observed command that failed are CodeSign, CpHeader, CpResource, SetOwnerAndGroup.
|
||||
# To make archive reliable disable 'Parallelize Build' option of scheme ENV["SCHEME"] for the moment.
|
||||
disable_parallelize_builds
|
||||
|
||||
# Perform a pod install
|
||||
cocoapods(repo_update: true)
|
||||
|
||||
|
@ -277,8 +274,9 @@ platform :ios do
|
|||
private_lane :update_build_number do |options|
|
||||
build_number = options[:build_number]
|
||||
|
||||
increment_build_number_in_xcodeproj(
|
||||
build_number: build_number,
|
||||
update_file_content(
|
||||
"../Config/AppIdentifiers.xcconfig",
|
||||
/(CURRENT_PROJECT_VERSION\s*=)\s*.*/ => "\\1 #{build_number}"
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -300,17 +298,6 @@ platform :ios do
|
|||
preprocessor_definitions
|
||||
end
|
||||
|
||||
desc "Disable 'Parallelize Build' option of build action of main scheme"
|
||||
private_lane :disable_parallelize_builds do
|
||||
project_path = "../#{ENV["PROJECT_PATH"]}"
|
||||
scheme_name = ENV["SCHEME"]
|
||||
|
||||
scheme_path = Xcodeproj::XCScheme.shared_data_dir(project_path) + "#{scheme_name}.xcscheme"
|
||||
scheme = Xcodeproj::XCScheme.new(scheme_path)
|
||||
scheme.build_action.xml_element.attributes["parallelizeBuildables"] = "NO"
|
||||
scheme.save_as(project_path, scheme_name)
|
||||
end
|
||||
|
||||
desc "Edit the Podfile in order to point MatrixKit and MatrixSDK to the appropriate branches."
|
||||
private_lane :edit_podfile do |options|
|
||||
require 'net/http'
|
||||
|
@ -350,3 +337,16 @@ platform :ios do
|
|||
.last # Latest ref found, in "version:refname" semantic order
|
||||
end
|
||||
end
|
||||
|
||||
# Update an arbitrary file by applying some RegExp replacements to its content
|
||||
#
|
||||
# @param [String] file The path to the file that needs replacing
|
||||
# @param [Hash<RegExp, String>] replacements A list of replacements to apply
|
||||
#
|
||||
def update_file_content(file, replacements)
|
||||
content = File.read(file)
|
||||
replacements.each do |pattern, replacement|
|
||||
content.gsub!(pattern, replacement)
|
||||
end
|
||||
File.write(file, content)
|
||||
end
|
|
@ -3,3 +3,4 @@
|
|||
# Ensure this file is checked in to source control!
|
||||
|
||||
gem 'fastlane-plugin-versioning'
|
||||
gem 'fastlane-plugin-xcodegen'
|
||||
|
|
33
project.yml
Normal file
33
project.yml
Normal file
|
@ -0,0 +1,33 @@
|
|||
name: Riot
|
||||
attributes:
|
||||
ORGANIZATIONNAME: matrix.org
|
||||
|
||||
configs:
|
||||
Debug: debug
|
||||
Release: release
|
||||
|
||||
fileGroups:
|
||||
- README.md
|
||||
- CHANGES.rst
|
||||
- AUTHORS.rst
|
||||
- Podfile
|
||||
- project.yml
|
||||
|
||||
configFiles:
|
||||
Debug: Config/Project-Debug.xcconfig
|
||||
Release: Config/Project-Release.xcconfig
|
||||
|
||||
options:
|
||||
defaultConfig: Release
|
||||
groupSortPosition: bottom
|
||||
transitivelyLinkDependencies: false
|
||||
createIntermediateGroups: true
|
||||
useBaseInternationalization: true
|
||||
postGenCommand: sh Tools/XcodeGen/postGenCommand.sh
|
||||
|
||||
include:
|
||||
- path: Riot/target.yml
|
||||
- path: RiotTests/target.yml
|
||||
- path: RiotShareExtension/target.yml
|
||||
- path: SiriIntents/target.yml
|
||||
- path: RiotNSE/target.yml
|
Loading…
Reference in a new issue