Merge pull request #4314 from vector-im/release/1.3.6/release

Release 1.3.6
This commit is contained in:
manuroe 2021-05-07 16:29:07 +02:00 committed by GitHub
commit 4d5a544214
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
166 changed files with 5874 additions and 1502 deletions

View file

@ -9,6 +9,10 @@ on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
env:
# Make the git branch for a PR available to our Fastfile
MX_GIT_BRANCH: ${{ github.event.pull_request.head.ref }}
jobs:
build:
name: Build
@ -39,7 +43,7 @@ jobs:
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Use right MatrixKit and MatrixSDK versions
run: bundle exec fastlane point_dependencies_to_pending_releases
run: bundle exec fastlane point_dependencies_to_related_branches
# Main step
- name: Build iOS simulator
@ -75,7 +79,7 @@ jobs:
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Use right MatrixKit and MatrixSDK versions
run: bundle exec fastlane point_dependencies_to_pending_releases
run: bundle exec fastlane point_dependencies_to_related_branches
# Main step
- name: Unit tests

View file

@ -1,3 +1,42 @@
Changes in 1.3.6 (2021-05-07)
=================================================
✨ Features
*
🙌 Improvements
* Jitsi: Use Jitsi server from homeserver's Well Known, if present, to create conferences (#3158).
* RoomMemberDetailsVC: Enable / disable "Hide all messages from this user" from settings (#4281).
* RoomVC: Show / Hide More and Report Content contextual menu from settings (#4285).
* SettingsVC: Show / hide NSFW and decrypted content options from build settings (#4290).
* RoomVC: Tweaked Scroll to Bottom FAB button (#4272).
* DesignKit: Introduce a new framework to manage design components.
* Add Jitsi widget remove banner for privileged users.
* Update "Jump to unread" banner to a pill style button.
* CallVC: Add transfer button.
🐛 Bugfix
* RoomVC: Avoid navigation to integration management using integration popup with settings set to integration disabled (#4261).
* RiotSettings: Logging out resets RiotSettings (#4259).
* RoomVC: Crash in `setScrollToBottomHidden` method (#4270).
* Notifications: Make them work in debug mode (#4274).
* VoIP: Fix call bar layout issue (#4300).
⚠️ API Changes
*
🗣 Translations
*
🧱 Build
* GH Actions: Make jobs use the right version of MatrixKit and MatrixSDK.
Others
*
Improvements:
* Upgrade MatrixKit version ([v0.14.11](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.14.11)).
Changes in 1.3.5 (2021-04-22)
=================================================

View file

@ -44,6 +44,9 @@ class AppConfiguration: CommonConfiguration {
// Each room member will be considered as a potential contact.
MXKContactManager.shared().contactManagerMXRoomSource = MXKContactManagerMXRoomSource.all
// Use UIKit BackgroundTask for handling background tasks in the SDK
MXSDKOptions.sharedInstance().backgroundModeHandler = MXUIKitBackgroundModeHandler()
// Enable key backup on app
MXSDKOptions.sharedInstance().enableKeyBackupWhenStartingMXCrypto = true
}

View file

@ -22,8 +22,8 @@ APPLICATION_GROUP_IDENTIFIER = group.im.vector
APPLICATION_SCHEME = element
// Version
MARKETING_VERSION = 1.3.5
CURRENT_PROJECT_VERSION = 1.3.5
MARKETING_VERSION = 1.3.6
CURRENT_PROJECT_VERSION = 1.3.6
// Team
@ -32,13 +32,13 @@ DEVELOPMENT_TEAM = 7J4U792NQT
// Provisioning profiles
RIOT_PROVISIONING_PROFILE_SPECIFIER = Vector App Store
RIOT_PROVISIONING_PROFILE = f65e7447-b8a3-46cc-8fba-fa60e55e2511
RIOT_PROVISIONING_PROFILE = 4b43c1ca-3246-4984-828f-165838f5715a
NSE_PROVISIONING_PROFILE_SPECIFIER = "Vector NSE: App Store"
NSE_PROVISIONING_PROFILE = 31dc9316-e029-47fd-81f5-778db07d76a2
NSE_PROVISIONING_PROFILE = de44ca91-4318-4c23-8611-b531793505c2
SHARE_EXTENSION_PROVISIONING_PROFILE_SPECIFIER = "Vector Share Extension: App Store"
SHARE_EXTENSION_PROVISIONING_PROFILE = 1a3be143-50c7-4ae2-834e-00596a053141
SHARE_EXTENSION_PROVISIONING_PROFILE = 546090a2-77ca-4bc2-b904-da5bd97a2f37
SIRI_INTENTS_PROVISIONING_PROFILE_SPECIFIER = "Vector Siri Intents: App Store"
SIRI_INTENTS_PROVISIONING_PROFILE = 18a66f93-ffe1-4008-b343-58350cc65023
SIRI_INTENTS_PROVISIONING_PROFILE = 6951ad31-4850-445a-89c8-b64bca0a1c44

View file

@ -180,7 +180,7 @@ final class BuildSettings: NSObject {
"https://scalar-staging.riot.im/scalar/api",
]
// Jitsi server used outside integrations to create conference calls from the call button in the timeline
static let jitsiServerUrl = NSURL(string: "https://jitsi.riot.im")
static let jitsiServerUrl: URL = URL(string: "https://jitsi.riot.im")!
// MARK: - Features
@ -242,6 +242,8 @@ final class BuildSettings: NSObject {
static let settingsScreenShowChangePassword:Bool = true
static let settingsScreenShowInviteFriends:Bool = true
static let settingsScreenShowEnableStunServerFallback: Bool = true
static let settingsScreenShowNotificationDecodedContentOption: Bool = true
static let settingsScreenShowNsfwRoomsOption: Bool = true
static let settingsSecurityScreenShowSessions:Bool = true
static let settingsSecurityScreenShowSetupBackup:Bool = true
static let settingsSecurityScreenShowRestoreBackup:Bool = true
@ -268,6 +270,12 @@ final class BuildSettings: NSObject {
static let roomScreenAllowMediaLibraryAction: Bool = true
static let roomScreenAllowStickerAction: Bool = true
static let roomScreenAllowFilesAction: Bool = true
// MARK: - Room Contextual Menu
static let roomContextualMenuShowMoreOptionForMessages: Bool = true
static let roomContextualMenuShowMoreOptionForStates: Bool = true
static let roomContextualMenuShowReportContentOption: Bool = true
// MARK: - Room Info Screen
@ -284,6 +292,10 @@ final class BuildSettings: NSObject {
static let roomSettingsScreenShowAdvancedSettings: Bool = true
static let roomSettingsScreenAdvancedShowEncryptToVerifiedOption: Bool = true
// MARK: - Room Member Screen
static let roomMemberScreenShowIgnore: Bool = true
// MARK: - Message
static let messageDetailsAllowShare: Bool = true
static let messageDetailsAllowPermalink: Bool = true

View file

@ -63,9 +63,6 @@ class CommonConfiguration: NSObject, Configurable {
// Disable identicon use
sdkOptions.disableIdenticonUseForUserAvatar = true
// Use UIKit BackgroundTask for handling background tasks in the SDK
sdkOptions.backgroundModeHandler = MXUIKitBackgroundModeHandler()
// Pass httpAdditionalHeaders to the SDK
sdkOptions.httpAdditionalHeaders = BuildSettings.httpAdditionalHeaders

27
DesignKit/Common.xcconfig Normal file
View file

@ -0,0 +1,27 @@
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Config/AppIdentifiers.xcconfig"
PRODUCT_NAME = DesignKit
PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).designkit
INFOPLIST_FILE = DesignKit/Info.plist
SKIP_INSTALL = YES

20
DesignKit/Debug.xcconfig Normal file
View file

@ -0,0 +1,20 @@
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Common.xcconfig"

27
DesignKit/DesignKit.h Normal file
View file

@ -0,0 +1,27 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import <Foundation/Foundation.h>
//! Project version number for DesignKit.
FOUNDATION_EXPORT double DesignKitVersionNumber;
//! Project version string for DesignKit.
FOUNDATION_EXPORT const unsigned char DesignKitVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <DesignKit/PublicHeader.h>

22
DesignKit/Info.plist Normal file
View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
</dict>
</plist>

View file

@ -0,0 +1,20 @@
//
// 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.
//
// Configuration settings file format documentation can be found at:
// https://help.apple.com/xcode/#/dev745c5c974
#include "Common.xcconfig"

View file

@ -0,0 +1,64 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
/// Colors at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1255%3A1104
@objc public protocol Colors {
/// - Focused/Active states
/// - CTAs
var accent: UIColor { get }
/// - Error messages
/// - Content requiring user attention
/// - Notification, alerts
var alert: UIColor { get }
/// - Text
/// - Icons
var primaryContent: UIColor { get }
/// - Text
/// - Icons
var secondaryContent: UIColor { get }
/// - Text
/// - Icons
var tertiaryContent: UIColor { get }
/// - Text
/// - Icons
var quarterlyContent: UIColor { get }
/// Separating line
var separator: UIColor { get }
// Cards, tiles
var tile: UIColor { get }
/// Top navigation background on iOS
var navigation: UIColor { get }
/// Background UI color
var background: UIColor { get }
/// - Names in chat timeline
/// - Avatars default states that include first name letter
var namesAndAvatars: [UIColor] { get }
}

View file

@ -0,0 +1,28 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
/// Theme v2. May be named again as `Theme` when the migration completed.
@objc public protocol ThemeV2 {
/// Colors object
var colors: Colors { get }
/// may contain more design components in future, like icons, audio files etc.
}

View file

@ -0,0 +1,56 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
/// Dark theme colors. Will be a struct when things are more Swifty.
public class DarkColors: Colors {
public let accent: UIColor = UIColor(rgb: 0x0DBD8B)
public let alert: UIColor = UIColor(rgb: 0xFF4B55)
public let primaryContent: UIColor = UIColor(rgb: 0xFFFFFF)
public let secondaryContent: UIColor = UIColor(rgb: 0xA9B2BC)
public let tertiaryContent: UIColor = UIColor(rgb: 0x8E99A4)
public let quarterlyContent: UIColor = UIColor(rgb: 0x6F7882)
public let separator: UIColor = UIColor(rgb: 0x21262C)
public let tile: UIColor = UIColor(rgb: 0x394049)
public let navigation: UIColor = UIColor(rgb: 0x21262C)
public let background: UIColor = UIColor(rgb: 0x15191E)
public let namesAndAvatars: [UIColor] = [
UIColor(rgb: 0x368BD6),
UIColor(rgb: 0xAC3BA8),
UIColor(rgb: 0x03B381),
UIColor(rgb: 0xE64F7A),
UIColor(rgb: 0xFF812D),
UIColor(rgb: 0x2DC2C5),
UIColor(rgb: 0x5C56F5),
UIColor(rgb: 0x74D12C)
]
public init() {}
}

View file

@ -0,0 +1,56 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
/// Light theme colors. Will be a struct when things are more Swifty.
public class LightColors: Colors {
public let accent: UIColor = UIColor(rgb: 0x0DBD8B)
public let alert: UIColor = UIColor(rgb: 0xFF4B55)
public let primaryContent: UIColor = UIColor(rgb: 0x17191C)
public let secondaryContent: UIColor = UIColor(rgb: 0x737D8C)
public let tertiaryContent: UIColor = UIColor(rgb: 0x8D97A5)
public let quarterlyContent: UIColor = UIColor(rgb: 0xC1C6CD)
public let separator: UIColor = UIColor(rgb: 0xE3E8F0)
public let tile: UIColor = UIColor(rgb: 0xF3F8FD)
public let navigation: UIColor = UIColor(rgb: 0xF4F6FA)
public let background: UIColor = UIColor(rgb: 0xFFFFFF)
public let namesAndAvatars: [UIColor] = [
UIColor(rgb: 0x368BD6),
UIColor(rgb: 0xAC3BA8),
UIColor(rgb: 0x03B381),
UIColor(rgb: 0xE64F7A),
UIColor(rgb: 0xFF812D),
UIColor(rgb: 0x2DC2C5),
UIColor(rgb: 0x5C56F5),
UIColor(rgb: 0x74D12C)
]
public init() {}
}

33
DesignKit/target.yml Normal file
View file

@ -0,0 +1,33 @@
name: DesignKit
schemes:
DesignKit:
analyze:
config: Debug
archive:
config: Release
build:
targets:
DesignKit:
- running
- profiling
- analyzing
- archiving
profile:
config: Release
run:
config: Debug
disableMainThreadChecker: true
targets:
DesignKit:
type: framework
platform: iOS
configFiles:
Debug: Debug.xcconfig
Release: Release.xcconfig
sources:
- path: .
- path: ../Riot/Categories/UIColor.swift

View file

@ -68,7 +68,7 @@ If you want to modify MatrixKit and/or MatrixSDK locally and see the result in E
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.
**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`.
**Important**: By working with [XcodeGen](https://github.com/yonaskolb/XcodeGen) you will need to use the _New Build System_ in Xcode, to have your some of the xcconfig variables taken into account. It should be enabled by default on the latest Xcode versions, but if you need to enable it go to Xcode menu and select `File > Workspace Settings… > Build System` and then choose `New Build System`.
### `$matrixKitVersion` Modification

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.10'
$matrixKitVersion = '= 0.14.11'
# $matrixKitVersion = :local
# $matrixKitVersion = {'develop' => 'develop'}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

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

View file

@ -0,0 +1,23 @@
{
"images" : [
{
"filename" : "back.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "back@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "back@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 B

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 888 B

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 772 B

View file

@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "Close.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Close@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Close@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View file

@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "Up.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Up@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Up@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "original"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,015 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 776 B

After

Width:  |  Height:  |  Size: 958 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -20,8 +20,6 @@
<false/>
<key>syncLocalContacts</key>
<false/>
<key>createConferenceCallsWithJitsi</key>
<true/>
<key>enableRageShake</key>
<true/>
<key>maxAllowedMediaCacheSize</key>
@ -34,5 +32,7 @@
<integer>15020851</integer>
<key>enableBotCreation</key>
<false/>
<key>enableRingingForGroupCalls</key>
<false/>
</dict>
</plist>

View file

@ -1,23 +1,26 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "cancel.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "cancel@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cancel@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
}

View file

@ -129,3 +129,11 @@
"callbar_only_multiple_paused" = "عَدَد %@ مُكالَمَة مُعلَّقة";
"callbar_active_and_single_paused" = "مُكالَمَةٌ واحدةٌ نَشِطة (%@) · مُكالَمَةٌ واحدةٌ مُعلَّقة";
"callbar_only_single_paused" = "مُكالَمَة مُعلَّقة";
"auth_softlogout_reason" = "لَقَد سَجَّلَ مُدير الخادِم الرَّئيس الخَّاصِّ بِك (%1$@) الخُرُوج مِن الحِساب الخَّاصِّ بِك %2$@ (%3$@).";
"auth_softlogout_signed_out" = "لَقَد سَجَّلتَ الخُرُوج";
"auth_autodiscover_invalid_response" = "اِستِجابَة اِكتِشاف الخادِم الرَّئيس غَير صَالِحة";
"auth_accept_policies" = "يُرجَى مُراجَعَة سِياسَات هَذَا الخادِم الرَّئيس وقُبُولِهَا:";
"auth_add_email_and_phone_warning" = "التَّسجِيل بِواسِطَة البَريد الإلِكتُرونيّ وَرَقم الهَاتِف معًا غَير مَدعوم حَتَّى تَتَوَفَر بيئة بَرمجة التَّطبيقات (API). سَوفَ يَتِّم أخذ رَقم الهَاتِف فَقَط فِي الاِعتِبار. يُمكِنُكَ إضافَة البَريد الإلِكتُرونيّ الخَّاصّ بِك إلَى مَلَفّ التَّعريف الخَّاصِّ بِك فِي الإعدَادَات.";
"auth_reset_password_success_message" = "لَقَد تمَّ إعادَةُ ضَبطِ كَلِمَةِ المُرُور الخَّاصَّةِ بِك.\n\nلَقَد تمَّ تَسجِيلُ خُرُوجِك مِن جَميعِ الجَلَسات وَلَن تَستَلِمَ بَعد الآن دَفعَ الإِشعَارات. لِإعادَةِ تَفعِيل الإِشعَارات، أعِد تَسجِيلَ الدُّخول عَلَى كُلِّ جِهاز.";
"auth_reset_password_error_not_found" = "لَا يَبدو أنَّ عُنوان البَريد الإلِكتُرونيّ الخَّاصِّ بِك مُقتَرِنٌ بِمُعَرِّف Matrix عَلَى الخادِمِ الرَّئيس هَذَا.";
"auth_reset_password_error_unauthorized" = "فَشَلَ التَّحَقُق مِن عُنوان البَريد الإلِكتُرونيّ: تَأكَّد مِن نَقرِكَ عَلَى الرَّابِط الَّذي فِي البَريد الإلِكتُرونيّ";

View file

@ -1327,8 +1327,8 @@
"callbar_only_single_active" = "Laufender Anruf (%@)";
"room_event_action_delete_confirmation_message" = "Möchtest Du die nicht gesendete Nachricht wirklich löschen?";
"room_event_action_delete_confirmation_title" = "Nicht gesendete Nachricht löschen";
"room_details_integrations" = "Einbindungen";
"room_details_search" = "Raum suchen";
"room_details_integrations" = "Integrationen";
"room_details_search" = "Raum durchsuchen";
"room_multiple_typing_notification" = "%@ und andere";
"room_accessibility_video_call" = "Videoanruf";
"room_message_replying_to" = "%@ anworten";

View file

@ -123,6 +123,12 @@
/* Incoming named video conference invite from a specific person */
"VIDEO_CONF_NAMED_FROM_USER" = "Video group call from %@: '%@'";
/* A user added a Jitsi call to a room */
"GROUP_CALL_STARTED" = "Group call started";
/* Group call from user, CallKit caller name */
"GROUP_CALL_FROM_USER" = "%@ (Group call)";
/** Key verification **/
"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ wants to verify";

View file

@ -65,12 +65,13 @@
"less" = "Less";
// Call Bar
"callbar_only_single_active" = "Active call (%@)";
"callbar_only_single_active" = "Tap to return to the call (%@)";
"callbar_active_and_single_paused" = "1 active call (%@) · 1 paused call";
"callbar_active_and_multiple_paused" = "1 active call (%@) · %@ paused calls";
"callbar_only_single_paused" = "Paused call";
"callbar_only_multiple_paused" = "%@ paused calls";
"callbar_return" = "Return";
"callbar_only_single_active_group" = "Tap to Join the group call (%@)";
// Accessibility
"accessibility_checkbox_label" = "checkbox";
@ -312,7 +313,8 @@ Tap the + to start adding people.";
"room_member_power_level_short_custom" = "Custom";
// Chat
"room_jump_to_first_unread" = "Jump to first unread message";
"room_slide_to_end_group_call" = "Slide to end the call for everyone";
"room_jump_to_first_unread" = "Jump to unread";
"room_accessiblity_scroll_to_bottom" = "Scroll to bottom";
"room_new_message_notification" = "%d new message";
"room_new_messages_notification" = "%d new messages";
@ -395,6 +397,8 @@ Tap the + to start adding people.";
"room_accessibility_hangup" = "Hang up";
"room_place_voice_call" = "Voice call";
"room_open_dialpad" = "Dial pad";
"room_join_group_call" = "Join";
"room_no_privileges_to_create_group_call" = "You need to be an admin or a moderator to start a call.";
"media_type_accessibility_image" = "Image";
"media_type_accessibility_audio" = "Audio";
@ -526,6 +530,7 @@ Tap the + to start adding people.";
"settings_labs_e2e_encryption_prompt_message" = "To finish setting up encryption you must log in again.";
"settings_labs_create_conference_with_jitsi" = "Create conference calls with jitsi";
"settings_labs_message_reaction" = "React to messages with emoji";
"settings_labs_enable_ringing_for_group_calls" = "Ring for group calls";
"settings_version" = "Version %@";
"settings_olm_version" = "Olm Version %@";
@ -823,10 +828,22 @@ Tap the + to start adding people.";
"event_formatter_message_edited_mention" = "(edited)";
"event_formatter_call_voice" = "Voice call";
"event_formatter_call_video" = "Video call";
"event_formatter_call_has_ended" = "This call has ended";
"event_formatter_call_you_currently_in" = "You're currently in this call";
"event_formatter_call_connecting" = "Connecting…";
"event_formatter_call_ringing" = "Ringing…";
"event_formatter_call_has_ended" = "Ended %@";
"event_formatter_call_you_currently_in" = "Active call";
"event_formatter_call_you_declined" = "You declined this call";
"event_formatter_call_you_missed" = "You missed this call";
"event_formatter_call_connection_failed" = "Connection failed";
"event_formatter_call_back" = "Call back";
"event_formatter_call_decline" = "Decline";
"event_formatter_call_answer" = "Answer";
"event_formatter_call_retry" = "Retry";
"event_formatter_call_end_call" = "End call";
"event_formatter_group_call" = "Group call";
"event_formatter_group_call_join" = "Join";
"event_formatter_group_call_leave" = "Leave";
"event_formatter_group_call_incoming" = "%@ in %@";
// Events formatter with you
"event_formatter_widget_added_by_you" = "You added the widget: %@";

View file

@ -1,7 +1,7 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "La cámara se utiliza para tomar fotos y vídeos, realizar llamadas de vídeo.";
"NSPhotoLibraryUsageDescription" = "La biblioteca de fotos se utiliza para enviar fotos y vídeos.";
"NSMicrophoneUsageDescription" = "El micrófono se utiliza para tomar vídeos, realizar llamadas.";
"NSCameraUsageDescription" = "La cámara se usa para sacar fotos, vídeos y hacer videollamadas.";
"NSPhotoLibraryUsageDescription" = "La biblioteca de fotos se usa para enviar fotos y vídeos.";
"NSMicrophoneUsageDescription" = "El micrófono se usa para grabar vídeos y realizar llamadas.";
"NSContactsUsageDescription" = "Para mostrarte cuáles de tus contactos ya utilizan Matrix, Element puede enviar las direcciones de correo electrónico y números telefónicos de tu agenda de contactos a tu Servidor de Identidad de Matrix. En los casos que se puede, tu información personal se cifra antes de ser enviada - por favor consulta la política de privacidad de tu Servidor de Identidad.";
"NSFaceIDUsageDescription" = "Face ID se usa para acceder a tu aplicación.";
"NSCalendarsUsageDescription" = "Mostrar tus reuniones en la aplicación.";

View file

@ -54,3 +54,19 @@
"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ en %@";
/* Sticker from a specific person, not referencing a room. */
"STICKER_FROM_USER" = "%@ envió una pegatina";
/** Key verification **/
"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ quiere verificar";
/** Notification messages **/
/* New message indicator on unknown room */
"MESSAGE" = "Mensaje";
/* New message indicator from a DM */
"MESSAGE_FROM_X" = "Mensaje de %@";
/* New message indicator on a room */
"MESSAGE_IN_X" = "Mensaje en %@";
"MESSAGE_PROTECTED" = "Nuevo mensaje";

View file

@ -51,7 +51,7 @@
"auth_repeat_password_placeholder" = "Repite la contraseña";
"auth_repeat_new_password_placeholder" = "Confirma tu contraseña nueva";
"auth_home_server_placeholder" = "URL (ej. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (ej. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (ej.: https://matrix.org)";
"auth_invalid_login_param" = "Nombre de usuario y/o contraseña incorrectos";
"auth_invalid_user_name" = "Los nombres de usuario solo pueden contener letras, números, puntos, guiones y guiones bajos";
"auth_invalid_password" = "Contraseña demasiado corta (mínimo 6)";
@ -121,12 +121,12 @@
"auth_msisdn_validation_title" = "Verificación Pendiente";
"auth_msisdn_validation_message" = "Hemos enviado un SMS con un código de activación. Por favor, ingresa este código a continuación.";
"auth_msisdn_validation_error" = "No se pudo verificar el número telefónico.";
"auth_recaptcha_message" = "Este Servidor Doméstico quiere asegurarse de que no eres un robot";
"auth_recaptcha_message" = "Este servidor base quiere asegurarse de que no eres un robot";
"auth_reset_password_message" = "Para restablecer tu contraseña, ingresa la dirección de correo electrónico vinculada a tu cuenta:";
"auth_reset_password_missing_email" = "Debes ingresar la dirección de correo electrónico vinculada a tu cuenta.";
"auth_reset_password_email_validation_message" = "Se envió un correo electrónico a %@. Una vez que hayas seguido el enlace que contiene, haz clic a continuación.";
"auth_reset_password_error_unauthorized" = "No se pudo verificar la dirección de correo electrónico: asegúrate de hacer clic en el enlace del correo electrónico";
"auth_reset_password_error_not_found" = "Tu dirección de correo electrónico no parece estar asociada a una ID de Matrix en este Servidor Doméstico.";
"auth_reset_password_error_not_found" = "Tu dirección de correo electrónico no parece estar asociada a una ID de Matrix en este servidor base.";
"auth_reset_password_success_message" = "Tu contraseña fue restablecida.\n\nSe ha cerrado sesión en todos tus dispositivos y ya no recibirás notificaciones push. Para volver a habilitar las notificaciones, vuelve a iniciar sesión en cada dispositivo.";
"auth_add_email_and_phone_warning" = "Todavía no es posible registrarse con correo electrónico y número telefónico a la vez, hasta que exista la API. Solo se tendrá en cuenta el número telefónico. Puedes añadir tu correo electrónico a tu perfil en ajustes.";
// Chat creation
@ -550,3 +550,43 @@
"deactivate_account_forget_messages_information_part3" = ": esto provocará que los usuarios futuros vean conversaciones incompletas)";
// String for App Store
"store_short_description" = "Chat/VoIP descentralizado y seguro";
"room_participants_leave_prompt_title_for_dm" = "Salir";
"people_empty_view_title" = "Personas";
"social_login_button_title_sign_up" = "Registrarse con %@";
"social_login_button_title_sign_in" = "Iniciar sesión con %@";
"social_login_button_title_continue" = "Seguir con %@";
"social_login_list_title_sign_up" = "O";
"social_login_list_title_sign_in" = "O";
// Social login
"social_login_list_title_continue" = "Seguir con";
"auth_softlogout_clear_data_sign_out" = "Cerrar sesión";
"auth_softlogout_clear_data_sign_out_title" = "¿Seguro?";
"auth_softlogout_clear_data_button" = "Borrar todos los datos";
"auth_softlogout_clear_data_message_2" = "Bórralos si vas a dejar de usar este dispositivo, o quieres iniciar sesión con otra cuenta.";
"auth_softlogout_clear_data_message_1" = "Cuidado: tus datos personales (incluyendo claves de cifrado) todavía están alojados en este dispositivo.";
"auth_softlogout_clear_data" = "Borrar datos personales";
"auth_softlogout_reason" = "La administración de tu servidor base (%1$@) ha cerrado tu sesión %2$@ (%3$@).";
"auth_softlogout_sign_in" = "Iniciar sesión";
"auth_softlogout_signed_out" = "No tienes una sesión iniciada";
"auth_accept_policies" = "Por favor, lee y acepta los términos de este servidor base:";
"auth_add_phone_message_2" = "Pon un número de teléfono para que te descubran personas que te conozcan.";
"auth_add_email_message_2" = "Pon un correo para poder recuperar tu cuenta en el futuro, y, opcionalmente que las personas que te conozcan descubran tu cuenta.";
"auth_login_single_sign_on" = "Iniciar sesión";
// Accessibility
"accessibility_checkbox_label" = "casilla de selección";
"callbar_return" = "Volver";
"callbar_only_multiple_paused" = "%@ llamadas en espera";
"callbar_only_single_paused" = "Llamada en espera";
"callbar_active_and_multiple_paused" = "1 llamada en curso (%@) · %@ llamadas en espera";
"callbar_active_and_single_paused" = "1 llamada en curso (%@) · 1 llamada en espera";
// Call Bar
"callbar_only_single_active" = "Llamada en curso (%@)";
"less" = "Menos";
"more" = "Más";
"switch" = "Cambiar";
"skip" = "Saltar";
"close" = "Cerrar";

View file

@ -0,0 +1 @@

View file

@ -0,0 +1 @@

View file

@ -1 +1,17 @@
// Room Details
"room_details_title" = "جزئیات اتاق";
"invite" = "دعوت";
// Actions
"view" = "مشاهده";
"leave" = "ترک";
"save" = "ذخیره";
"cancel" = "لغو";
"retry" = "تلاش مجدد";
"continue" = "ادامه";
"close" = "بستن";
"back" = "بازگشت";

View file

@ -1350,3 +1350,9 @@
// Call Bar
"callbar_only_single_active" = "Aktív hívás (%@)";
"room_details_integrations" = "Integrációk";
"room_details_search" = "Szoba keresése";
"room_multiple_typing_notification" = "%@ és mások";
"room_accessibility_video_call" = "Videohívás";
"room_message_replying_to" = "Válasz erre: %@";
"room_message_editing" = "Szerkesztés";

View file

@ -0,0 +1,9 @@
"NSContactsUsageDescription" = "For å finne kontakter som allerede bruker Matrix, kan Element sende e-postadresser og telefonnummer i din adressebok til utvalgte Matrix identitetstjenere. Der det støttes blir data anonymisert (hashet) - vennligst sjekk din identitetstjener for detaljer.";
"NSFaceIDUsageDescription" = "Face ID brukes for å få tilgang til appen.";
"NSCalendarsUsageDescription" = "Se dine avtalte møter i appen.";
"NSMicrophoneUsageDescription" = "Mikrofonen brukes til videoopptak, og i samtaler.";
"NSPhotoLibraryUsageDescription" = "Bildebiblioteket brukes for å sende bilder og videoer.";
// Permissions usage explanations
"NSCameraUsageDescription" = "Kameraet brukes til å ta bilder, spille inn video, og i videosamtaler.";

View file

@ -0,0 +1,113 @@
/** Key verification **/
"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ ønsker å bekrefte";
/* Incoming named video conference invite from a specific person */
"VIDEO_CONF_NAMED_FROM_USER" = "Video-konferansesamtale fra %@: '%@'";
/* Incoming named voice conference invite from a specific person */
"VOICE_CONF_NAMED_FROM_USER" = "Konferansesamtale fra %@: '%@'";
/* Incoming unnamed voice conference invite from a specific person */
"VOICE_CONF_FROM_USER" = "Konferansesamtale fra %@";
/* Incoming unnamed video conference invite from a specific person */
"VIDEO_CONF_FROM_USER" = "Video-konferansesamtale fra %@";
/* Incoming one-to-one video call */
"VIDEO_CALL_FROM_USER" = "Videosamtale fra %@";
/** Calls **/
/* Incoming one-to-one voice call */
"VOICE_CALL_FROM_USER" = "%@ ringer";
/* A user has invited you to a named room */
"USER_INVITE_TO_NAMED_ROOM" = "%@ har invitert deg til %@";
/* A user has invited you to an (unamed) group chat */
"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ har invitert deg til en gruppesamtale";
/** Invites **/
/* A user has invited you to a chat */
"USER_INVITE_TO_CHAT" = "%@ har invitert deg til en samtale";
/* Look, stuff's happened, alright? Just open the app. */
"MSGS_IN_TWO_PLUS_ROOMS" = "%@ nye meldinger i %@, %@ og andre";
/* Multiple messages in two rooms */
"MSGS_IN_TWO_ROOMS" = "%@ nye meldinger i %@ og %@";
/* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */
"MSGS_FROM_TWO_PLUS_USERS" = "%@ nye meldinger fra %@, %@ og andre";
/* Multiple unread messages from three people */
"MSGS_FROM_THREE_USERS" = "%@ nye meldinger fra %@, %@ og %@";
/* Multiple unread messages from two people */
"MSGS_FROM_TWO_USERS" = "%@ nye meldinger fra %@ og %@";
/* Multiple unread messages from a specific person, not referencing a room */
"MSGS_FROM_USER" = "%@ nye meldinger i %@";
/** Coalesced messages **/
/* Multiple unread messages in a room */
"UNREAD_IN_ROOM" = "%@ meldinger i %@";
"MESSAGE_PROTECTED" = "Ny melding";
/* New message indicator on a room */
"MESSAGE_IN_X" = "Melding i %@";
/* New message indicator from a DM */
"MESSAGE_FROM_X" = "Melding fra %@";
/** Notification messages **/
/* New message indicator on unknown room */
"MESSAGE" = "Melding";
/* Sticker from a specific person, not referencing a room. */
"STICKER_FROM_USER" = "%@ sendte et klistremerke";
/* A single unread message */
"SINGLE_UNREAD" = "Du mottok en melding";
/* A single unread message in a room */
"SINGLE_UNREAD_IN_ROOM" = "Du mottok en melding i %@";
/* New action message from a specific person in a named room. */
"IMAGE_FROM_USER_IN_ROOM" = "%@ lastet opp et bilde %@ i %@";
/** Image Messages **/
/* New action message from a specific person, not referencing a room. */
"IMAGE_FROM_USER" = "%@ sendte et bilde %@";
/* New action message from a specific person in a named room. */
"ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@";
/* New action message from a specific person, not referencing a room. */
"ACTION_FROM_USER" = "* %@ %@";
/* New message from a specific person in a named room. Content included. */
"MSG_FROM_USER_IN_ROOM_WITH_CONTENT" = "%@ i %@: %@";
/** Single, unencrypted messages (where we can include the content */
/* New message from a specific person, not referencing a room. Content included. */
"MSG_FROM_USER_WITH_CONTENT" = "%@: %@";
/* New message from a specific person in a named room */
"MSG_FROM_USER_IN_ROOM" = "%@ skrevet i %@";
/** Single, end-to-end encrypted messages (ie. we don't know what they say) */
/* New message from a specific person, not referencing a room */
"MSG_FROM_USER" = "%@ sendte en melding";
/* Message title for a specific person in a named room */
"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ i %@";

View file

@ -557,7 +557,7 @@
"secrets_setup_recovery_key_loading" = "Laster…";
"secrets_setup_recovery_key_export_action" = "Lagre";
"secrets_setup_recovery_key_done_action" = "Fullført";
"secrets_setup_recovery_key_storage_alert_title" = "Hold det trygt";
"secrets_setup_recovery_key_storage_alert_title" = "Oppbevar trygt";
"secrets_setup_recovery_passphrase_validate_action" = "Fullført";
"secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Bekreft";
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Bekreft passordfrasen";
@ -630,7 +630,7 @@
"room_resource_usage_limit_reached_message_1_default" = "Denne hjemmeserveren har overskredet en av sine ressursgrenser så ";
"room_resource_limit_exceeded_message_contact_3" = " for å fortsette å bruke denne tjenesten.";
"room_resource_limit_exceeded_message_contact_2_link" = "kontakt tjenesteadministratoren";
"room_resource_limit_exceeded_message_contact_1" = " Vær så snill ";
"room_resource_limit_exceeded_message_contact_1" = " Vennligst ";
"room_predecessor_link" = "Trykk her for å se eldre meldinger.";
"room_predecessor_information" = "Dette rommet er en fortsettelse av en annen samtale.";
"room_replacement_information" = "Dette rommet er erstattet og er ikke lenger aktivt.";
@ -785,3 +785,78 @@
// Call Bar
"callbar_only_single_active" = "Aktiv samtale (%@)";
"secrets_setup_recovery_key_information" = "Oppbevar din sikkerhetsnøkkel trygt. Den kan brukes til å å tilgang til dine sikrede meldinger og data.";
// MARK: - Secrets set up
// Recovery Key
"secrets_setup_recovery_key_title" = "Lagre din sikkerhetsnøkkel";
"secrets_recovery_with_key_invalid_recovery_key_message" = "Vennligst sjekk at du har tastet riktig sikkerhetsnøkkel.";
"secrets_recovery_with_key_invalid_recovery_key_title" = "Ugyldig sikkerhetsnøkkel, ingen tilgang til sikret lagringsområde";
"secrets_recovery_with_key_recover_action" = "Bruk sikkerhetsnøkkel";
"secrets_setup_recovery_key_storage_alert_message" = "✓ Skriv ut og oppbevar på et trygt sted\n✓ Lagre på en USB minnepenn, eller ekstern sikkerhetskopi\n✓ Kopièr til din personlige skylagring";
"secrets_recovery_with_passphrase_invalid_passphrase_title" = "Feil sikkerhetsfrase, ingen tilgang til sikret lagringsområde";
"secrets_recovery_with_key_information_verify_device" = "Bruk din sikkerhetsnøkkel for å bekrefte denne enheten.";
"secrets_recovery_with_key_information_default" = "Få tilgang til dine sikrede meldinger og identitetsnøkkelen som brukes for å bekrefte andre sesjoner, ved å taste inn din sikkerhetsnøkkel.";
"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Vennligst sjekk at du har tastet riktig sikkerhetsfrase.";
"room_open_dialpad" = "Nummertastatur";
"settings_labs" = "LABS";
"room_place_voice_call" = "Lydsamtale";
"settings_pin_rooms_with_unread" = "Feste rom med uleste meldinger";
"settings_global_settings_info" = "Globale varslingsinnstillinger er tilgjengelige på din %@ nettleserklient";
"room_multiple_typing_notification" = "%@ og andre";
"room_event_action_delete_confirmation_message" = "Er du sikker på at du vil slette denne usendte meldingen?";
"external_link_confirmation_message" = "Lenken %@ bringer deg til et annet nettsted: %@\n\nEr du sikker på at du vil fortsette?";
"room_accessibility_video_call" = "Videosamtale";
"room_event_action_delete_confirmation_title" = "Slett usendt melding";
"room_unsent_messages_cancel_message" = "Er du sikker på at du vil slette alle usendte meldinger i dette rommet?";
"room_unsent_messages_cancel_title" = "Slett usendte meldinger";
"room_message_replying_to" = "Svarer til %@";
"room_message_editing" = "Redigerer";
"room_member_power_level_custom_in" = "Tilpasset (%@) av %@";
"pin_protection_confirm_pin_to_disable" = "Bekreft PIN-kode for å deaktivere PIN-kode";
"pin_protection_choose_pin" = "Opprett en PIN-kode for sikkerhet";
"device_verification_self_verify_alert_message" = "Verifiser den nye påloggingen som vil ha tilgang til kontoen din:% @";
// Service terms - Variant for identity server when displayed out of a context
"service_terms_modal_title_identity_server" = "Finne kontakter";
"widget_integration_missing_room_id" = "Manglende rom_id i forespørsel.";
"rage_shake_prompt" = "Det ser ut til at du rister telefonen i frustrasjon. Ønsker du å sende inn rapport om feil?";
"room_details_addresses_invalid_address_prompt_title" = "Ugyldig alias format";
"security_settings_crypto_sessions_description_2" = "Om du ikke gjenkjenner en pålogging, endre ditt passord og tilbakestill sikkerhetskopi.";
"settings_key_backup_info_trust_signature_invalid_device_verified" = "Sikkerhetskopien har en ugyldig signatur fra %@";
"settings_key_backup_info_trust_signature_valid_device_unverified" = "Sikkerhetskopi har signatur fra %@";
"settings_key_backup_info_trust_signature_valid" = "Sikkerhetskopien har en gyldig signatur fra denne økten";
"settings_key_backup_info_trust_signature_unknown" = "Sikkerhetskopi av økten har signatur med ID: %@";
"settings_key_backup_info_progress_done" = "Alle nøkler sikkerhetskopiert";
"settings_key_backup_info_progress" = "Sikkerhetskopierer %@ nøkler…";
"settings_key_backup_info_not_valid" = "Denne økten sikkerhetskopierer ikke dine nøkler, men du har en eksisterende sikkerhetskopi du kan gjenopprette fra og legge til, for å gå videre.";
"settings_key_backup_info_valid" = "Denne økten sikkerhetskopierer dine nøkler.";
"settings_key_backup_info_version" = "Sikkerhetskopi av nøkler versjon : %@";
"settings_key_backup_info_signout_warning" = "Før du logger ut, koble denne sesjonen til sikkerhetskopi av nøkler for å unngå tap av nøkler som kanskje bare er lagret på denne enheten.";
"settings_key_backup_info_none" = "Nøklene dine for denne sesjonen blir ikke sikkerhetskopiert";
"settings_third_party_notices" = "Tredjepartsmerknader";
"settings_labs_e2e_encryption_prompt_message" = "Vennligst logg inn igjen for å ferdigstille oppsett av kryptering.";
"settings_key_backup" = "Sikkerhetskopi av meldingsnøkler";
"settings_ui_theme_picker_message" = "\"Auto\" bruker din enhets innstillinger for å bytte bakgrunnsfarge";
// MARK: - Favourites
"favourites_empty_view_title" = "Favorittrom og personer";
"create_room_placeholder_address" = "#testrom: matrix.org";
"device_verification_emoji_santa" = "Julenisse";
"device_verification_security_advice_emoji" = "Sammenlign de unike emojiene, og sjekk at de vises i samme rekkefølge.";
// MARK: Sign out warning
"sign_out_existing_key_backup_alert_title" = "Er du sikker på at du vil logge av?";
"service_terms_modal_message_identity_server" = "Godta brukervilkårene til identitetsserveren (%@) for å finne kontakter.";
"event_formatter_call_back" = "Ring tilbake";
"room_details_advanced_e2e_encryption_disabled" = "Kryptering er ikke aktivert i dette rommet.";
"room_details_flair_section" = "Vi brukervalg for samfunn";
"room_details_photo_for_dm" = "Bilde";
"room_details_photo" = "Rombilde";
"settings_flair" = "Vis brukervalg hvor tillat";
"settings_on_denied_notification" = "Varsler er ikke tillat for %@, vennligst tillat dem i enhetens innstillinger";
"settings_pin_rooms_with_missed_notif" = "Fest rom med tapte varsler";

View file

@ -1023,7 +1023,7 @@
// MARK: - Secrets reset
"secrets_reset_title" = "Alles terugzetten";
"secrets_reset_title" = "Alles opnieuw instellen";
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Wachtwoord bevestigen";
"secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Bevestigen";
"secrets_setup_recovery_passphrase_validate_action" = "Klaar";
@ -1058,7 +1058,7 @@
// Recover with passphrase
"secrets_recovery_with_passphrase_title" = "Herstelwachtwoord";
"secrets_recovery_reset_action_part_2" = "Alles terugzetten";
"secrets_recovery_reset_action_part_2" = "Alles opnieuw instellen";
// MARK: - Secrets recovery

View file

@ -1,6 +1,7 @@
// Permissions usage explanations
"NSCameraUsageDescription" = "Aparat służy do robienia zdjęć i nagrywania filmów, prowadzenia rozmów wideo.";
"NSPhotoLibraryUsageDescription" = "Biblioteka zdjęć służy do wysyłania zdjęć i filmów.";
"NSMicrophoneUsageDescription" = "Mikrofon służy do robienia filmów, wykonywania połączeń.";
"NSContactsUsageDescription" = "Możemy pokazać Ci, które z Twoich kontaktów korzystają aktualnie z Element, bądź Matrix. Możemy wysyłać adresy e-mail i numery telefonów z Twojej książki adresowej na Twój serwer Matrix. New Vector nie przechowuje Twoich danych, ani nie wykorzystuje ich w żadnym celu. Aby uzyskać dodatkowe informacje zajrzyj do zakładki \"polityka prywatności\" w ustawieniach aplikacji.";
"NSCameraUsageDescription" = "Kamera wykorzystywana jest do robienia zdjęć, nagrywania filmów i prowadzenia rozmów wideo.";
"NSPhotoLibraryUsageDescription" = "Biblioteka zdjęć wykorzystywana jest do wysyłania zdjęć i filmów.";
"NSMicrophoneUsageDescription" = "Mikrofon wykorzystywany jest podczas nagrywania filmów i wykonywania połączeń.";
"NSContactsUsageDescription" = "Aby móc znaleźć osoby z Twoich kontaktów, które korzystają już z sieci Matrix, Element może wysłać adresy e-mail i numery telefonów z Twojej książki adresowej do wybranego serwera tożsamości Matrix. Tam, gdzie jest to obsługiwane, dane osobowe są szyfrowane przed wysłaniem - zapoznaj się z polityką prywatności Twojego serwera tożsamości, aby uzyskać więcej informacji.";
"NSCalendarsUsageDescription" = "Zobacz swoje zaplanowane spotkania w aplikacji.";
"NSFaceIDUsageDescription" = "Face ID wykorzystywane jest do odblokowywania aplikacji.";

View file

@ -1,5 +1,5 @@
/* New message from a specific person, not referencing a room */
"MSG_FROM_USER" = "%@ wysłał(a) wiadomość";
"MSG_FROM_USER" = "%@ wysłał(-a) wiadomość";
/* New message from a specific person, not referencing a room. Content included. */
"MSG_FROM_USER_WITH_CONTENT" = "%@: %@";
/* New message from a specific person in a named room. Content included. */
@ -23,27 +23,27 @@
/* Look, stuff's happened, alright? Just open the app. */
"MSGS_IN_TWO_PLUS_ROOMS" = "%@ nowych wiadomości w %@, %@ i innych";
/* New action message from a specific person, not referencing a room. */
"IMAGE_FROM_USER" = "%@ wysłał(a) zdjęcie %@";
"IMAGE_FROM_USER" = "%@ wysłał(-a) zdjęcie %@";
/* New action message from a specific person in a named room. */
"IMAGE_FROM_USER_IN_ROOM" = "%@ wysłał(a) zdjęcie %@ w %@";
"IMAGE_FROM_USER_IN_ROOM" = "%@ wysłał(-a) zdjęcie %@ w %@";
/* Multiple unread messages from a specific person, not referencing a room */
"MSGS_FROM_USER" = "%@ nowych wiadomości w %@";
/* Multiple messages in two rooms */
"MSGS_IN_TWO_ROOMS" = "%@ nowych wiadomości w %@ i %@";
/* A user has invited you to a named room */
"USER_INVITE_TO_NAMED_ROOM" = "%@ zaprosił(a) Ciebie do %@";
"USER_INVITE_TO_NAMED_ROOM" = "%@ zaprosił(-a) Ciebie do %@";
/* A user has invited you to an (unamed) group chat */
"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ zaprosił(a) Ciebie do rozmowy grupowej";
"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ zaprosił(-a) Ciebie do rozmowy grupowej";
/* A user has invited you to a chat */
"USER_INVITE_TO_CHAT" = "%@ zaprosił(a) Ciebie do rozmowy";
"USER_INVITE_TO_CHAT" = "%@ zaprosił(-a) Ciebie do rozmowy";
/* Message title for a specific person in a named room */
"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ w %@";
/* New message from a specific person in a named room */
"MSG_FROM_USER_IN_ROOM" = "%@ opublikowany w %@";
/* Sticker from a specific person, not referencing a room. */
"STICKER_FROM_USER" = "%@ wysłał(a) naklejkę";
"STICKER_FROM_USER" = "%@ wysłał(-a) naklejkę";
/* Incoming one-to-one voice call */
"VOICE_CALL_FROM_USER" = "Zadzwonił do Ciebie z %@";
"VOICE_CALL_FROM_USER" = "Połączenie głosowe z %@";
/* Incoming one-to-one video call */
"VIDEO_CALL_FROM_USER" = "Połączenie wideo z %@";
/* Incoming unnamed voice conference invite from a specific person */
@ -54,10 +54,11 @@
"VOICE_CONF_NAMED_FROM_USER" = "Połączenie grupowe z %@: '%@'";
/* Incoming named video conference invite from a specific person */
"VIDEO_CONF_NAMED_FROM_USER" = "Grupowe połączenie wideo z %@: '%@'";
"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ wymaga weryfikacji";
"KEY_VERIFICATION_REQUEST_FROM_USER" = "%@ chce się z Tobą zweryfikować";
/* New message indicator on unknown room */
"MESSAGE" = "Wiadomość";
/* New message indicator from a DM */
"MESSAGE_FROM_X" = "Wiadomość od %@";
/* New message indicator on a room */
"MESSAGE_IN_X" = "Wiadomość w %@";
"MESSAGE_PROTECTED" = "Nowa Wiadomość";

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,7 @@
"title_groups" = "Bashkësi";
"warning" = "Sinjalizim";
// Actions
"view" = "Parje";
"view" = "Shiheni";
"next" = "Pasuesja";
"back" = "Mbrapsht";
"continue" = "Vazhdo";
@ -1344,3 +1344,4 @@
"room_accessibility_video_call" = "Thirrje Video";
"room_message_replying_to" = "Në përgjigje të %@";
"room_message_editing" = "Përpunim";
"room_details_search" = "Kërkoni për dhomë";

View file

@ -1 +1,4 @@
"NSFaceIDUsageDescription" = "ittusmres Face ID i wekcam ɣer tsensi nnek.";
"NSPhotoLibraryUsageDescription" = "Tettusmres tasedlist n twelafin i wazan n twelafin d ibidyuten.";

View file

@ -58,13 +58,6 @@ extern NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestAcceptPre
*/
extern NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed;
/**
Action identifier used when the user pressed "Call back" button for a declined call.
The `userInfo` dictionary contains an `MXEvent` object under the `kMXKRoomBubbleCellEventKey` key, representing the invite event of the declined call.
*/
extern NSString *const kMXKRoomBubbleCellCallBackButtonPressed;
/**
Define a `MXKRoomBubbleTableViewCell` category at Riot level to handle bubble customisation.
*/

View file

@ -33,7 +33,6 @@ NSString *const kMXKRoomBubbleCellLongPressOnReactionView = @"kMXKRoomBubbleCell
NSString *const kMXKRoomBubbleCellEventIdKey = @"kMXKRoomBubbleCellEventIdKey";
NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestAcceptPressed = @"kMXKRoomBubbleCellKeyVerificationAcceptPressed";
NSString *const kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed = @"kMXKRoomBubbleCellKeyVerificationDeclinePressed";
NSString *const kMXKRoomBubbleCellCallBackButtonPressed = @"kMXKRoomBubbleCellCallBackButtonPressed";
@implementation MXKRoomBubbleTableViewCell (Riot)

View file

@ -18,6 +18,8 @@
#import <MatrixSDK/MXSession.h>
@class HomeserverConfiguration;
@interface MXSession (Riot)
/**
@ -26,15 +28,9 @@
- (NSUInteger)vc_missedDiscussionsCount;
/**
Check if E2E by default is welcomed on the user's HS.
The default value is YES.
HS admins can disable it in /.well-known/matrix/client by returning:
"im.vector.riot.e2ee": {
"default": false
}
*/
- (BOOL)vc_isE2EByDefaultEnabledByHSAdmin;
Return the homeserver configuration based on HS Well-Known or BuildSettings properties according to existing values.
*/
- (HomeserverConfiguration*)vc_homeserverConfiguration;
/**
Riot version of [MXSession canEnableE2EByDefaultInNewRoomWithUsers:]

View file

@ -49,25 +49,17 @@
return missedDiscussionsCount;
}
- (BOOL)vc_isE2EByDefaultEnabledByHSAdmin
- (HomeserverConfiguration*)vc_homeserverConfiguration
{
BOOL isE2EByDefaultEnabledByHSAdmin = YES;
MXWellKnown *wellKnown = self.homeserverWellknown;
if (wellKnown.JSONDictionary[@"im.vector.riot.e2ee"][@"default"])
{
MXJSONModelSetBoolean(isE2EByDefaultEnabledByHSAdmin, wellKnown.JSONDictionary[@"im.vector.riot.e2ee"][@"default"]);
}
return isE2EByDefaultEnabledByHSAdmin;
HomeserverConfigurationBuilder *configurationBuilder = [HomeserverConfigurationBuilder new];
return [configurationBuilder buildFrom:self.homeserverWellknown];
}
- (MXHTTPOperation*)vc_canEnableE2EByDefaultInNewRoomWithUsers:(NSArray<NSString*>*)userIds
success:(void (^)(BOOL canEnableE2E))success
failure:(void (^)(NSError *error))failure;
{
if (self.vc_isE2EByDefaultEnabledByHSAdmin)
if ([self vc_homeserverConfiguration].isE2EEByDefaultEnabled)
{
return [self canEnableE2EByDefaultInNewRoomWithUsers:userIds success:success failure:failure];
}

View file

@ -64,4 +64,16 @@ extension UIView {
self.accessibilityTraits.insert(.notEnabled)
}
}
@objc func vc_addShadow(withColor color: UIColor, offset: CGSize, radius: CGFloat, opacity: CGFloat) {
layer.shadowColor = color.cgColor
layer.shadowOpacity = Float(opacity)
layer.shadowRadius = radius
layer.shadowOffset = offset
}
@objc func vc_removeShadow() {
layer.shadowColor = UIColor.clear.cgColor
}
}

View file

@ -31,6 +31,7 @@ internal enum Asset {
internal static let callChatIcon = ImageAsset(name: "call_chat_icon")
internal static let callDialpadBackspaceIcon = ImageAsset(name: "call_dialpad_backspace_icon")
internal static let callDialpadCallIcon = ImageAsset(name: "call_dialpad_call_icon")
internal static let callGoToChatIcon = ImageAsset(name: "call_go_to_chat_icon")
internal static let callHangupLarge = ImageAsset(name: "call_hangup_large")
internal static let callMoreIcon = ImageAsset(name: "call_more_icon")
internal static let callPausedIcon = ImageAsset(name: "call_paused_icon")
@ -104,7 +105,9 @@ internal enum Asset {
internal static let actionSticker = ImageAsset(name: "action_sticker")
internal static let error = ImageAsset(name: "error")
internal static let errorMessageTick = ImageAsset(name: "error_message_tick")
internal static let newClose = ImageAsset(name: "new_close")
internal static let roomActivitiesRetry = ImageAsset(name: "room_activities_retry")
internal static let roomScrollUp = ImageAsset(name: "room_scroll_up")
internal static let scrolldown = ImageAsset(name: "scrolldown")
internal static let scrolldownDark = ImageAsset(name: "scrolldown_dark")
internal static let sendingMessageTick = ImageAsset(name: "sending_message_tick")

View file

@ -502,10 +502,14 @@ internal enum VectorL10n {
internal static func callbarOnlyMultiplePaused(_ p1: String) -> String {
return VectorL10n.tr("Vector", "callbar_only_multiple_paused", p1)
}
/// Active call (%@)
/// Tap to return to the call (%@)
internal static func callbarOnlySingleActive(_ p1: String) -> String {
return VectorL10n.tr("Vector", "callbar_only_single_active", p1)
}
/// Tap to Join the group call (%@)
internal static func callbarOnlySingleActiveGroup(_ p1: String) -> String {
return VectorL10n.tr("Vector", "callbar_only_single_active_group", p1)
}
/// Paused call
internal static var callbarOnlySinglePaused: String {
return VectorL10n.tr("Vector", "callbar_only_single_paused")
@ -1238,13 +1242,41 @@ internal enum VectorL10n {
internal static var errorUserAlreadyLoggedIn: String {
return VectorL10n.tr("Vector", "error_user_already_logged_in")
}
/// Answer
internal static var eventFormatterCallAnswer: String {
return VectorL10n.tr("Vector", "event_formatter_call_answer")
}
/// Call back
internal static var eventFormatterCallBack: String {
return VectorL10n.tr("Vector", "event_formatter_call_back")
}
/// This call has ended
internal static var eventFormatterCallHasEnded: String {
return VectorL10n.tr("Vector", "event_formatter_call_has_ended")
/// Connecting
internal static var eventFormatterCallConnecting: String {
return VectorL10n.tr("Vector", "event_formatter_call_connecting")
}
/// Connection failed
internal static var eventFormatterCallConnectionFailed: String {
return VectorL10n.tr("Vector", "event_formatter_call_connection_failed")
}
/// Decline
internal static var eventFormatterCallDecline: String {
return VectorL10n.tr("Vector", "event_formatter_call_decline")
}
/// End call
internal static var eventFormatterCallEndCall: String {
return VectorL10n.tr("Vector", "event_formatter_call_end_call")
}
/// Ended %@
internal static func eventFormatterCallHasEnded(_ p1: String) -> String {
return VectorL10n.tr("Vector", "event_formatter_call_has_ended", p1)
}
/// Retry
internal static var eventFormatterCallRetry: String {
return VectorL10n.tr("Vector", "event_formatter_call_retry")
}
/// Ringing
internal static var eventFormatterCallRinging: String {
return VectorL10n.tr("Vector", "event_formatter_call_ringing")
}
/// Video call
internal static var eventFormatterCallVideo: String {
@ -1254,7 +1286,7 @@ internal enum VectorL10n {
internal static var eventFormatterCallVoice: String {
return VectorL10n.tr("Vector", "event_formatter_call_voice")
}
/// You're currently in this call
/// Active call
internal static var eventFormatterCallYouCurrentlyIn: String {
return VectorL10n.tr("Vector", "event_formatter_call_you_currently_in")
}
@ -1262,6 +1294,26 @@ internal enum VectorL10n {
internal static var eventFormatterCallYouDeclined: String {
return VectorL10n.tr("Vector", "event_formatter_call_you_declined")
}
/// You missed this call
internal static var eventFormatterCallYouMissed: String {
return VectorL10n.tr("Vector", "event_formatter_call_you_missed")
}
/// Group call
internal static var eventFormatterGroupCall: String {
return VectorL10n.tr("Vector", "event_formatter_group_call")
}
/// %@ in %@
internal static func eventFormatterGroupCallIncoming(_ p1: String, _ p2: String) -> String {
return VectorL10n.tr("Vector", "event_formatter_group_call_incoming", p1, p2)
}
/// Join
internal static var eventFormatterGroupCallJoin: String {
return VectorL10n.tr("Vector", "event_formatter_group_call_join")
}
/// Leave
internal static var eventFormatterGroupCallLeave: String {
return VectorL10n.tr("Vector", "event_formatter_group_call_leave")
}
/// VoIP conference added by %@
internal static func eventFormatterJitsiWidgetAdded(_ p1: String) -> String {
return VectorL10n.tr("Vector", "event_formatter_jitsi_widget_added", p1)
@ -2874,7 +2926,11 @@ internal enum VectorL10n {
internal static var roomIntroCellInformationRoomWithoutTopicSentence2Part2: String {
return VectorL10n.tr("Vector", "room_intro_cell_information_room_without_topic_sentence2_part2")
}
/// Jump to first unread message
/// Join
internal static var roomJoinGroupCall: String {
return VectorL10n.tr("Vector", "room_join_group_call")
}
/// Jump to unread
internal static var roomJumpToFirstUnread: String {
return VectorL10n.tr("Vector", "room_jump_to_first_unread")
}
@ -2950,6 +3006,10 @@ internal enum VectorL10n {
internal static func roomNewMessagesNotification(_ p1: Int) -> String {
return VectorL10n.tr("Vector", "room_new_messages_notification", p1)
}
/// You need to be an admin or a moderator to start a call.
internal static var roomNoPrivilegesToCreateGroupCall: String {
return VectorL10n.tr("Vector", "room_no_privileges_to_create_group_call")
}
/// Connectivity to the server has been lost.
internal static var roomOfflineNotification: String {
return VectorL10n.tr("Vector", "room_offline_notification")
@ -3330,6 +3390,10 @@ internal enum VectorL10n {
internal static var roomResourceUsageLimitReachedMessageContact3: String {
return VectorL10n.tr("Vector", "room_resource_usage_limit_reached_message_contact_3")
}
/// Slide to end the call for everyone
internal static var roomSlideToEndGroupCall: String {
return VectorL10n.tr("Vector", "room_slide_to_end_group_call")
}
/// Invite members
internal static var roomTitleInviteMembers: String {
return VectorL10n.tr("Vector", "room_title_invite_members")
@ -4230,6 +4294,10 @@ internal enum VectorL10n {
internal static var settingsLabsE2eEncryptionPromptMessage: String {
return VectorL10n.tr("Vector", "settings_labs_e2e_encryption_prompt_message")
}
/// Ring for group calls
internal static var settingsLabsEnableRingingForGroupCalls: String {
return VectorL10n.tr("Vector", "settings_labs_enable_ringing_for_group_calls")
}
/// React to messages with emoji
internal static var settingsLabsMessageReaction: String {
return VectorL10n.tr("Vector", "settings_labs_message_reaction")

View file

@ -17,39 +17,67 @@
import Foundation
import MatrixKit
// swiftlint:disable file_length
#if canImport(JitsiMeetSDK)
import JitsiMeetSDK
import CallKit
#endif
/// The number of milliseconds in one second.
private let MSEC_PER_SEC: TimeInterval = 1000
@objcMembers
/// Service to manage call screens and call bar UI management.
class CallPresenter: NSObject {
private enum Constants {
static let pipAnimationDuration: TimeInterval = 0.25
static let groupCallInviteLifetime: TimeInterval = 30
}
/// Utilized sessions
private var sessions: [MXSession] = []
/// Call view controllers map. Keys are callIds.
private var callVCs: [String: CallViewController] = [:]
/// Call background tasks map. Keys are callIds.
private var callBackgroundTasks: [String: MXBackgroundTask] = [:]
private weak var presentedCallVC: CallViewController? {
/// Actively presented direct call view controller.
private weak var presentedCallVC: UIViewController? {
didSet {
updateOnHoldCall()
}
}
private weak var inBarCallVC: CallViewController?
private weak var pipCallVC: CallViewController?
private weak var inBarCallVC: UIViewController?
private weak var pipCallVC: UIViewController?
/// UI operation queue for various UI operations
private var uiOperationQueue: OperationQueue = .main
/// Flag to indicate whether the presenter is active.
private var isStarted: Bool = false
private var callTimer: Timer?
#if canImport(JitsiMeetSDK)
private var widgetEventsListener: Any?
/// Jitsi calls map. Keys are CallKit call UUIDs, values are corresponding widgets.
private var jitsiCalls: [UUID: Widget] = [:]
/// The current Jitsi view controller being displayed or not.
private(set) var jitsiVC: JitsiViewController? {
didSet {
updateOnHoldCall()
}
}
#endif
private var isCallKitEnabled: Bool {
MXCallKitAdapter.callKitAvailable() && MXKAppSettings.standard()?.isCallKitEnabled == true
}
private var activeCallVC: CallViewController? {
private var activeCallVC: UIViewController? {
return callVCs.values.filter { (callVC) -> Bool in
guard let call = callVC.mxCall else {
return false
}
return !call.isOnHold
}.first
}.first ?? jitsiVC
}
private var onHoldCallVCs: [CallViewController] {
@ -101,20 +129,201 @@ class CallPresenter: NSObject {
}
/// Method to be called when the call status bar is tapped.
/// - Returns: If the user interaction handled or not
func callStatusBarButtonTapped() -> Bool {
if let callVC = inBarCallVC ?? activeCallVC {
func callStatusBarTapped() {
if let callVC = (inBarCallVC ?? activeCallVC) as? CallViewController {
dismissCallBar(for: callVC)
presentCallVC(callVC)
return true
return
}
if let jitsiVC = jitsiVC {
dismissCallBar(for: jitsiVC)
presentCallVC(jitsiVC)
}
}
// MARK - Group Calls
/// Open the Jitsi view controller from a widget.
/// - Parameter widget: the jitsi widget
func displayJitsiCall(withWidget widget: Widget) {
#if canImport(JitsiMeetSDK)
let createJitsiBlock = { [weak self] in
guard let self = self else { return }
self.jitsiVC = JitsiViewController()
self.jitsiVC?.openWidget(widget, withVideo: true, success: { [weak self] in
guard let self = self else { return }
if let jitsiVC = self.jitsiVC {
jitsiVC.delegate = self
self.presentCallVC(jitsiVC)
self.startJitsiCall(withWidget: widget)
}
}, failure: { [weak self] (error) in
guard let self = self else { return }
self.jitsiVC = nil
AppDelegate.theDelegate().showAlert(withTitle: nil,
message: VectorL10n.callJitsiError)
})
}
if let jitsiVC = jitsiVC {
if jitsiVC.widget.widgetId == widget.widgetId {
self.presentCallVC(jitsiVC)
} else {
// end previous Jitsi call first
endActiveJitsiCall()
createJitsiBlock()
}
} else {
createJitsiBlock()
}
#else
AppDelegate.theDelegate().showAlert(withTitle: nil,
message: Bundle.mxk_localizedString(forKey: "not_supported_yet"))
#endif
}
private func startJitsiCall(withWidget widget: Widget) {
if self.jitsiCalls.first(where: { $0.value.widgetId == widget.widgetId })?.key != nil {
// this Jitsi call is already managed by this class, no need to report the call again
return
}
guard let roomId = widget.roomId else {
return
}
guard let session = sessions.first else {
return
}
guard let room = session.room(withRoomId: roomId) else {
return
}
let newUUID = UUID()
let handle = CXHandle(type: .generic, value: roomId)
let startCallAction = CXStartCallAction(call: newUUID, handle: handle)
let transaction = CXTransaction(action: startCallAction)
JMCallKitProxy.request(transaction) { (error) in
if error == nil {
JMCallKitProxy.reportCallUpdate(with: newUUID,
handle: roomId,
displayName: room.summary.displayname,
hasVideo: true)
JMCallKitProxy.reportOutgoingCall(with: newUUID, connectedAt: nil)
self.jitsiCalls[newUUID] = widget
}
}
}
func endActiveJitsiCall() {
guard let jitsiVC = jitsiVC else {
// there is no active Jitsi call
return
}
if pipCallVC == jitsiVC {
// this call currently in the PiP mode,
// first present it by exiting PiP mode and then dismiss it
exitPipCallVC(jitsiVC)
}
dismissCallVC(jitsiVC)
jitsiVC.hangup()
self.jitsiVC = nil
guard let widget = jitsiVC.widget else {
return
}
guard let uuid = self.jitsiCalls.first(where: { $0.value.widgetId == widget.widgetId })?.key else {
// this Jitsi call is not managed by this class
return
}
let endCallAction = CXEndCallAction(call: uuid)
let transaction = CXTransaction(action: endCallAction)
JMCallKitProxy.request(transaction) { (error) in
if error == nil {
self.jitsiCalls.removeValue(forKey: uuid)
}
}
}
func processWidgetEvent(_ event: MXEvent, inSession session: MXSession) {
guard JMCallKitProxy.isProviderConfigured() else {
// CallKit proxy is not configured, no benefit in parsing the event
return
}
guard let widget = Widget(widgetEvent: event, inMatrixSession: session) else {
return
}
if self.jitsiCalls.first(where: { $0.value.widgetId == widget.widgetId })?.key != nil {
// this Jitsi call is already managed by this class, no need to report the call again
return
}
if widget.isActive {
guard widget.type == kWidgetTypeJitsiV1 || widget.type == kWidgetTypeJitsiV2 else {
// not a Jitsi widget, ignore
return
}
if let jitsiVC = jitsiVC,
jitsiVC.widget.widgetId == widget.widgetId {
// this is already the Jitsi call we have atm
return
}
if TimeInterval(event.age)/MSEC_PER_SEC > Constants.groupCallInviteLifetime {
// too late to process the event
return
}
// an active Jitsi widget
let newUUID = UUID()
// assume this Jitsi call will survive
self.jitsiCalls[newUUID] = widget
if event.sender == session.myUserId {
// outgoing call
JMCallKitProxy.reportOutgoingCall(with: newUUID, connectedAt: nil)
} else {
// incoming call
guard RiotSettings.shared.enableRingingForGroupCalls else {
// do not ring for Jitsi calls
return
}
let user = session.user(withUserId: event.sender)
let displayName = NSString.localizedUserNotificationString(forKey: "GROUP_CALL_FROM_USER",
arguments: [user?.displayname as Any])
JMCallKitProxy.reportNewIncomingCall(UUID: newUUID,
handle: widget.roomId,
displayName: displayName,
hasVideo: true) { (error) in
if error != nil {
self.jitsiCalls.removeValue(forKey: newUUID)
}
}
}
} else {
guard let uuid = self.jitsiCalls.first(where: { $0.value.widgetId == widget.widgetId })?.key else {
// this Jitsi call is not managed by this class
return
}
JMCallKitProxy.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
self.jitsiCalls.removeValue(forKey: uuid)
}
return false
}
// MARK: - Private
private func updateOnHoldCall() {
guard let presentedCallVC = presentedCallVC else {
guard let presentedCallVC = presentedCallVC as? CallViewController else {
return
}
@ -131,9 +340,6 @@ class CallPresenter: NSObject {
}
private func shouldHandleCall(_ call: MXCall) -> Bool {
if let delegate = delegate, !delegate.callPresenter(self, shouldHandleNewCall: call) {
return false
}
return callVCs.count < maximumNumberOfConcurrentCalls
}
@ -181,7 +387,19 @@ class CallPresenter: NSObject {
}
return
}
dismissCallVC(callVC, completion: completion)
if callVC.isDisplayingAlert {
completion()
} else {
dismissCallVC(callVC, completion: completion)
}
}
private func logCallVC(_ callVC: UIViewController, log: String) {
if let callVC = callVC as? CallViewController {
NSLog("[CallPresenter] \(log): call: \(String(describing: callVC.mxCall?.callId))")
} else if let callVC = callVC as? JitsiViewController {
NSLog("[CallPresenter] \(log): call: \(callVC.widget.widgetId)")
}
}
// MARK: - Timer
@ -200,17 +418,18 @@ class CallPresenter: NSObject {
}
@objc private func callTimerFired(_ timer: Timer) {
guard let inBarCallVC = inBarCallVC else {
return
if let inBarCallVC = inBarCallVC as? CallViewController {
guard let call = inBarCallVC.mxCall else {
return
}
guard call.state != .ended else {
return
}
updateCallBar()
} else if inBarCallVC as? JitsiViewController != nil {
updateCallBar()
}
guard let call = inBarCallVC.mxCall else {
return
}
guard call.state != .ended else {
return
}
presentCallBar(for: inBarCallVC, isUpdateOnly: true)
}
// MARK: - Observers
@ -232,8 +451,32 @@ class CallPresenter: NSObject {
selector: #selector(callTileTapped(_:)),
name: .RoomCallTileTapped,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(groupCallTileTapped(_:)),
name: .RoomGroupCallTileTapped,
object: nil)
isStarted = true
#if canImport(JitsiMeetSDK)
JMCallKitProxy.addListener(self)
guard let session = sessions.first else {
return
}
widgetEventsListener = session.listenToEvents([
MXEventType(identifier: kWidgetMatrixEventTypeString),
MXEventType(identifier: kWidgetModularEventTypeString)
]) { (event, direction, _) in
if direction == .backwards {
// ignore backwards events
return
}
self.processWidgetEvent(event, inSession: session)
}
#endif
}
private func removeCallObservers() {
@ -250,8 +493,24 @@ class CallPresenter: NSObject {
NotificationCenter.default.removeObserver(self,
name: .RoomCallTileTapped,
object: nil)
NotificationCenter.default.removeObserver(self,
name: .RoomGroupCallTileTapped,
object: nil)
isStarted = false
#if canImport(JitsiMeetSDK)
JMCallKitProxy.removeListener(self)
guard let session = sessions.first else {
return
}
if let widgetEventsListener = widgetEventsListener {
session.removeListener(widgetEventsListener)
}
widgetEventsListener = nil
#endif
}
@objc
@ -269,6 +528,15 @@ class CallPresenter: NSObject {
}
newCallVC.playRingtone = !isCallKitEnabled
newCallVC.delegate = self
if !call.isIncoming {
// put other native calls on hold
callVCs.values.forEach({ $0.mxCall.hold(true) })
// terminate Jitsi calls
endActiveJitsiCall()
}
callVCs[call.callId] = newCallVC
if UIApplication.shared.applicationState == .background && call.isIncoming {
@ -276,7 +544,7 @@ class CallPresenter: NSObject {
// Without CallKit this will allow us to play vibro until the call was ended
// With CallKit we'll inform the system when the call is ended to let the system terminate our app to save resources
let handler = MXSDKOptions.sharedInstance().backgroundModeHandler
let callBackgroundTask = handler.startBackgroundTask(withName: "[CallService] addMatrixCallObserver", expirationHandler: nil)
let callBackgroundTask = handler.startBackgroundTask(withName: "[CallPresenter] addMatrixCallObserver", expirationHandler: nil)
callBackgroundTasks[call.callId] = callBackgroundTask
}
@ -296,23 +564,23 @@ class CallPresenter: NSObject {
switch call.state {
case .createAnswer:
NSLog("[CallService] callStateChanged: call created answer: \(call.callId)")
NSLog("[CallPresenter] callStateChanged: call created answer: \(call.callId)")
if call.isIncoming, isCallKitEnabled, let callVC = callVCs[call.callId] {
presentCallVC(callVC)
}
case .connected:
NSLog("[CallService] callStateChanged: call connected: \(call.callId)")
NSLog("[CallPresenter] callStateChanged: call connected: \(call.callId)")
callTimer?.fire()
case .onHold:
NSLog("[CallService] callStateChanged: call holded: \(call.callId)")
NSLog("[CallPresenter] callStateChanged: call holded: \(call.callId)")
callTimer?.fire()
callHolded(withCallId: call.callId)
case .remotelyOnHold:
NSLog("[CallService] callStateChanged: call remotely holded: \(call.callId)")
NSLog("[CallPresenter] callStateChanged: call remotely holded: \(call.callId)")
callTimer?.fire()
callHolded(withCallId: call.callId)
case .ended:
NSLog("[CallService] callStateChanged: call ended: \(call.callId)")
NSLog("[CallPresenter] callStateChanged: call ended: \(call.callId)")
endCall(withCallId: call.callId)
default:
break
@ -321,7 +589,8 @@ class CallPresenter: NSObject {
@objc
private func callTileTapped(_ notification: Notification) {
NSLog("[CallService] callTileTapped")
NSLog("[CallPresenter] callTileTapped")
guard let bubbleData = notification.object as? RoomBubbleCellData else {
return
}
@ -334,7 +603,7 @@ class CallPresenter: NSObject {
return
}
NSLog("[CallService] callTileTapped: for call: \(callEventContent.callId)")
NSLog("[CallPresenter] callTileTapped: for call: \(callEventContent.callId)")
guard let session = sessions.first else { return }
@ -350,13 +619,55 @@ class CallPresenter: NSObject {
return
}
presentCallVC(callVC)
if callVC == pipCallVC {
exitPipCallVC(callVC)
} else {
presentCallVC(callVC)
}
}
@objc
private func groupCallTileTapped(_ notification: Notification) {
NSLog("[CallPresenter] groupCallTileTapped")
guard let bubbleData = notification.object as? RoomBubbleCellData else {
return
}
guard let randomEvent = bubbleData.allLinkedEvents().randomElement() else {
return
}
guard randomEvent.eventType == .custom,
(randomEvent.type == kWidgetMatrixEventTypeString ||
randomEvent.type == kWidgetModularEventTypeString) else {
return
}
guard let session = sessions.first else { return }
guard let widget = Widget(widgetEvent: randomEvent, inMatrixSession: session) else {
return
}
NSLog("[CallPresenter] groupCallTileTapped: for call: \(widget.widgetId)")
guard let jitsiVC = jitsiVC,
jitsiVC.widget.widgetId == widget.widgetId else {
return
}
if jitsiVC == pipCallVC {
exitPipCallVC(jitsiVC)
} else {
presentCallVC(jitsiVC)
}
}
// MARK: - Call Screens
private func presentCallVC(_ callVC: CallViewController, completion: (() -> Void)? = nil) {
NSLog("[CallService] presentCallVC: call: \(String(describing: callVC.mxCall?.callId))")
private func presentCallVC(_ callVC: UIViewController, completion: (() -> Void)? = nil) {
logCallVC(callVC, log: "presentCallVC")
// do not use PiP transitions here, as we really want to present the screen
callVC.transitioningDelegate = nil
@ -379,8 +690,8 @@ class CallPresenter: NSObject {
uiOperationQueue.addOperation(operation)
}
private func dismissCallVC(_ callVC: CallViewController, completion: (() -> Void)? = nil) {
NSLog("[CallService] dismissCallVC: call: \(String(describing: callVC.mxCall?.callId))")
private func dismissCallVC(_ callVC: UIViewController, completion: (() -> Void)? = nil) {
logCallVC(callVC, log: "dismissCallVC")
// do not use PiP transitions here, as we really want to dismiss the screen
callVC.transitioningDelegate = nil
@ -394,8 +705,8 @@ class CallPresenter: NSObject {
uiOperationQueue.addOperation(operation)
}
private func enterPipCallVC(_ callVC: CallViewController, completion: (() -> Void)? = nil) {
NSLog("[CallService] enterPipCallVC: call: \(String(describing: callVC.mxCall?.callId))")
private func enterPipCallVC(_ callVC: UIViewController, completion: (() -> Void)? = nil) {
logCallVC(callVC, log: "enterPipCallVC")
// assign self as transitioning delegate
callVC.transitioningDelegate = self
@ -410,8 +721,8 @@ class CallPresenter: NSObject {
uiOperationQueue.addOperation(operation)
}
private func exitPipCallVC(_ callVC: CallViewController, completion: (() -> Void)? = nil) {
NSLog("[CallService] exitPipCallVC: call: \(String(describing: callVC.mxCall?.callId))")
private func exitPipCallVC(_ callVC: UIViewController, completion: (() -> Void)? = nil) {
logCallVC(callVC, log: "exitPipCallVC")
// assign self as transitioning delegate
callVC.transitioningDelegate = self
@ -428,24 +739,29 @@ class CallPresenter: NSObject {
// MARK: - Call Bar
private func presentCallBar(for callVC: CallViewController?, isUpdateOnly: Bool = false, completion: (() -> Void)? = nil) {
NSLog("[CallService] presentCallBar: call: \(String(describing: callVC?.mxCall?.callId))")
private func presentCallBar(for callVC: UIViewController, completion: (() -> Void)? = nil) {
logCallVC(callVC, log: "presentCallBar")
let activeCallVC = self.activeCallVC
let operation = CallBarPresentOperation(presenter: self, activeCallVC: activeCallVC, numberOfPausedCalls: numberOfPausedCalls) { [weak self] in
// active calls are more prior to paused ones.
// So, if user taps the bar when we have one active and one paused calls, we navigate to the active one.
if !isUpdateOnly {
self?.inBarCallVC = activeCallVC ?? callVC
}
// So, if user taps the bar when we have one active and one paused call, we navigate to the active one.
self?.inBarCallVC = activeCallVC ?? callVC
completion?()
}
uiOperationQueue.addOperation(operation)
}
private func dismissCallBar(for callVC: CallViewController, completion: (() -> Void)? = nil) {
NSLog("[CallService] dismissCallBar: call: \(String(describing: callVC.mxCall?.callId))")
private func updateCallBar() {
let activeCallVC = self.activeCallVC
let operation = CallBarUpdateOperation(presenter: self, activeCallVC: activeCallVC, numberOfPausedCalls: numberOfPausedCalls)
uiOperationQueue.addOperation(operation)
}
private func dismissCallBar(for callVC: UIViewController, completion: (() -> Void)? = nil) {
logCallVC(callVC, log: "dismissCallBar")
let operation = CallBarDismissOperation(presenter: self) { [weak self] in
if callVC == self?.inBarCallVC {
@ -474,8 +790,13 @@ extension CallPresenter: MXKCallViewControllerDelegate {
// wait for the call state changes, will be handled there
return
} else {
dismissCallVC(callVC)
self.presentCallBar(for: callVC, completion: completion)
if callVC.mxCall.isVideoCall {
// go to pip mode here
enterPipCallVC(callVC, completion: completion)
} else {
dismissCallVC(callVC)
self.presentCallBar(for: callVC, completion: completion)
}
}
}
@ -497,20 +818,6 @@ extension CallPresenter: MXKCallViewControllerDelegate {
presentCallVC(onHoldCallVC)
}
func callViewControllerDidTapPiPButton(_ callViewController: MXKCallViewController!) {
guard let callVC = callViewController as? CallViewController else {
// this call screen is not handled by this service
return
}
// sanity check
// do not enter PiP mode if not a video call
guard callVC.mxCall.isVideoCall else { return }
// go to pip mode here
enterPipCallVC(callVC)
}
}
// MARK: - UIViewControllerTransitioningDelegate
@ -562,3 +869,84 @@ extension OperationQueue {
}
}
#if canImport(JitsiMeetSDK)
// MARK: - JMCallKitListener
extension CallPresenter: JMCallKitListener {
func providerDidReset() {
}
func performAnswerCall(UUID: UUID) {
guard let widget = jitsiCalls[UUID] else {
return
}
displayJitsiCall(withWidget: widget)
}
func performEndCall(UUID: UUID) {
guard let widget = jitsiCalls[UUID] else {
return
}
if let jitsiVC = jitsiVC, jitsiVC.widget.widgetId == widget.widgetId {
// hangup an active call
dismissCallVC(jitsiVC)
endActiveJitsiCall()
} else {
// decline incoming call
JitsiService.shared.declineWidget(withId: widget.widgetId)
}
}
func performSetMutedCall(UUID: UUID, isMuted: Bool) {
guard let widget = jitsiCalls[UUID] else {
return
}
if let jitsiVC = jitsiVC, jitsiVC.widget.widgetId == widget.widgetId {
// mute the active Jitsi call
jitsiVC.setAudioMuted(isMuted)
}
}
func performStartCall(UUID: UUID, isVideo: Bool) {
}
func providerDidActivateAudioSession(session: AVAudioSession) {
}
func providerDidDeactivateAudioSession(session: AVAudioSession) {
}
func providerTimedOutPerformingAction(action: CXAction) {
}
}
// MARK: - JitsiViewControllerDelegate
extension CallPresenter: JitsiViewControllerDelegate {
func jitsiViewController(_ jitsiViewController: JitsiViewController!, dismissViewJitsiController completion: (() -> Void)!) {
if jitsiViewController == jitsiVC {
endActiveJitsiCall()
}
}
func jitsiViewController(_ jitsiViewController: JitsiViewController!, goBackToApp completion: (() -> Void)!) {
if jitsiViewController == jitsiVC {
enterPipCallVC(jitsiViewController, completion: completion)
}
}
}
#endif

View file

@ -18,32 +18,31 @@ import Foundation
@objc
protocol CallPresenterDelegate: class {
// New call
func callPresenter(_ presenter: CallPresenter,
shouldHandleNewCall call: MXCall) -> Bool
// Call screens
func callPresenter(_ presenter: CallPresenter,
presentCallViewController viewController: CallViewController,
presentCallViewController viewController: UIViewController,
completion:(() -> Void)?)
func callPresenter(_ presenter: CallPresenter,
dismissCallViewController viewController: CallViewController,
dismissCallViewController viewController: UIViewController,
completion:(() -> Void)?)
// Call Bar
func callPresenter(_ presenter: CallPresenter,
presentCallBarFor activeCallViewController: CallViewController?,
presentCallBarFor activeCallViewController: UIViewController?,
numberOfPausedCalls: UInt,
completion:(() -> Void)?)
func callPresenter(_ presenter: CallPresenter,
updateCallBarFor activeCallViewController: UIViewController?,
numberOfPausedCalls: UInt)
func callPresenter(_ presenter: CallPresenter,
dismissCallBar completion:(() -> Void)?)
// PiP
func callPresenter(_ presenter: CallPresenter,
enterPipForCallViewController viewController: CallViewController,
enterPipForCallViewController viewController: UIViewController,
completion:(() -> Void)?)
func callPresenter(_ presenter: CallPresenter,
exitPipForCallViewController viewController: CallViewController,
exitPipForCallViewController viewController: UIViewController,
completion:(() -> Void)?)
}

View file

@ -19,12 +19,12 @@ import Foundation
class CallBarPresentOperation: AsyncOperation {
private var presenter: CallPresenter
private var activeCallVC: CallViewController?
private var activeCallVC: UIViewController?
private var numberOfPausedCalls: UInt
private var completion: (() -> Void)?
init(presenter: CallPresenter,
activeCallVC: CallViewController?,
activeCallVC: UIViewController?,
numberOfPausedCalls: UInt,
completion: (() -> Void)? = nil) {
self.presenter = presenter
@ -36,7 +36,10 @@ class CallBarPresentOperation: AsyncOperation {
override func main() {
presenter.delegate?.callPresenter(presenter, presentCallBarFor: activeCallVC, numberOfPausedCalls: numberOfPausedCalls, completion: {
self.finish()
self.completion?()
// wait for the next life cycle to detect status bar layout updates
DispatchQueue.main.async {
self.completion?()
}
})
}

View file

@ -0,0 +1,38 @@
//
// 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
class CallBarUpdateOperation: AsyncOperation {
private var presenter: CallPresenter
private var activeCallVC: UIViewController?
private var numberOfPausedCalls: UInt
init(presenter: CallPresenter,
activeCallVC: UIViewController?,
numberOfPausedCalls: UInt) {
self.presenter = presenter
self.activeCallVC = activeCallVC
self.numberOfPausedCalls = numberOfPausedCalls
}
override func main() {
presenter.delegate?.callPresenter(presenter, updateCallBarFor: activeCallVC, numberOfPausedCalls: numberOfPausedCalls)
self.finish()
}
}

View file

@ -19,11 +19,11 @@ import Foundation
class CallVCDismissOperation: AsyncOperation {
private var presenter: CallPresenter
private var callVC: CallViewController
private var callVC: UIViewController
private var completion: (() -> Void)?
init(presenter: CallPresenter,
callVC: CallViewController,
callVC: UIViewController,
completion: (() -> Void)? = nil) {
self.presenter = presenter
self.callVC = callVC

View file

@ -19,11 +19,11 @@ import Foundation
class CallVCEnterPipOperation: AsyncOperation {
private var presenter: CallPresenter
private var callVC: CallViewController
private var callVC: UIViewController
private var completion: (() -> Void)?
init(presenter: CallPresenter,
callVC: CallViewController,
callVC: UIViewController,
completion: (() -> Void)? = nil) {
self.presenter = presenter
self.callVC = callVC

View file

@ -19,11 +19,11 @@ import Foundation
class CallVCExitPipOperation: AsyncOperation {
private var presenter: CallPresenter
private var callVC: CallViewController
private var callVC: UIViewController
private var completion: (() -> Void)?
init(presenter: CallPresenter,
callVC: CallViewController,
callVC: UIViewController,
completion: (() -> Void)? = nil) {
self.presenter = presenter
self.callVC = callVC

View file

@ -19,11 +19,11 @@ import Foundation
class CallVCPresentOperation: AsyncOperation {
private var presenter: CallPresenter
private var callVC: CallViewController
private var callVC: UIViewController
private var completion: (() -> Void)?
init(presenter: CallPresenter,
callVC: CallViewController,
callVC: UIViewController,
completion: (() -> Void)? = nil) {
self.presenter = presenter
self.callVC = callVC

View file

@ -25,7 +25,7 @@ import Foundation
class PiPAnimator: NSObject {
private enum Constants {
static let pipViewScale: CGFloat = 0.3
static let pipViewSize: CGSize = CGSize(width: 90, height: 130)
}
let animationDuration: TimeInterval
@ -62,15 +62,15 @@ class PiPAnimator: NSObject {
pipView.delegate = pipViewDelegate
keyWindow.addSubview(pipView)
let transform = CGAffineTransform(scaleX: Constants.pipViewScale, y: Constants.pipViewScale)
let targetRect = fromVC.view.bounds.applying(transform)
let scale = Constants.pipViewSize.width/pipView.frame.width
let transform = CGAffineTransform(scaleX: scale, y: scale)
let targetSize = Constants.pipViewSize
let animator = UIViewPropertyAnimator(duration: animationDuration, dampingRatio: 1) {
pipView.transform = transform
pipView.move(in: keyWindow,
to: .bottomLeft,
targetSize: targetRect.size)
targetSize: targetSize)
}
animator.addCompletion { (position) in
@ -117,6 +117,7 @@ class PiPAnimator: NSObject {
animator.addCompletion { (position) in
toVC.additionalSafeAreaInsets = .zero
toVC.view.frame = context.finalFrame(for: toVC)
toVC.view.isHidden = false
snapshot.removeFromSuperview()

View file

@ -17,8 +17,8 @@
import UIKit
@objc enum PiPViewPosition: Int {
case bottomLeft // default value
case bottomRight
case bottomLeft
case bottomRight // default value
case topRight
case topLeft
}
@ -32,12 +32,12 @@ import UIKit
class PiPView: UIView {
private enum Defaults {
static let margins: UIOffset = UIOffset(horizontal: 20, vertical: 20)
static let margins: UIEdgeInsets = UIEdgeInsets(top: 64, left: 20, bottom: 64, right: 20)
static let cornerRadius: CGFloat = 8
static let animationDuration: TimeInterval = 0.25
}
var margins: UIOffset = Defaults.margins {
var margins: UIEdgeInsets = Defaults.margins {
didSet {
guard self.superview != nil else { return }
self.move(to: self.position, animated: true)
@ -48,7 +48,7 @@ class PiPView: UIView {
layer.cornerRadius = cornerRadius
}
}
var position: PiPViewPosition = .bottomLeft
var position: PiPViewPosition = .bottomRight
weak var delegate: PiPViewDelegate?
private var originalCenter: CGPoint = .zero
@ -97,7 +97,7 @@ NSLayoutConstraint.activate([
}
func move(in view: UIView? = nil,
to position: PiPViewPosition = .bottomLeft,
to position: PiPViewPosition = .bottomRight,
targetSize: CGSize? = nil,
animated: Bool = false,
completion: ((Bool) -> Void)? = nil) {
@ -148,25 +148,36 @@ NSLayoutConstraint.activate([
}
let targetSize = targetSize ?? frame.size
var superviewWidth: CGFloat = 0
var superviewHeight: CGFloat = 0
if UIDevice.current.orientation.isPortrait {
superviewWidth = min(view.bounds.width, view.bounds.height)
superviewHeight = max(view.bounds.width, view.bounds.height)
} else {
superviewWidth = max(view.bounds.width, view.bounds.height)
superviewHeight = min(view.bounds.width, view.bounds.height)
}
switch position {
case .bottomLeft:
let origin = CGPoint(x: margins.horizontal + view.safeAreaInsets.left,
y: view.bounds.height - view.safeAreaInsets.bottom - targetSize.height - margins.vertical)
let origin = CGPoint(x: margins.left + view.safeAreaInsets.left,
y: superviewHeight - view.safeAreaInsets.bottom - targetSize.height - margins.bottom)
return CGRect(origin: origin,
size: targetSize)
case .bottomRight:
let origin = CGPoint(x: view.bounds.width - view.safeAreaInsets.right - margins.horizontal - targetSize.width,
y: view.bounds.height - view.safeAreaInsets.bottom - targetSize.height - margins.vertical)
let origin = CGPoint(x: superviewWidth - view.safeAreaInsets.right - margins.right - targetSize.width,
y: superviewHeight - view.safeAreaInsets.bottom - targetSize.height - margins.bottom)
return CGRect(origin: origin,
size: targetSize)
case .topRight:
let origin = CGPoint(x: view.bounds.width - view.safeAreaInsets.right - margins.horizontal - targetSize.width,
y: margins.vertical + view.safeAreaInsets.top)
let origin = CGPoint(x: superviewWidth - view.safeAreaInsets.right - margins.right - targetSize.width,
y: margins.top + view.safeAreaInsets.top)
return CGRect(origin: origin,
size: targetSize)
case .topLeft:
let origin = CGPoint(x: margins.horizontal + view.safeAreaInsets.left,
y: margins.vertical + view.safeAreaInsets.top)
let origin = CGPoint(x: margins.left + view.safeAreaInsets.left,
y: margins.top + view.safeAreaInsets.top)
return CGRect(origin: origin,
size: targetSize)
}

View file

@ -323,6 +323,30 @@ Matrix session observer used to detect new opened sessions.
#pragma mark - UNUserNotificationCenterDelegate
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
NSDictionary *userInfo = notification.request.content.userInfo;
if (userInfo[Constants.userInfoKeyPresentNotificationOnForeground])
{
if (!userInfo[Constants.userInfoKeyPresentNotificationInRoom]
&& [[AppDelegate theDelegate].visibleRoomId isEqualToString:userInfo[@"room_id"]])
{
// do not show the notification when we're in the notified room
completionHandler(UNNotificationPresentationOptionNone);
}
else
{
completionHandler(UNNotificationPresentationOptionBadge
| UNNotificationPresentationOptionSound
| UNNotificationPresentationOptionAlert);
}
}
else
{
completionHandler(UNNotificationPresentationOptionNone);
}
}
// iOS 10+, see application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
@ -568,23 +592,37 @@ Matrix session observer used to detect new opened sessions.
return;
}
// process the call invite synchronously
[session.callManager handleCallEvent:lastCallInvite];
MXCall *call = [session.callManager callWithCallId:lastCallInvite.content[@"call_id"]];
if (call)
if (lastCallInvite.eventType == MXEventTypeCallInvite)
{
[session.callManager.callKitAdapter reportIncomingCall:call];
NSLog(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Reporting new call in room %@ for the event: %@", roomId, eventId);
// Wait for the sync response in cache to be processed for data integrity.
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
// After reporting the call, we can continue async. Launch a background sync to handle call answers/declines on other devices of the user.
[self launchBackgroundSync];
});
// process the call invite synchronously
[session.callManager handleCallEvent:lastCallInvite];
MXCall *call = [session.callManager callWithCallId:lastCallInvite.content[@"call_id"]];
if (call)
{
[session.callManager.callKitAdapter reportIncomingCall:call];
NSLog(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Reporting new call in room %@ for the event: %@", roomId, eventId);
// Wait for the sync response in cache to be processed for data integrity.
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
// After reporting the call, we can continue async. Launch a background sync to handle call answers/declines on other devices of the user.
[self launchBackgroundSync];
});
}
else
{
NSLog(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Error on call object on room %@ for the event: %@", roomId, eventId);
}
}
else if ([lastCallInvite.type isEqualToString:kWidgetMatrixEventTypeString] ||
[lastCallInvite.type isEqualToString:kWidgetModularEventTypeString])
{
[[AppDelegate theDelegate].callPresenter processWidgetEvent:lastCallInvite
inSession:session];
}
else
{
NSLog(@"[PushNotificationService] didReceiveIncomingPushWithPayload: Error on call object on room %@ for the event: %@", roomId, eventId);
// It's a serious error. There is nothing to avoid iOS to kill us here.
NSLog(@"[PushNotificationService] didReceiveIncomingPushWithPayload: We have an unknown type of event for %@. There is something wrong.", eventId);
}
}
else

View file

@ -27,7 +27,6 @@ final class RiotSettings: NSObject {
static let identityServerUrlString = "identityserverurl"
static let enableCrashReport = "enableCrashReport"
static let enableRageShake = "enableRageShake"
static let createConferenceCallsWithJitsi = "createConferenceCallsWithJitsi"
static let userInterfaceTheme = "userInterfaceTheme"
static let notificationsShowDecryptedContent = "showDecryptedContent"
static let pinRoomsWithMissedNotifications = "pinRoomsWithMissedNotif"
@ -52,6 +51,7 @@ final class RiotSettings: NSObject {
static let roomCreationScreenAllowRoomTypeConfiguration = "roomCreationScreenAllowRoomTypeConfiguration"
static let roomCreationScreenRoomIsPublic = "roomCreationScreenRoomIsPublic"
static let allowInviteExernalUsers = "allowInviteExernalUsers"
static let enableRingingForGroupCalls = "enableRingingForGroupCalls"
static let roomSettingsScreenShowLowPriorityOption = "roomSettingsScreenShowLowPriorityOption"
static let roomSettingsScreenShowDirectChatOption = "roomSettingsScreenShowDirectChatOption"
static let roomSettingsScreenAllowChangingAccessSettings = "roomSettingsScreenAllowChangingAccessSettings"
@ -60,6 +60,8 @@ final class RiotSettings: NSObject {
static let roomSettingsScreenShowFlairSettings = "roomSettingsScreenShowFlairSettings"
static let roomSettingsScreenShowAdvancedSettings = "roomSettingsScreenShowAdvancedSettings"
static let roomSettingsScreenAdvancedShowEncryptToVerifiedOption = "roomSettingsScreenAdvancedShowEncryptToVerifiedOption"
static let settingsScreenShowNotificationDecodedContentOption = "settingsScreenShowNotificationDecodedContentOption"
static let settingsScreenShowNsfwRoomsOption = "settingsScreenShowNsfwRoomsOption"
static let roomsAllowToJoinPublicRooms = "roomsAllowToJoinPublicRooms"
static let homeScreenShowFavouritesTab = "homeScreenShowFavouritesTab"
static let homeScreenShowPeopleTab = "homeScreenShowPeopleTab"
@ -71,7 +73,11 @@ final class RiotSettings: NSObject {
static let roomScreenAllowMediaLibraryAction = "roomScreenAllowMediaLibraryAction"
static let roomScreenAllowStickerAction = "roomScreenAllowStickerAction"
static let roomScreenAllowFilesAction = "roomScreenAllowFilesAction"
static let roomContextualMenuShowMoreOptionForMessages = "roomContextualMenuShowMoreOptionForMessages"
static let roomContextualMenuShowMoreOptionForStates = "roomContextualMenuShowMoreOptionForStates"
static let roomContextualMenuShowReportContentOption = "roomContextualMenuShowReportContentOption"
static let roomInfoScreenShowIntegrations = "roomInfoScreenShowIntegrations"
static let roomMemberScreenShowIgnore = "roomMemberScreenShowIgnore"
static let unifiedSearchScreenShowPublicDirectory = "unifiedSearchScreenShowPublicDirectory"
}
@ -85,36 +91,6 @@ final class RiotSettings: NSObject {
return userDefaults
}()
// MARK: - Public
func reset() {
defaults.removeObject(forKey: UserDefaultsKeys.settingsScreenShowChangePassword)
defaults.removeObject(forKey: UserDefaultsKeys.settingsScreenShowInviteFriends)
defaults.removeObject(forKey: UserDefaultsKeys.settingsScreenShowEnableStunServerFallback)
defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowSessions)
defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowSetupBackup)
defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowRestoreBackup)
defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowDeleteBackup)
defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyInfo)
defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowCryptographyExport)
defaults.removeObject(forKey: UserDefaultsKeys.settingsSecurityScreenShowAdvancedUnverifiedDevices)
defaults.removeObject(forKey: UserDefaultsKeys.roomCreationScreenAllowEncryptionConfiguration)
defaults.removeObject(forKey: UserDefaultsKeys.roomCreationScreenRoomIsEncrypted)
defaults.removeObject(forKey: UserDefaultsKeys.roomCreationScreenAllowRoomTypeConfiguration)
defaults.removeObject(forKey: UserDefaultsKeys.roomCreationScreenRoomIsPublic)
defaults.removeObject(forKey: UserDefaultsKeys.allowInviteExernalUsers)
defaults.removeObject(forKey: UserDefaultsKeys.roomSettingsScreenShowLowPriorityOption)
defaults.removeObject(forKey: UserDefaultsKeys.roomSettingsScreenShowDirectChatOption)
defaults.removeObject(forKey: UserDefaultsKeys.roomSettingsScreenAllowChangingAccessSettings)
defaults.removeObject(forKey: UserDefaultsKeys.roomSettingsScreenAllowChangingHistorySettings)
defaults.removeObject(forKey: UserDefaultsKeys.roomSettingsScreenShowAddressSettings)
defaults.removeObject(forKey: UserDefaultsKeys.roomSettingsScreenShowFlairSettings)
defaults.removeObject(forKey: UserDefaultsKeys.roomSettingsScreenShowAdvancedSettings)
defaults.removeObject(forKey: UserDefaultsKeys.roomSettingsScreenAdvancedShowEncryptToVerifiedOption)
defaults.removeObject(forKey: UserDefaultsKeys.allowInviteExernalUsers)
defaults.removeObject(forKey: UserDefaultsKeys.roomsAllowToJoinPublicRooms)
}
// MARK: Servers
var homeserverUrlString: String {
@ -228,14 +204,15 @@ final class RiotSettings: NSObject {
// MARK: Labs
var createConferenceCallsWithJitsi: Bool {
/// Indicates if CallKit ringing is enabled for group calls. This setting does not disable the CallKit integration for group calls, only relates to ringing.
var enableRingingForGroupCalls: Bool {
get {
return defaults.bool(forKey: UserDefaultsKeys.createConferenceCallsWithJitsi)
return defaults.bool(forKey: UserDefaultsKeys.enableRingingForGroupCalls)
} set {
defaults.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi)
defaults.set(newValue, forKey: UserDefaultsKeys.enableRingingForGroupCalls)
}
}
// MARK: Calls
/// Indicate if `allowStunServerFallback` settings has been set once.
@ -352,6 +329,39 @@ final class RiotSettings: NSObject {
defaults.set(newValue, forKey: UserDefaultsKeys.roomScreenAllowFilesAction)
}
}
// MARK: - Room Contextual Menu
var roomContextualMenuShowMoreOptionForMessages: Bool {
get {
guard defaults.object(forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForMessages) != nil else {
return BuildSettings.roomContextualMenuShowMoreOptionForMessages
}
return defaults.bool(forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForMessages)
} set {
defaults.set(newValue, forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForMessages)
}
}
var roomContextualMenuShowMoreOptionForStates: Bool {
get {
guard defaults.object(forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForStates) != nil else {
return BuildSettings.roomContextualMenuShowMoreOptionForStates
}
return defaults.bool(forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForStates)
} set {
defaults.set(newValue, forKey: UserDefaultsKeys.roomContextualMenuShowMoreOptionForStates)
}
}
var roomContextualMenuShowReportContentOption: Bool {
get {
guard defaults.object(forKey: UserDefaultsKeys.roomContextualMenuShowReportContentOption) != nil else {
return BuildSettings.roomContextualMenuShowReportContentOption
}
return defaults.bool(forKey: UserDefaultsKeys.roomContextualMenuShowReportContentOption)
} set {
defaults.set(newValue, forKey: UserDefaultsKeys.roomContextualMenuShowReportContentOption)
}
}
// MARK: - Room Info Screen
@ -366,6 +376,19 @@ final class RiotSettings: NSObject {
}
}
// MARK: - Room Member Screen
var roomMemberScreenShowIgnore: Bool {
get {
guard defaults.object(forKey: UserDefaultsKeys.roomMemberScreenShowIgnore) != nil else {
return BuildSettings.roomMemberScreenShowIgnore
}
return defaults.bool(forKey: UserDefaultsKeys.roomMemberScreenShowIgnore)
} set {
defaults.set(newValue, forKey: UserDefaultsKeys.roomMemberScreenShowIgnore)
}
}
// MARK: - Room Creation Screen
var roomCreationScreenAllowEncryptionConfiguration: Bool {
@ -497,6 +520,26 @@ final class RiotSettings: NSObject {
defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowEnableStunServerFallback)
}
}
var settingsScreenShowNotificationDecodedContentOption: Bool {
get {
guard defaults.object(forKey: UserDefaultsKeys.settingsScreenShowNotificationDecodedContentOption) != nil else {
return BuildSettings.settingsScreenShowNotificationDecodedContentOption
}
return defaults.bool(forKey: UserDefaultsKeys.settingsScreenShowNotificationDecodedContentOption)
} set {
defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowNotificationDecodedContentOption)
}
}
var settingsScreenShowNsfwRoomsOption: Bool {
get {
guard defaults.object(forKey: UserDefaultsKeys.settingsScreenShowNsfwRoomsOption) != nil else {
return BuildSettings.settingsScreenShowNsfwRoomsOption
}
return defaults.bool(forKey: UserDefaultsKeys.settingsScreenShowNsfwRoomsOption)
} set {
defaults.set(newValue, forKey: UserDefaultsKeys.settingsScreenShowNsfwRoomsOption)
}
}
var settingsSecurityScreenShowSessions: Bool {
get {
guard defaults.object(forKey: UserDefaultsKeys.settingsSecurityScreenShowSessions) != nil else {

View file

@ -15,10 +15,11 @@
*/
import UIKit
import DesignKit
/// Provide color constant values defined by the designer
/// https://app.zeplin.io/project/5c122fa790c5b4241ffa6be7/screen/5c619592daff2f1241d82e75
@objc protocol Theme {
@objc protocol Theme: ThemeV2 {
var identifier: String { get }
@ -93,6 +94,9 @@ import UIKit
/// Color to tint the search background image
var matrixSearchBackgroundImageTintColor: UIColor { get }
/// Color to use in shadows. Should be contrast to `backgroundColor`.
var shadowColor: UIColor { get }
// MARK: - Customisation methods

View file

@ -155,6 +155,9 @@ NSString *const kThemeServiceDidChangeThemeNotification = @"kThemeServiceDidChan
// Define the UISearchBar cancel button color
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UISearchBar class]]] setTitleTextAttributes:@{ NSForegroundColorAttributeName : self.theme.tintColor } forState: UIControlStateNormal];
[[UIStackView appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class]]] setSpacing:-7];
[[UIStackView appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class]]] setDistribution:UIStackViewDistributionEqualCentering];
}
@end

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