Merge branch 'develop' into element_4014

# Conflicts:
#	Riot.xcodeproj/project.pbxproj
#	Riot/Modules/Room/DataSources/RoomDataSource.m
This commit is contained in:
Gil Eluard 2021-02-25 15:49:53 +01:00
commit 419efd886a
76 changed files with 9801 additions and 8280 deletions

3
.gitignore vendored
View file

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

View file

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

View file

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

View 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

View file

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

View 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

View 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

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View file

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

View file

@ -50,3 +50,10 @@ extension String {
return components(separatedBy: .whitespaces).joined()
}
}
extension Optional where Wrapped == String {
var isEmptyOrNil: Bool {
return self?.isEmpty ?? true
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

@ -165,6 +165,16 @@
*/
- (void)muteEditedRoomNotifications:(BOOL)mute;
/**
Show room directory.
*/
- (void)showRoomDirectory;
/**
Show a public room.
*/
- (void)openPublicRoom:(MXPublicRoom *)publicRoom;
#pragma mark - Scrolling
/**

View file

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

View file

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

View file

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

View file

@ -27,7 +27,8 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag)
RoomBubbleCellDataTagKeyVerificationRequestIncomingApproval,
RoomBubbleCellDataTagKeyVerificationRequest,
RoomBubbleCellDataTagKeyVerificationConclusion,
RoomBubbleCellDataTagCall
RoomBubbleCellDataTagCall,
RoomBubbleCellDataTagRoomCreationIntro
};
/**

View file

@ -217,6 +217,11 @@ static NSAttributedString *timestampVerticalWhitespace = nil;
return YES;
}
if (self.tag == RoomBubbleCellDataTagRoomCreationIntro)
{
return NO;
}
return [super hasNoDisplay];
}

View file

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

View file

@ -21,4 +21,6 @@ limitations under the License.
*/
@interface RoomFilesViewController : MXKRoomViewController
@property (nonatomic) BOOL showCancelBarButtonItem;
@end

View file

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

View file

@ -84,6 +84,8 @@
*/
@property (nonatomic) BOOL enableMention;
@property (nonatomic) BOOL showCancelBarButtonItem;
/**
The delegate for the view controller.
*/

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View 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
View 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: .

View file

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

View file

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

View file

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

View file

@ -0,0 +1,5 @@
#!/bin/bash
# This script is invoked by xcodegen for running post commands
cp IDETemplateMacros.plist Riot.xcodeproj/xcshareddata/

View file

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

View file

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