Merge pull request #6414 from vector-im/release/1.8.21/release

Release 1.8.21
This commit is contained in:
Doug
2022-07-12 19:29:34 +01:00
committed by GitHub
164 changed files with 2579 additions and 1808 deletions
+7 -1
View File
@@ -30,8 +30,14 @@ vendor/
Pods/
## Ignore project files as we generate them with xcodegen (https://github.com/yonaskolb/XcodeGen)
# Plus ridiculous workaround to unignore the Package.resolved file for SwiftPM.
*.xcodeproj
*.xcworkspace
*.xcworkspace/*
!Riot.xcworkspace/xcshareddata
Riot.xcworkspace/xcshareddata/*
!Riot.xcworkspace/xcshareddata/swiftpm/
Riot.xcworkspace/xcshareddata/swiftpm/*
!Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved
# Fastlane
fastlane/report.xml
+47
View File
@@ -1,3 +1,50 @@
## Changes in 1.8.21 (2022-07-12)
✨ Features
- Analytics: Track non-fatal issues if consent provided ([#6308](https://github.com/vector-im/element-ios/pull/6308))
- Notifications: Add a setting for in-app notifications and use the value with existing functionality in PushNotificationService. ([#1108](https://github.com/vector-im/element-ios/issues/1108))
- Server Offline Activity Indicator ([#5607](https://github.com/vector-im/element-ios/issues/5607))
🙌 Improvements
- Add formatter build reply HTML unit tests ([#6380](https://github.com/vector-im/element-ios/pull/6380))
- Upgrade MatrixSDK version ([v0.23.11](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.23.11)).
- Update Files component ([#5372](https://github.com/vector-im/element-ios/issues/5372))
- Location sharing: Update map credits display and behavior. ([#6108](https://github.com/vector-im/element-ios/issues/6108))
- Location sharing: Add view to promote live location sharing labs flag on the sharing screen. ([#6238](https://github.com/vector-im/element-ios/issues/6238))
- Remove legacy Riot-Defaults property list ([#6273](https://github.com/vector-im/element-ios/issues/6273))
- DesignKit: Replace the local DesignKit target with the shared Swift package from ElementX. ([#6276](https://github.com/vector-im/element-ios/issues/6276))
- Enhance the VectorHostingController to be presented as a bottom sheet ([#6376](https://github.com/vector-im/element-ios/issues/6376))
- Location sharing: Live location sharing UI polishing. ([#6382](https://github.com/vector-im/element-ios/issues/6382))
🐛 Bugfixes
- VectorHostingController: Fix infinite loop due to the safe area insets fix. ([#6381](https://github.com/vector-im/element-ios/pull/6381))
- Fix layout issues in timeline poll cells (PSB-125) ([#5326](https://github.com/vector-im/element-ios/issues/5326))
- Fixed Invite user UI is always hidden by the keyboard ([#5341](https://github.com/vector-im/element-ios/issues/5341))
- Cross-Signing: Use ZXing library to generate QR codes ([#6358](https://github.com/vector-im/element-ios/issues/6358))
- Location sharing: Fix live location sharing lab flag activation, no more app relaunch needed. ([#6361](https://github.com/vector-im/element-ios/issues/6361))
- Display fallback when replied event content is partially missing ([#6371](https://github.com/vector-im/element-ios/issues/6371))
- Fix a few failing UI tests. ([#6386](https://github.com/vector-im/element-ios/issues/6386))
- Rename riot-keys.txt to element-keys.txt. ([#6391](https://github.com/vector-im/element-ios/issues/6391))
- Fix inoperant room links with alias/identifiers ([#6395](https://github.com/vector-im/element-ios/issues/6395))
- Fix slash commands from room composer ([#6398](https://github.com/vector-im/element-ios/issues/6398))
⚠️ API Changes
- Replace DesignKit framework with [DesignKit package](https://github.com/vector-im/element-x-ios/tree/develop/DesignKit/Sources). Colours are now generated in the [DesignTokens repo](https://github.com/vector-im/element-design-tokens) to be shared across all of our apps. ([#6275](https://github.com/vector-im/element-ios/pull/6275))
🧱 Build
- Update Podfile.lock ([#6387](https://github.com/vector-im/element-ios/pull/6387))
- Split `IntentHandler` into smaller, dedicated entities ([#6203](https://github.com/vector-im/element-ios/issues/6203))
Others
- Revert some font changes made when merging #6392. ([#6392](https://github.com/vector-im/element-ios/issues/6392))
## Changes in 1.8.20 (2022-06-28)
✨ Features
+2 -2
View File
@@ -15,5 +15,5 @@
//
// Version
MARKETING_VERSION = 1.8.20
CURRENT_PROJECT_VERSION = 1.8.20
MARKETING_VERSION = 1.8.21
CURRENT_PROJECT_VERSION = 1.8.21
+13
View File
@@ -405,4 +405,17 @@ final class BuildSettings: NSObject {
static let tileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx")!
static let locationSharingEnabled = true
// MARK: - MXKAppSettings
static let enableBotCreation: Bool = false
static let maxAllowedMediaCacheSize: Int = 1073741824
static let presenceColorForOfflineUser: Int = 15020851
static let presenceColorForOnlineUser: Int = 3401011
static let presenceColorForUnavailableUser: Int = 15066368
static let showAllEventsInRoomHistory: Bool = false
static let showLeftMembersInRoomMemberList: Bool = false
static let showRedactionsInRoomHistory: Bool = true
static let showUnsupportedEventsInRoomHistory: Bool = false
static let sortRoomMembersUsingLastSeenTime: Bool = true
static let syncLocalContacts: Bool = false
}
-55
View File
@@ -1,55 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
public extension UIFont {
// MARK: - Convenient methods
/// Update current font with a SymbolicTraits
func vc_withTraits(_ traits: UIFontDescriptor.SymbolicTraits) -> UIFont {
guard let descriptor = fontDescriptor.withSymbolicTraits(traits) else {
return self
}
return UIFont(descriptor: descriptor, size: 0) // Size 0 means keep the size as it is
}
/// Update current font with a given Weight
func vc_withWeight(weight: Weight) -> UIFont {
// Add the font weight to the descriptor
let weightedFontDescriptor = fontDescriptor.addingAttributes([
UIFontDescriptor.AttributeName.traits: [
UIFontDescriptor.TraitKey.weight: weight
]
])
return UIFont(descriptor: weightedFontDescriptor, size: 0)
}
// MARK: - Shortcuts
var vc_bold: UIFont {
return self.vc_withTraits(.traitBold)
}
var vc_semiBold: UIFont {
return self.vc_withWeight(weight: .semibold)
}
var vc_italic: UIFont {
return self.vc_withTraits(.traitItalic)
}
}
-22
View File
@@ -1,22 +0,0 @@
<?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>
-52
View File
@@ -1,52 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
/**
Struct for holding colour values for a particular theme.
*/
public struct ColorValues: Colors {
public let accent: UIColor
public let alert: UIColor
public let primaryContent: UIColor
public let secondaryContent: UIColor
public let tertiaryContent: UIColor
public let quarterlyContent: UIColor
public let quinaryContent: UIColor
public let separator: UIColor
public let system: UIColor
public let tile: UIColor
public let navigation: UIColor
public let background: UIColor
public let ems: UIColor
public let namesAndAvatars: [UIColor]
}
-73
View File
@@ -1,73 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// Colors at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1255%3A1104
public protocol Colors {
associatedtype ColorType
/// - Focused/Active states
/// - CTAs
var accent: ColorType { get }
/// - Error messages
/// - Content requiring user attention
/// - Notification, alerts
var alert: ColorType { get }
/// - Text
/// - Icons
var primaryContent: ColorType { get }
/// - Text
/// - Icons
var secondaryContent: ColorType { get }
/// - Text
/// - Icons
var tertiaryContent: ColorType { get }
/// - Text
/// - Icons
var quarterlyContent: ColorType { get }
/// - separating lines and other UI components
var quinaryContent: ColorType { get }
/// - System-based areas and backgrounds
var system: ColorType { get }
/// Separating line
var separator: ColorType { get }
/// Cards, tiles
var tile: ColorType { get }
/// Top navigation background on iOS
var navigation: ColorType { get }
/// Background UI color
var background: ColorType { get }
/// Global color: The EMS brand's purple colour.
var ems: ColorType { get }
/// - Names in chat timeline
/// - Avatars default states that include first name letter
var namesAndAvatars: [ColorType] { get }
}
-69
View File
@@ -1,69 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import SwiftUI
/**
Struct for holding colors for use in SwiftUI.
*/
public struct ColorSwiftUI: Colors {
public let accent: Color
public let alert: Color
public let primaryContent: Color
public let secondaryContent: Color
public let tertiaryContent: Color
public let quarterlyContent: Color
public let quinaryContent: Color
public let separator: Color
public var system: Color
public let tile: Color
public let navigation: Color
public let background: Color
public var ems: Color
public let namesAndAvatars: [Color]
init(values: ColorValues) {
accent = Color(values.accent)
alert = Color(values.alert)
primaryContent = Color(values.primaryContent)
secondaryContent = Color(values.secondaryContent)
tertiaryContent = Color(values.tertiaryContent)
quarterlyContent = Color(values.quarterlyContent)
quinaryContent = Color(values.quinaryContent)
separator = Color(values.separator)
system = Color(values.system)
tile = Color(values.tile)
navigation = Color(values.navigation)
background = Color(values.background)
ems = Color(values.ems)
namesAndAvatars = values.namesAndAvatars.map({ Color($0) })
}
}
-67
View File
@@ -1,67 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
/**
ObjC class for holding colors for use in UIKit.
*/
@objcMembers public class ColorsUIKit: NSObject {
public let accent: UIColor
public let alert: UIColor
public let primaryContent: UIColor
public let secondaryContent: UIColor
public let tertiaryContent: UIColor
public let quarterlyContent: UIColor
public let quinaryContent: UIColor
public let separator: UIColor
public let system: UIColor
public let tile: UIColor
public let navigation: UIColor
public let background: UIColor
public let namesAndAvatars: [UIColor]
init(values: ColorValues) {
accent = values.accent
alert = values.alert
primaryContent = values.primaryContent
secondaryContent = values.secondaryContent
tertiaryContent = values.tertiaryContent
quarterlyContent = values.quarterlyContent
quinaryContent = values.quinaryContent
separator = values.separator
system = values.system
tile = values.tile
navigation = values.navigation
background = values.background
namesAndAvatars = values.namesAndAvatars
}
}
-85
View File
@@ -1,85 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
/// Describe fonts used in the application.
/// Font names are based on Element typograhy https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0 which is based on Apple font text styles (UIFont.TextStyle): https://developer.apple.com/documentation/uikit/uifonttextstyle
/// Create a custom TextStyle enum (like DesignKit.Fonts.TextStyle) is also a possiblity
public protocol Fonts {
associatedtype FontType
/// The font for large titles.
var largeTitle: FontType { get }
/// `largeTitle` with a Bold weight.
var largeTitleB: FontType { get }
/// The font for first-level hierarchical headings.
var title1: FontType { get }
/// `title1` with a Bold weight.
var title1B: FontType { get }
/// The font for second-level hierarchical headings.
var title2: FontType { get }
/// `title2` with a Bold weight.
var title2B: FontType { get }
/// The font for third-level hierarchical headings.
var title3: FontType { get }
/// `title3` with a Semi Bold weight.
var title3SB: FontType { get }
/// The font for headings.
var headline: FontType { get }
/// The font for subheadings.
var subheadline: FontType { get }
/// The font for body text.
var body: FontType { get }
/// `body` with a Semi Bold weight.
var bodySB: FontType { get }
/// The font for callouts.
var callout: FontType { get }
/// `callout` with a Semi Bold weight.
var calloutSB: FontType { get }
/// The font for footnotes.
var footnote: FontType { get }
/// `footnote` with a Semi Bold weight.
var footnoteSB: FontType { get }
/// The font for standard captions.
var caption1: FontType { get }
/// `caption1` with a Semi Bold weight.
var caption1SB: FontType { get }
/// The font for alternate captions.
var caption2: FontType { get }
/// `caption2` with a Semi Bold weight.
var caption2SB: FontType { get }
}
-91
View File
@@ -1,91 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import SwiftUI
/**
Struct for holding fonts for use in SwiftUI.
*/
public struct FontSwiftUI: Fonts {
public let uiFonts: FontsUIKit
public var largeTitle: Font
public var largeTitleB: Font
public var title1: Font
public var title1B: Font
public var title2: Font
public var title2B: Font
public var title3: Font
public var title3SB: Font
public var headline: Font
public var subheadline: Font
public var body: Font
public var bodySB: Font
public var callout: Font
public var calloutSB: Font
public var footnote: Font
public var footnoteSB: Font
public var caption1: Font
public var caption1SB: Font
public var caption2: Font
public var caption2SB: Font
public init(values: ElementFonts) {
self.uiFonts = FontsUIKit(values: values)
self.largeTitle = values.largeTitle.font
self.largeTitleB = values.largeTitleB.font
self.title1 = values.title1.font
self.title1B = values.title1B.font
self.title2 = values.title2.font
self.title2B = values.title2B.font
self.title3 = values.title3.font
self.title3SB = values.title3SB.font
self.headline = values.headline.font
self.subheadline = values.subheadline.font
self.body = values.body.font
self.bodySB = values.bodySB.font
self.callout = values.callout.font
self.calloutSB = values.calloutSB.font
self.footnote = values.footnote.font
self.footnoteSB = values.footnoteSB.font
self.caption1 = values.caption1.font
self.caption1SB = values.caption1SB.font
self.caption2 = values.caption2.font
self.caption2SB = values.caption2SB.font
}
}
-87
View File
@@ -1,87 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
/**
ObjC class for holding fonts for use in UIKit.
*/
@objcMembers public class FontsUIKit: NSObject, Fonts {
public var largeTitle: UIFont
public var largeTitleB: UIFont
public var title1: UIFont
public var title1B: UIFont
public var title2: UIFont
public var title2B: UIFont
public var title3: UIFont
public var title3SB: UIFont
public var headline: UIFont
public var subheadline: UIFont
public var body: UIFont
public var bodySB: UIFont
public var callout: UIFont
public var calloutSB: UIFont
public var footnote: UIFont
public var footnoteSB: UIFont
public var caption1: UIFont
public var caption1SB: UIFont
public var caption2: UIFont
public var caption2SB: UIFont
public init(values: ElementFonts) {
self.largeTitle = values.largeTitle.uiFont
self.largeTitleB = values.largeTitleB.uiFont
self.title1 = values.title1.uiFont
self.title1B = values.title1B.uiFont
self.title2 = values.title2.uiFont
self.title2B = values.title2B.uiFont
self.title3 = values.title3.uiFont
self.title3SB = values.title3SB.uiFont
self.headline = values.headline.uiFont
self.subheadline = values.subheadline.uiFont
self.body = values.body.uiFont
self.bodySB = values.bodySB.uiFont
self.callout = values.callout.uiFont
self.calloutSB = values.calloutSB.uiFont
self.footnote = values.footnote.uiFont
self.footnoteSB = values.footnoteSB.uiFont
self.caption1 = values.caption1.uiFont
self.caption1SB = values.caption1SB.uiFont
self.caption2 = values.caption2.uiFont
self.caption2SB = values.caption2SB.uiFont
}
}
@@ -1,51 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
import SwiftUI
/// Dark theme colors.
public class DarkColors {
private static let values = ColorValues(
accent: UIColor(rgb:0x0DBD8B),
alert: UIColor(rgb:0xFF4B55),
primaryContent: UIColor(rgb:0xFFFFFF),
secondaryContent: UIColor(rgb:0xA9B2BC),
tertiaryContent: UIColor(rgb:0x8E99A4),
quarterlyContent: UIColor(rgb:0x6F7882),
quinaryContent: UIColor(rgb:0x394049),
separator: UIColor(rgb:0x21262C),
system: UIColor(rgb:0x21262C),
tile: UIColor(rgb:0x394049),
navigation: UIColor(rgb:0x21262C),
background: UIColor(rgb:0x15191E),
ems: UIColor(rgb: 0x7E69FF),
namesAndAvatars: [
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 static var uiKit = ColorsUIKit(values: values)
public static var swiftUI = ColorSwiftUI(values: values)
}
@@ -1,57 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import UIKit
import SwiftUI
/// Light theme colors.
public class LightColors {
private static let values = ColorValues(
accent: UIColor(rgb:0x0DBD8B),
alert: UIColor(rgb:0xFF4B55),
primaryContent: UIColor(rgb:0x17191C),
secondaryContent: UIColor(rgb:0x737D8C),
tertiaryContent: UIColor(rgb:0x8D97A5),
quarterlyContent: UIColor(rgb:0xC1C6CD),
quinaryContent: UIColor(rgb:0xE3E8F0),
separator: UIColor(rgb:0xE3E8F0),
system: UIColor(rgb:0xF4F6FA),
tile: UIColor(rgb:0xF3F8FD),
navigation: UIColor(rgb:0xF4F6FA),
background: UIColor(rgb:0xFFFFFF),
ems: UIColor(rgb: 0x7E69FF),
namesAndAvatars: [
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 static var uiKit = ColorsUIKit(values: values)
public static var swiftUI = ColorSwiftUI(values: values)
}
-150
View File
@@ -1,150 +0,0 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import SwiftUI
/// Fonts at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0
@objcMembers
public class ElementFonts {
// MARK: - Types
/// A wrapper to provide both a `UIFont` and a SwiftUI `Font` in the same type.
/// The need for this comes from `Font` not adapting for dynamic type until the app
/// is restarted (or working at all in Xcode Previews) when initialised from a `UIFont`
/// (even if that font was created with the appropriate metrics).
public struct SharedFont {
public let uiFont: UIFont
public let font: Font
}
// MARK: - Setup
public init() {
}
// MARK: - Private
/// Returns an instance of the font associated with the text style and scaled appropriately for the content size category defined in the trait collection.
/// Keep this method private method at the moment and create a DesignKit.Fonts.TextStyle if needed.
fileprivate func font(forTextStyle textStyle: UIFont.TextStyle, compatibleWith traitCollection: UITraitCollection? = nil) -> UIFont {
return UIFont.preferredFont(forTextStyle: textStyle, compatibleWith: traitCollection)
}
}
// MARK: - Fonts protocol
extension ElementFonts: Fonts {
public var largeTitle: SharedFont {
let uiFont = self.font(forTextStyle: .largeTitle)
return SharedFont(uiFont: uiFont, font: .largeTitle)
}
public var largeTitleB: SharedFont {
let uiFont = self.largeTitle.uiFont.vc_bold
return SharedFont(uiFont: uiFont, font: .largeTitle.bold())
}
public var title1: SharedFont {
let uiFont = self.font(forTextStyle: .title1)
return SharedFont(uiFont: uiFont, font: .title)
}
public var title1B: SharedFont {
let uiFont = self.title1.uiFont.vc_bold
return SharedFont(uiFont: uiFont, font: .title.bold())
}
public var title2: SharedFont {
let uiFont = self.font(forTextStyle: .title2)
return SharedFont(uiFont: uiFont, font: .title2)
}
public var title2B: SharedFont {
let uiFont = self.title2.uiFont.vc_bold
return SharedFont(uiFont: uiFont, font: .title2.bold())
}
public var title3: SharedFont {
let uiFont = self.font(forTextStyle: .title3)
return SharedFont(uiFont: uiFont, font: .title3)
}
public var title3SB: SharedFont {
let uiFont = self.title3.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .title3.weight(.semibold))
}
public var headline: SharedFont {
let uiFont = self.font(forTextStyle: .headline)
return SharedFont(uiFont: uiFont, font: .headline)
}
public var subheadline: SharedFont {
let uiFont = self.font(forTextStyle: .subheadline)
return SharedFont(uiFont: uiFont, font: .subheadline)
}
public var body: SharedFont {
let uiFont = self.font(forTextStyle: .body)
return SharedFont(uiFont: uiFont, font: .body)
}
public var bodySB: SharedFont {
let uiFont = self.body.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .body.weight(.semibold))
}
public var callout: SharedFont {
let uiFont = self.font(forTextStyle: .callout)
return SharedFont(uiFont: uiFont, font: .callout)
}
public var calloutSB: SharedFont {
let uiFont = self.callout.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .callout.weight(.semibold))
}
public var footnote: SharedFont {
let uiFont = self.font(forTextStyle: .footnote)
return SharedFont(uiFont: uiFont, font: .footnote)
}
public var footnoteSB: SharedFont {
let uiFont = self.footnote.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .footnote.weight(.semibold))
}
public var caption1: SharedFont {
let uiFont = self.font(forTextStyle: .caption1)
return SharedFont(uiFont: uiFont, font: .caption)
}
public var caption1SB: SharedFont {
let uiFont = self.caption1.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .caption.weight(.semibold))
}
public var caption2: SharedFont {
let uiFont = self.font(forTextStyle: .caption2)
return SharedFont(uiFont: uiFont, font: .caption2)
}
public var caption2SB: SharedFont {
let uiFont = self.caption2.uiFont.vc_semiBold
return SharedFont(uiFont: uiFont, font: .caption2.weight(.semibold))
}
}
-33
View File
@@ -1,33 +0,0 @@
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
+2 -1
View File
@@ -16,7 +16,7 @@ use_frameworks!
# - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI
#
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
$matrixSDKVersion = '= 0.23.10'
$matrixSDKVersion = '= 0.23.11'
# $matrixSDKVersion = :local
# $matrixSDKVersion = { :branch => 'develop'}
# $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } }
@@ -72,6 +72,7 @@ abstract_target 'RiotPods' do
# PostHog for analytics
pod 'PostHog', '~> 1.4.4'
pod 'Sentry', '~> 7.15.0'
pod 'AnalyticsEvents', :git => 'https://github.com/matrix-org/matrix-analytics-events.git', :branch => 'release/swift', :inhibit_warnings => false
# pod 'AnalyticsEvents', :path => '../matrix-analytics-events/AnalyticsEvents.podspec'
+8 -2
View File
@@ -87,6 +87,9 @@ PODS:
- Reusable/View (= 4.1.2)
- Reusable/Storyboard (4.1.2)
- Reusable/View (4.1.2)
- Sentry (7.15.0):
- Sentry/Core (= 7.15.0)
- Sentry/Core (7.15.0)
- SideMenu (6.5.0)
- SwiftBase32 (0.9.0)
- SwiftGen (6.5.1)
@@ -126,6 +129,7 @@ DEPENDENCIES:
- PostHog (~> 1.4.4)
- ReadMoreTextView (~> 3.0.1)
- Reusable (~> 4.1)
- Sentry (~> 7.15.0)
- SideMenu (~> 6.5)
- SwiftBase32 (~> 0.9.0)
- SwiftGen (~> 6.3)
@@ -169,6 +173,7 @@ SPEC REPOS:
- ReadMoreTextView
- Realm
- Reusable
- Sentry
- SideMenu
- SwiftBase32
- SwiftGen
@@ -223,6 +228,7 @@ SPEC CHECKSUMS:
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2
Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136
Sentry: 63ca44f5e0c8cea0ee5a07686b02e56104f41ef7
SideMenu: f583187d21c5b1dd04c72002be544b555a2627a2
SwiftBase32: 9399c25a80666dc66b51e10076bf591e3bbb8f17
SwiftGen: a6d22010845f08fe18fbdf3a07a8e380fd22e0ea
@@ -235,6 +241,6 @@ SPEC CHECKSUMS:
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
PODFILE CHECKSUM: fdddeaf3f403004b1cc6200add1b6b9e00d40906
PODFILE CHECKSUM: b3c7c064fc2b74dc937762364faab403fc3fd041
COCOAPODS: 1.11.2
COCOAPODS: 1.11.3
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.3">
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
@@ -0,0 +1,50 @@
{
"pins" : [
{
"identity" : "element-design-tokens",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vector-im/element-design-tokens.git",
"state" : {
"revision" : "02ba42d9ec02f90370a6cfc35a68d7312696636c",
"version" : "0.0.2"
}
},
{
"identity" : "element-x-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/vector-im/element-x-ios",
"state" : {
"revision" : "0a199ee61126feb8c8a462200cb4749d6eb3ba77",
"version" : "1.0.1-202207011447"
}
},
{
"identity" : "maplibre-gl-native-distribution",
"kind" : "remoteSourceControl",
"location" : "https://github.com/maplibre/maplibre-gl-native-distribution",
"state" : {
"revision" : "d761956e81e74d8bdbfba31e0ec3a75616190658",
"version" : "5.12.2"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections",
"state" : {
"revision" : "48254824bb4248676bf7ce56014ff57b142b77eb",
"version" : "1.0.2"
}
},
{
"identity" : "swiftui-introspect",
"kind" : "remoteSourceControl",
"location" : "https://github.com/siteline/SwiftUI-Introspect.git",
"state" : {
"revision" : "f2616860a41f9d9932da412a8978fec79c06fe24",
"version" : "0.1.4"
}
}
],
"version" : 2
}
-38
View File
@@ -1,38 +0,0 @@
<?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>pinRoomsWithMissedNotif</key>
<true/>
<key>pinRoomsWithUnread</key>
<true/>
<key>matrixApps</key>
<true/>
<key>showAllEventsInRoomHistory</key>
<false/>
<key>showRedactionsInRoomHistory</key>
<true/>
<key>showUnsupportedEventsInRoomHistory</key>
<false/>
<key>sortRoomMembersUsingLastSeenTime</key>
<true/>
<key>showLeftMembersInRoomMemberList</key>
<false/>
<key>syncLocalContacts</key>
<false/>
<key>enableRageShake</key>
<true/>
<key>maxAllowedMediaCacheSize</key>
<integer>1073741824</integer>
<key>presenceColorForOnlineUser</key>
<integer>3401011</integer>
<key>presenceColorForUnavailableUser</key>
<integer>15066368</integer>
<key>presenceColorForOfflineUser</key>
<integer>15020851</integer>
<key>enableBotCreation</key>
<false/>
<key>enableRingingForGroupCalls</key>
<false/>
</dict>
</plist>
@@ -88,6 +88,3 @@
"password_validation_error_contain_uppercase_letter" = "Contain an upper-case letter.";
"password_validation_error_contain_number" = "Contain a number.";
"password_validation_error_contain_symbol" = "Contain a symbol.";
// MARK: Room Info
"room_info_back_button_title" = "Room Info";
+10 -2
View File
@@ -1060,6 +1060,8 @@ Tap the + to start adding people.";
"today" = "Today";
"yesterday" = "Yesterday";
"network_offline_prompt" = "The Internet connection appears to be offline.";
"network_offline_title" = "You're offline";
"network_offline_message" = "You're offline, check your connection.";
"homeserver_connection_lost" = "Could not connect to the homeserver.";
"public_room_section_title" = "Public Rooms (at %@):";
"bug_report_prompt" = "The application has crashed last time. Would you like to submit a crash report?";
@@ -1822,6 +1824,7 @@ Tap the + to start adding people.";
"room_info_list_one_member" = "1 member";
"room_info_list_several_members" = "%@ members";
"room_info_list_section_other" = "Other";
"room_info_back_button_title" = "Room Info";
// MARK: - Dial Pad
"dialpad_title" = "Dial pad";
@@ -2164,6 +2167,7 @@ Tap the + to start adding people.";
To enable access, tap Settings> Location and select Always";
"location_sharing_allow_background_location_validate_action" = "Settings";
"location_sharing_allow_background_location_cancel_action" = "Not now";
"location_sharing_map_credits_title" = "© Copyright";
// MARK: Live location sharing
@@ -2180,7 +2184,7 @@ To enable access, tap Settings> Location and select Always";
"location_sharing_live_list_item_last_update" = "Updated %@ ago";
"location_sharing_live_list_item_last_update_invalid" = "Unknown last update";
"location_sharing_live_list_item_current_user_display_name" = "You";
"location_sharing_live_list_item_stop_sharing_action" = "Stop sharing";
"location_sharing_live_list_item_stop_sharing_action" = "Stop";
"location_sharing_live_timer_incoming" = "Live until %@";
"location_sharing_live_loading" = "Loading Live location...";
"location_sharing_live_error" = "Live location error";
@@ -2192,6 +2196,10 @@ To enable access, tap Settings> Location and select Always";
"location_sharing_live_stop_sharing_error" = "Fail to stop sharing location";
"location_sharing_live_stop_sharing_progress" = "Stop location sharing";
"location_sharing_live_lab_promotion_title" = "Live location sharing";
"location_sharing_live_lab_promotion_text" = "Please note: this is a labs feature using a temporary implementation that allows the history of your shared location to be permanently visible to other people in the room.";
"location_sharing_live_lab_promotion_activation" = "Enable live location sharing";
// MARK: - MatrixKit
@@ -2340,7 +2348,7 @@ To enable access, tap Settings> Location and select Always";
// Settings
"settings" = "Settings";
"settings_enable_inapp_notifications" = "Enable In-App notifications";
"settings_enable_inapp_notifications" = "Enable in-app notifications";
"settings_enable_push_notifications" = "Enable push notifications";
"settings_enter_validation_token_for" = "Enter validation token for %@:";
+17
View File
@@ -2424,3 +2424,20 @@
"location_sharing_allow_background_location_validate_action" = "Ajustes";
"location_sharing_allow_background_location_title" = "Permitir acceso";
"settings_ui_show_redactions_in_room_history" = "Mostrar un indicador donde se haya eliminado un mensaje";
"settings_timeline" = "LÍNEA DE TIEMPO";
"room_accessibility_record_voice_message_hint" = "Toca dos veces y mantén para grabar.";
// MARK: Reactions
"room_event_action_reaction_more" = "%@ más";
"leave_space_selection_no_rooms" = "No seleccionar ninguna sala";
"leave_space_selection_all_rooms" = "Seleccionar todas las salas";
"leave_space_selection_title" = "SELECCIONAR SALAS";
"leave_space_and_more_rooms" = "Salir del espacio y %@ salas";
"leave_space_and_one_room" = "Salir del espacio y 1 sala";
// Mark: Leave space
"leave_space_action" = "Salir del espacio";
"home_context_menu_mark_as_read" = "Marcar como leído";
"room_accessibility_record_voice_message" = "Grabar mensaje de voz";
+18
View File
@@ -2272,3 +2272,21 @@
"location_sharing_allow_background_location_title" = "Luba ligipääs asukohale";
"settings_labs_enable_live_location_sharing" = "Praeguse asukoha jagamine reaalajas (funktsionaalsus on arendamisel ning ajutiselt on asukohad jututoa ajaloos näha)";
"settings_ui_show_redactions_in_room_history" = "Näita kustutatud sõnumite asemel kohatäidet";
// MARK: Reactions
"room_event_action_reaction_more" = "veel %@";
"leave_space_selection_no_rooms" = "Ära vali ühtegi jututuba";
"leave_space_selection_all_rooms" = "Vali kõik jututoad";
"leave_space_selection_title" = "VALI JUTUTUBE";
"leave_space_and_more_rooms" = "Lahku kogukonnakeskusest ja %@'st jututoast";
"leave_space_and_one_room" = "Lahku kogukonnakeskusest ja 1'st jututoast";
// Mark: Leave space
"leave_space_action" = "Lahku kogukonnakeskusest";
"spaces_feature_not_available" = "See funktsionaalsus pole siin rakenduses saadaval. Seni saad vastavat võimalust kasutada %@'i versioonis tavaarvutis.";
"home_context_menu_mark_as_read" = "Märgi loetuks";
"settings_timeline" = "AJAJOON";
"room_accessibility_record_voice_message_hint" = "Salvestamiseks klõpsi kaks korda ja hoia.";
"room_accessibility_record_voice_message" = "Salvesta häälsõnum";
+19
View File
@@ -2530,3 +2530,22 @@
"location_sharing_allow_background_location_message" = "Jika Anda ingin membagikan lokasi langsung Anda, Element membutuhkan akses lokasi ketika aplikasi berada di latar belakang.Untuk memberikan akses, ketuk Pengaturan> Lokasi dan pilih Selalu";
"location_sharing_allow_background_location_title" = "Perbolehkan akses";
"settings_labs_enable_live_location_sharing" = "Pembagian lokasi langsung — bagikan lokasi saat ini (dalam pengembangan aktif, dan sementara, lokasi tetap di riwayat ruangan)";
// MARK: Reactions
"room_event_action_reaction_more" = "%@ lainnya";
"leave_space_selection_no_rooms" = "Pilih tidak ada ruangan";
"leave_space_selection_all_rooms" = "Pilih semua ruangan";
"leave_space_selection_title" = "PILIH RUANGAN";
"leave_space_and_more_rooms" = "Tinggalkan space dan %@ ruangan";
"leave_space_and_one_room" = "Tinggalkan space dan 1 ruangan";
// Mark: Leave space
"leave_space_action" = "Tinggalkan space";
"spaces_feature_not_available" = "Fitur ini tidak tersedia di sini. Untuk sekarang, Anda dapat melakukannya dengan %@ di komputer Anda.";
"home_context_menu_mark_as_read" = "Tandai sebagai dibaca";
"settings_ui_show_redactions_in_room_history" = "Tampilkan tampungan untuk pesan yang dihapus";
"settings_timeline" = "LINIMASA";
"room_accessibility_record_voice_message_hint" = "Ketuk dua kali dan tekan untuk merekam.";
"room_accessibility_record_voice_message" = "Rekam Pesan Suara";
+18
View File
@@ -2304,3 +2304,21 @@
"location_sharing_allow_background_location_title" = "Permetti accesso";
"settings_labs_enable_live_location_sharing" = "Condivisione posizione in tempo reale - condividi la posizione attuale (in sviluppo attivo e, per ora, le posizioni restano nella cronologia della stanza)";
"settings_ui_show_redactions_in_room_history" = "Mostra un segnaposto per i messaggi rimossi";
// MARK: Reactions
"room_event_action_reaction_more" = "Altre %@";
"leave_space_selection_no_rooms" = "Non selezionare alcuna stanza";
"leave_space_selection_all_rooms" = "Seleziona tutte le stanze";
"leave_space_selection_title" = "SELEZIONA STANZE";
"leave_space_and_more_rooms" = "Esci dallo spazio e da %@ stanze";
"leave_space_and_one_room" = "Esci dallo spazio e da 1 stanza";
// Mark: Leave space
"leave_space_action" = "Esci dallo spazio";
"spaces_feature_not_available" = "Questa funzionalità non è disponibile qui. Per ora puoi farlo con %@ sul tuo computer.";
"home_context_menu_mark_as_read" = "Segna come letto";
"settings_timeline" = "LINEA TEMPORALE";
"room_accessibility_record_voice_message_hint" = "Doppio tocco e tieni premuto per registrare.";
"room_accessibility_record_voice_message" = "Registra messaggio vocale";
+16
View File
@@ -1640,3 +1640,19 @@
"existing" = "既存";
"new_word" = "新規";
"stop" = "停止";
"spaces_creation_post_process_creating_space_task" = "%@を作成中";
"side_menu_coach_message" = "右にスワイプまたはタップで全てのルームが表示されます";
"spaces_creation_post_process_creating_space" = "スペースを作成中";
"spaces_creation_add_rooms_message" = "このスペースはあなた専用のため、他の人に通知されることはありません。この設定は後から変更できます。";
"spaces_creation_add_rooms_title" = "どれを追加しますか?";
"spaces_creation_sharing_type_me_and_teammates_detail" = "あなたとチームメイトの非公開のスペース";
"spaces_creation_sharing_type_me_and_teammates_title" = "自分とチームメイト";
"spaces_creation_sharing_type_just_me_detail" = "ルームを整理するための非公開のスペース";
"spaces_creation_sharing_type_just_me_title" = "自分専用";
"spaces_creation_sharing_type_message" = "参加者を選択してください%@。この設定は後から変更できます。";
"spaces_creation_settings_message" = "詳細を入力してください。この設定は後から変更できます。";
"spaces_creation_address_default_message" = "スペースは以下のように表記されます\n%@";
"space_settings_current_address_message" = "スペースは以下のように表記されます\n%@";
"space_topic" = "説明文";
"spaces_creation_cancel_message" = "進捗状況は失われます。";
"spaces_creation_cancel_title" = "スペースの作成を停止しますか?";
+18
View File
@@ -2491,3 +2491,21 @@
/* The %@ placeholder will be replaced with the integration manager's URL. */
"settings_integrations_allow_description" = "Gebruik een integratiebeheerder (%@) om bots, bruggen, widgets en stickerpakketten te beheren.\n\nIntegratiebeheerders ontvangen configuratiedata en kunnen widgets aanpassen, kameruitnodigingen versturen en bestuursniveaus instellen namens u.";
"settings_ui_show_redactions_in_room_history" = "Toon een aanduiding voor verwijderde berichten";
// MARK: Reactions
"room_event_action_reaction_more" = "%@ meer";
"leave_space_selection_no_rooms" = "Selecteer geen kamers";
"leave_space_selection_all_rooms" = "Selecteer alle kamers";
"leave_space_selection_title" = "KAMERS KIEZEN";
"leave_space_and_more_rooms" = "Verlaat space en %@ kamers";
"leave_space_and_one_room" = "Verlaat space en 1 kamer";
// Mark: Leave space
"leave_space_action" = "Verlaat space";
"spaces_feature_not_available" = "Deze functie is hier niet beschikbaar. Voor nu kunt u dit doen met %@ op uw computer.";
"home_context_menu_mark_as_read" = "Markeer als gelezen";
"settings_timeline" = "TIJDLIJN";
"room_accessibility_record_voice_message_hint" = "Dubbeltik en houd vast om op te nemen.";
"room_accessibility_record_voice_message" = "Spraakbericht opnemen";
+18
View File
@@ -2301,3 +2301,21 @@
"location_sharing_allow_background_location_title" = "Permitir acesso";
"settings_labs_enable_live_location_sharing" = "Compartilhamento de localização ao vivo - compartilhar localização atual (desenvolvimento ativo, e temporariamente, localizações persistem em histórico de sala)";
"settings_ui_show_redactions_in_room_history" = "Mostrar um placeholder para mensagens removidas";
// MARK: Reactions
"room_event_action_reaction_more" = "Mais %@";
"leave_space_selection_no_rooms" = "Selecionar nenhuma sala";
"leave_space_selection_all_rooms" = "Selecionar todas as salas";
"leave_space_selection_title" = "SELECIONAR SALAS";
"leave_space_and_more_rooms" = "Sair de espaço e %@ salas";
"leave_space_and_one_room" = "Sair de espaço e 1 sala";
// Mark: Leave space
"leave_space_action" = "Sair de espaço";
"spaces_feature_not_available" = "Esta funcionalidade não está disponível aqui. Por enquanto, você pode fazer isto com %@ em seu computador.";
"home_context_menu_mark_as_read" = "Marcar como lida";
"settings_timeline" = "TIMELINE";
"room_accessibility_record_voice_message_hint" = "Toque duplo e segure para gravar.";
"room_accessibility_record_voice_message" = "Gravar Mensagem de Voz";
+1
View File
@@ -6,3 +6,4 @@
"NSCalendarsUsageDescription" = "Просматривайте запланированные встречи в приложении.";
"NSFaceIDUsageDescription" = "Face ID используется для доступа к вашему приложению.";
"NSLocationWhenInUseUsageDescription" = "Когда вы делитесь с людьми своим местоположением, Element необходим доступ, чтобы показать им карту.";
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Когда вы сообщаете людям свое местоположение, Element будет необходим доступ, чтобы показать им карту.";
+18
View File
@@ -2527,3 +2527,21 @@
"location_sharing_allow_background_location_title" = "Povoliť prístup";
"settings_labs_enable_live_location_sharing" = "Zdieľanie polohy v reálnom čase - zdieľanie aktuálnej polohy (v aktívnom vývoji a polohy dočasne pretrvávajú v histórii miestnosti)";
"settings_ui_show_redactions_in_room_history" = "Zobrazovať náhrady za odstránené správy";
// MARK: Reactions
"room_event_action_reaction_more" = "%@ viac";
"leave_space_selection_no_rooms" = "Nevybrať žiadne miestnosti";
"leave_space_selection_all_rooms" = "Vybrať všetky miestnosti";
"leave_space_selection_title" = "VYBRAŤ MIESTNOSTI";
"leave_space_and_more_rooms" = "Opustiť priestor a %@ miestností";
"leave_space_and_one_room" = "Opustiť priestor a 1 miestnosť";
// Mark: Leave space
"leave_space_action" = "Opustiť priestor";
"spaces_feature_not_available" = "Táto funkcia tu nie je k dispozícii. Zatiaľ to môžete urobiť pomocou %@ na vašom počítači.";
"home_context_menu_mark_as_read" = "Označiť ako prečítané";
"settings_timeline" = "ČASOVÁ OS";
"room_accessibility_record_voice_message_hint" = "Ak chcete nahrávať, dvakrát ťuknite a podržte.";
"room_accessibility_record_voice_message" = "Nahrať hlasovú správu";
+23 -4
View File
@@ -294,7 +294,7 @@
"settings_add_email_address" = "Lägg till e-postadress";
"settings_phone_number" = "Telefon";
"settings_add_phone_number" = "Lägg till telefonnummer";
"settings_change_password" = "Byt Matrixkontolösenord";
"settings_change_password" = "Byt lösenord";
"settings_night_mode" = "Nattläge";
"settings_fail_to_update_profile" = "Misslyckades att uppdatera profil";
"settings_three_pids_management_information_part1" = "Hantera vilka e-postadresser eller telefonnummer som du kan använda för att logga in eller återfå ditt konto här. Kontrollera vilka som kan hitta dig i ";
@@ -325,9 +325,9 @@
"settings_privacy_policy" = "Integritetspolicy";
"settings_send_crash_report" = "Skicka anonyma krasch- och användningsdata";
"settings_enable_rageshake" = "Raseriskaka för att rapportera bugg";
"settings_old_password" = "gammalt lösenord";
"settings_new_password" = "nytt lösenord";
"settings_confirm_password" = "bekräfta lösenord";
"settings_old_password" = "Gammalt lösenord";
"settings_new_password" = "Nytt lösenord";
"settings_confirm_password" = "Bekräfta lösenord";
"settings_fail_to_update_password" = "Misslyckades att uppdatera Matrixkontolösenord";
"settings_password_updated" = "Ditt Matrixkontolösenord har uppdaterats";
"settings_add_3pid_password_title_email" = "Lägg till e-postadress";
@@ -2267,3 +2267,22 @@
"location_sharing_allow_background_location_message" = "Om du vill dela din realtidsplats så behöver Element platsåtkomst när appen är i bakgrunden. För att aktivera åtkomst, gå till Inställningar > Plats och välj Alltid";
"location_sharing_allow_background_location_title" = "Tillåt åtkomst";
"settings_labs_enable_live_location_sharing" = "Platsdelning i realtid - dela nuvarande plats (aktiv utveckling, och för tillfället ligger platser kvar i rumshistoriken)";
// MARK: Reactions
"room_event_action_reaction_more" = "%@ till";
"leave_space_selection_no_rooms" = "Välj inga rum";
"leave_space_selection_all_rooms" = "Välj alla rum";
"leave_space_selection_title" = "VÄLJ RUM";
"leave_space_and_more_rooms" = "Lämna utrymme och %@ rum";
"leave_space_and_one_room" = "Lämna utrymme och 1 rum";
// Mark: Leave space
"leave_space_action" = "Lämna utrymme";
"spaces_feature_not_available" = "Den här funktionen är inte tillgänglig här. För tillfället kan du göra det med %@ på din dator.";
"home_context_menu_mark_as_read" = "Markera som läst";
"settings_ui_show_redactions_in_room_history" = "Visa en platshållare för borttagna meddelanden";
"settings_timeline" = "TIDSLINJE";
"room_accessibility_record_voice_message_hint" = "Dubbeltryck och håll för att spela in.";
"room_accessibility_record_voice_message" = "Spela in röstmeddelande";
+18
View File
@@ -2529,3 +2529,21 @@
"location_sharing_allow_background_location_title" = "Дозволити доступ";
"settings_labs_enable_live_location_sharing" = "Поширення місцеперебування наживо - діліться поточним розташуванням (в активній розробці, місця тимчасово зберігаються в історії кімнат)";
"settings_ui_show_redactions_in_room_history" = "Показувати заповнювач для вилучених повідомлень";
// MARK: Reactions
"room_event_action_reaction_more" = "Ще %@";
"leave_space_selection_no_rooms" = "Не вибирати кімнати";
"leave_space_selection_all_rooms" = "Вибрати всі кімнати";
"leave_space_selection_title" = "ВИБРАТИ КІМНАТИ";
"leave_space_and_more_rooms" = "Вийти з простору та %@ кімнат";
"leave_space_and_one_room" = "Вийти з простору та однієї кімнати";
// Mark: Leave space
"leave_space_action" = "Вийти з простору";
"spaces_feature_not_available" = "Ця функція тут недоступна. Наразі ви можете це зробити з %@ на своєму комп’ютері.";
"home_context_menu_mark_as_read" = "Позначити прочитаним";
"settings_timeline" = "СТРІЧКА";
"room_accessibility_record_voice_message_hint" = "Двічі торкніться й утримуйте для запису.";
"room_accessibility_record_voice_message" = "Записати голосове повідомлення";
-73
View File
@@ -1,73 +0,0 @@
// swiftlint:disable all
// Generated using SwiftGen https://github.com/SwiftGen/SwiftGen
import Foundation
// swiftlint:disable superfluous_disable_command
// swiftlint:disable file_length
// MARK: - Plist Files
// swiftlint:disable identifier_name line_length type_body_length
internal enum RiotDefaults {
private static let _document = PlistDocument(path: "Riot-Defaults.plist")
internal static let enableBotCreation: Bool = _document["enableBotCreation"]
internal static let enableRageShake: Bool = _document["enableRageShake"]
internal static let enableRingingForGroupCalls: Bool = _document["enableRingingForGroupCalls"]
internal static let matrixApps: Bool = _document["matrixApps"]
internal static let maxAllowedMediaCacheSize: Int = _document["maxAllowedMediaCacheSize"]
internal static let pinRoomsWithMissedNotif: Bool = _document["pinRoomsWithMissedNotif"]
internal static let pinRoomsWithUnread: Bool = _document["pinRoomsWithUnread"]
internal static let presenceColorForOfflineUser: Int = _document["presenceColorForOfflineUser"]
internal static let presenceColorForOnlineUser: Int = _document["presenceColorForOnlineUser"]
internal static let presenceColorForUnavailableUser: Int = _document["presenceColorForUnavailableUser"]
internal static let showAllEventsInRoomHistory: Bool = _document["showAllEventsInRoomHistory"]
internal static let showLeftMembersInRoomMemberList: Bool = _document["showLeftMembersInRoomMemberList"]
internal static let showRedactionsInRoomHistory: Bool = _document["showRedactionsInRoomHistory"]
internal static let showUnsupportedEventsInRoomHistory: Bool = _document["showUnsupportedEventsInRoomHistory"]
internal static let sortRoomMembersUsingLastSeenTime: Bool = _document["sortRoomMembersUsingLastSeenTime"]
internal static let syncLocalContacts: Bool = _document["syncLocalContacts"]
}
// swiftlint:enable identifier_name line_length type_body_length
// MARK: - Implementation Details
private func arrayFromPlist<T>(at path: String) -> [T] {
guard let url = BundleToken.bundle.url(forResource: path, withExtension: nil),
let data = NSArray(contentsOf: url) as? [T] else {
fatalError("Unable to load PLIST at path: \(path)")
}
return data
}
private struct PlistDocument {
let data: [String: Any]
init(path: String) {
guard let url = BundleToken.bundle.url(forResource: path, withExtension: nil),
let data = NSDictionary(contentsOf: url) as? [String: Any] else {
fatalError("Unable to load PLIST at path: \(path)")
}
self.data = data
}
subscript<T>(key: String) -> T {
guard let result = data[key] as? T else {
fatalError("Property '\(key)' is not of type \(T.self)")
}
return result
}
}
// swiftlint:disable convenience_type
private final class BundleToken {
static let bundle: Bundle = {
#if SWIFT_PACKAGE
return Bundle.module
#else
return Bundle(for: BundleToken.self)
#endif
}()
}
// swiftlint:enable convenience_type
+30 -2
View File
@@ -2815,6 +2815,18 @@ public class VectorL10n: NSObject {
public static var locationSharingLiveError: String {
return VectorL10n.tr("Vector", "location_sharing_live_error")
}
/// Enable live location sharing
public static var locationSharingLiveLabPromotionActivation: String {
return VectorL10n.tr("Vector", "location_sharing_live_lab_promotion_activation")
}
/// Please note: this is a labs feature using a temporary implementation that allows the history of your shared location to be permanently visible to other people in the room.
public static var locationSharingLiveLabPromotionText: String {
return VectorL10n.tr("Vector", "location_sharing_live_lab_promotion_text")
}
/// Live location sharing
public static var locationSharingLiveLabPromotionTitle: String {
return VectorL10n.tr("Vector", "location_sharing_live_lab_promotion_title")
}
/// You
public static var locationSharingLiveListItemCurrentUserDisplayName: String {
return VectorL10n.tr("Vector", "location_sharing_live_list_item_current_user_display_name")
@@ -2831,7 +2843,7 @@ public class VectorL10n: NSObject {
public static var locationSharingLiveListItemSharingExpired: String {
return VectorL10n.tr("Vector", "location_sharing_live_list_item_sharing_expired")
}
/// Stop sharing
/// Stop
public static var locationSharingLiveListItemStopSharingAction: String {
return VectorL10n.tr("Vector", "location_sharing_live_list_item_stop_sharing_action")
}
@@ -2895,6 +2907,10 @@ public class VectorL10n: NSObject {
public static func locationSharingLocatingUserErrorTitle(_ p1: String) -> String {
return VectorL10n.tr("Vector", "location_sharing_locating_user_error_title", p1)
}
/// © Copyright
public static var locationSharingMapCreditsTitle: String {
return VectorL10n.tr("Vector", "location_sharing_map_credits_title")
}
/// Open in Apple Maps
public static var locationSharingOpenAppleMaps: String {
return VectorL10n.tr("Vector", "location_sharing_open_apple_maps")
@@ -3239,10 +3255,18 @@ public class VectorL10n: NSObject {
public static var networkErrorNotReachable: String {
return VectorL10n.tr("Vector", "network_error_not_reachable")
}
/// You're offline, check your connection.
public static var networkOfflineMessage: String {
return VectorL10n.tr("Vector", "network_offline_message")
}
/// The Internet connection appears to be offline.
public static var networkOfflinePrompt: String {
return VectorL10n.tr("Vector", "network_offline_prompt")
}
/// You're offline
public static var networkOfflineTitle: String {
return VectorL10n.tr("Vector", "network_offline_title")
}
/// New
public static var newWord: String {
return VectorL10n.tr("Vector", "new_word")
@@ -5231,6 +5255,10 @@ public class VectorL10n: NSObject {
public static var roomEventFailedToSend: String {
return VectorL10n.tr("Vector", "room_event_failed_to_send")
}
/// Room Info
public static var roomInfoBackButtonTitle: String {
return VectorL10n.tr("Vector", "room_info_back_button_title")
}
/// 1 member
public static var roomInfoListOneMember: String {
return VectorL10n.tr("Vector", "room_info_list_one_member")
@@ -6723,7 +6751,7 @@ public class VectorL10n: NSObject {
public static var settingsEnableCallkit: String {
return VectorL10n.tr("Vector", "settings_enable_callkit")
}
/// Enable In-App notifications
/// Enable in-app notifications
public static var settingsEnableInappNotifications: String {
return VectorL10n.tr("Vector", "settings_enable_inapp_notifications")
}
-4
View File
@@ -234,10 +234,6 @@ public extension VectorL10n {
static var passwordValidationInfoHeader: String {
return VectorL10n.tr("Untranslated", "password_validation_info_header")
}
/// Room Info
static var roomInfoBackButtonTitle: String {
return VectorL10n.tr("Untranslated", "room_info_back_button_title")
}
}
// swiftlint:enable function_parameter_count identifier_name line_length type_body_length
@@ -335,7 +335,7 @@ Matrix session observer used to detect new opened sessions.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
NSDictionary *userInfo = notification.request.content.userInfo;
if (userInfo[Constants.userInfoKeyPresentNotificationOnForeground])
if (RiotSettings.shared.showInAppNotifications || userInfo[Constants.userInfoKeyPresentNotificationOnForeground])
{
if (!userInfo[Constants.userInfoKeyPresentNotificationInRoom]
&& [[AppDelegate theDelegate].visibleRoomId isEqualToString:userInfo[@"room_id"]])
@@ -347,7 +347,7 @@ Matrix session observer used to detect new opened sessions.
{
completionHandler(UNNotificationPresentationOptionBadge
| UNNotificationPresentationOptionSound
| UNNotificationPresentationOptionAlert);
| UNNotificationPresentationOptionBanner);
}
}
else
+14 -1
View File
@@ -80,6 +80,10 @@ final class RiotSettings: NSObject {
return RiotSettings.defaults.object(forKey: UserDefaultsKeys.notificationsShowDecryptedContent) != nil
}
/// Indicate if notifications should be shown whilst the app is in the foreground.
@UserDefault(key: "showInAppNotifications", defaultValue: true, storage: defaults)
var showInAppNotifications
/// Indicate if encrypted messages content should be displayed in notifications.
@UserDefault(key: UserDefaultsKeys.notificationsShowDecryptedContent, defaultValue: false, storage: defaults)
var showDecryptedContentInNotifications
@@ -154,7 +158,11 @@ final class RiotSettings: NSObject {
/// Indicates if live location sharing is enabled
@UserDefault(key: UserDefaultsKeys.enableLiveLocationSharing, defaultValue: false, storage: defaults)
var enableLiveLocationSharing
var enableLiveLocationSharing {
didSet {
NotificationCenter.default.post(name: RiotSettings.didUpdateLiveLocationSharingActivation, object: self)
}
}
// MARK: Calls
@@ -375,3 +383,8 @@ final class RiotSettings: NSObject {
@UserDefault(key: "lastNumberOfTrackedSpaces", defaultValue: nil, storage: defaults)
var lastNumberOfTrackedSpaces: Int?
}
// MARK: - RiotSettings notification constants
extension RiotSettings {
public static let didUpdateLiveLocationSharingActivation = Notification.Name("RiotSettingsDidUpdateLiveLocationSharingActivation")
}
@@ -0,0 +1,82 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
import DesignTokens
extension UIColor {
/// The colors from DesignKit, resolved for light mode only.
static let elementLight = ElementUIColorsResolved(dynamicColors: element, userInterfaceStyle: .light)
/// The colors from DesignKit, resolved for dark mode only.
static let elementDark = ElementUIColorsResolved(dynamicColors: element, userInterfaceStyle: .dark)
}
/// The dynamic colors from DesignKit, resolved to light or dark mode for use in the UIKit themes.
///
/// As Element doesn't (currently) update the app's `UIUserInterfaceStyle` when selecting
/// a custom theme, the dynamic colors provided by DesignKit need resolving for each theme to
/// prevent them from respecting the interface style and rendering in the wrong style.
@objcMembers public class ElementUIColorsResolved: NSObject {
// MARK: Compound
public let accent: UIColor
public let alert: UIColor
public let primaryContent: UIColor
public let secondaryContent: UIColor
public let tertiaryContent: UIColor
public let quaternaryContent: UIColor
public let quinaryContent: UIColor
public let system: UIColor
public let background: UIColor
public let namesAndAvatars: [UIColor]
// MARK: Legacy
public let quarterlyContent: UIColor
public let navigation: UIColor
public let tile: UIColor
public let separator: UIColor
// MARK: Setup
public init(dynamicColors: ElementUIColors, userInterfaceStyle: UIUserInterfaceStyle) {
let traitCollection = UITraitCollection(userInterfaceStyle: userInterfaceStyle)
self.accent = dynamicColors.accent.resolvedColor(with: traitCollection)
self.alert = dynamicColors.alert.resolvedColor(with: traitCollection)
self.primaryContent = dynamicColors.primaryContent.resolvedColor(with: traitCollection)
self.secondaryContent = dynamicColors.secondaryContent.resolvedColor(with: traitCollection)
self.tertiaryContent = dynamicColors.tertiaryContent.resolvedColor(with: traitCollection)
self.quaternaryContent = dynamicColors.quaternaryContent.resolvedColor(with: traitCollection)
self.quinaryContent = dynamicColors.quinaryContent.resolvedColor(with: traitCollection)
self.system = dynamicColors.system.resolvedColor(with: traitCollection)
self.background = dynamicColors.background.resolvedColor(with: traitCollection)
self.namesAndAvatars = dynamicColors.contentAndAvatars
// Legacy colours
self.quarterlyContent = dynamicColors.quaternaryContent.resolvedColor(with: traitCollection)
self.navigation = dynamicColors.system.resolvedColor(with: traitCollection)
if userInterfaceStyle == .light {
self.tile = UIColor(rgb: 0xF3F8FD)
self.separator = dynamicColors.quinaryContent.resolvedColor(with: traitCollection)
} else {
self.tile = dynamicColors.quinaryContent.resolvedColor(with: traitCollection)
self.separator = dynamicColors.system.resolvedColor(with: traitCollection)
}
super.init()
}
}
@@ -14,29 +14,18 @@
// limitations under the License.
//
import Foundation
import UIKit
import DesignKit
import DesignTokens
/// Theme v2. May be named again as `Theme` when the migration completed.
@objc public protocol ThemeV2 {
/// Colors object
var colors: ColorsUIKit { get }
var colors: ElementUIColorsResolved { get }
/// Fonts object
var fonts: FontsUIKit { get }
/// may contain more design components in future, like icons, audio files etc.
}
/// Theme v2 for SwiftUI.
public protocol ThemeSwiftUIType {
/// Colors object
var colors: ColorSwiftUI { get }
/// Fonts object
var fonts: FontSwiftUI { get }
var fonts: ElementUIFonts { get }
/// may contain more design components in future, like icons, audio files etc.
}
+3 -3
View File
@@ -181,9 +181,9 @@ class DarkTheme: NSObject, Theme {
button.setTitleColor(self.tintColor, for: .normal)
}
/// MARK: - Theme v2
var colors: ColorsUIKit = DarkColors.uiKit
// MARK: - Theme v2
var colors = UIColor.elementDark
var fonts: FontsUIKit = FontsUIKit(values: ElementFonts())
var fonts = UIFont.element
}
@@ -14,7 +14,6 @@
limitations under the License.
*/
import Foundation
import UIKit
import DesignKit
@@ -186,8 +185,8 @@ class DefaultTheme: NSObject, Theme {
button.setTitleColor(self.tintColor, for: .normal)
}
/// MARK: - Theme v2
var colors: ColorsUIKit = LightColors.uiKit
// MARK: - Theme v2
var colors = UIColor.elementLight
var fonts: FontsUIKit = FontsUIKit(values: ElementFonts())
var fonts = UIFont.element
}
+15 -2
View File
@@ -17,8 +17,12 @@
import PostHog
import AnalyticsEvents
/// A class responsible for managing an analytics client
/// and sending events through this client.
/// A class responsible for managing a variety of analytics clients
/// and sending events through these clients.
///
/// Events may include user activity, or app health data such as crashes,
/// non-fatal issues and performance. `Analytics` class serves as a façade
/// to all these use cases.
///
/// ## Creating Analytics Events
///
@@ -42,6 +46,9 @@ import AnalyticsEvents
/// The analytics client to send events with.
private var client: AnalyticsClientProtocol = PostHogAnalyticsClient()
/// The monitoring client to track crashes, issues and performance
private var monitoringClient = SentryMonitoringClient()
/// The service used to interact with account data settings.
private var service: AnalyticsService?
@@ -106,6 +113,7 @@ import AnalyticsEvents
// The order is important here. PostHog ignores the reset if stopped.
reset()
client.stop()
monitoringClient.stop()
MXLog.debug("[Analytics] Stopped.")
}
@@ -115,6 +123,7 @@ import AnalyticsEvents
guard RiotSettings.shared.enableAnalytics, !isRunning else { return }
client.start()
monitoringClient.start()
// Sanity check in case something went wrong.
guard client.isRunning else { return }
@@ -163,6 +172,7 @@ import AnalyticsEvents
/// Note: **MUST** be called before stopping PostHog or the reset is ignored.
func reset() {
client.reset()
monitoringClient.reset()
MXLog.debug("[Analytics] Reset.")
RiotSettings.shared.isIdentifiedForAnalytics = false
@@ -374,4 +384,7 @@ extension Analytics: MXAnalyticsDelegate {
capture(event: event)
}
func trackNonFatalIssue(_ issue: String, details: [String : Any]?) {
monitoringClient.trackNonFatalIssue(issue, details: details)
}
}
@@ -0,0 +1,67 @@
//
// Copyright 2022 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 Sentry
import MatrixSDK
/// Sentry client used as part of the Analytics set of tools to track health metrics
/// of the application, such as crashes, non-fatal issues and performance.
///
/// All analytics tracking, incl. health metrics, is subject to user consent,
/// configurable in user settings.
struct SentryMonitoringClient {
private static let sentryDSN = "https://a5e37731f9b94642a1b93093cacbee4c@sentry.tools.element.io/47"
func start() {
guard !SentrySDK.isEnabled else { return }
MXLog.debug("[SentryMonitoringClient] Started")
SentrySDK.start { options in
options.dsn = Self.sentryDSN
options.tracesSampleRate = 1.0
options.beforeSend = { event in
MXLog.debug("[SentryMonitoringClient] Issue detected: \(event)")
return event
}
options.onCrashedLastRun = { event in
MXLog.debug("[SentryMonitoringClient] Last run crashed: \(event)")
}
}
}
func stop() {
MXLog.debug("[SentryMonitoringClient] Stopped")
SentrySDK.close()
}
func reset() {
MXLog.debug("[SentryMonitoringClient] Reset")
SentrySDK.startSession()
}
func trackNonFatalIssue(_ issue: String, details: [String: Any]?) {
guard SentrySDK.isEnabled else { return }
let event = Event()
event.level = .error
event.message = .init(formatted: issue)
event.extra = details
SentrySDK.capture(event: event)
}
}
@@ -94,6 +94,16 @@ final class AppCoordinator: NSObject, AppCoordinatorType {
self.addSideMenu()
}
NotificationCenter.default.addObserver(forName: NSNotification.Name.appDelegateNetworkStatusDidChange, object: nil, queue: OperationQueue.main) { [weak self] notification in
guard let self = self else { return }
if AppDelegate.theDelegate().isOffline {
self.splitViewCoordinator?.showAppStateIndicator(with: VectorL10n.networkOfflineTitle, icon: UIImage(systemName: "wifi.slash"))
} else {
self.splitViewCoordinator?.hideAppStateIndicator()
}
}
// NOTE: When split view is shown there can be no Matrix sessions ready. Keep this behavior or use a loading screen before showing the split view.
self.showSplitView()
MXLog.debug("[AppCoordinator] Showed split view")
+29 -144
View File
@@ -195,8 +195,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
UIView *launchAnimationContainerView;
}
@property (strong, nonatomic) UIAlertController *mxInAppNotification;
@property (strong, nonatomic) UIAlertController *logoutConfirmation;
@property (weak, nonatomic) UIAlertController *gdprConsentNotGivenAlertController;
@@ -587,13 +585,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Remove expired URL previews from the cache
[URLPreviewService.shared removeExpiredCacheData];
// Hide potential notification
if (self.mxInAppNotification)
{
[self.mxInAppNotification dismissViewControllerAnimated:NO completion:nil];
self.mxInAppNotification = nil;
}
// Discard any process on pending universal link
[self resetPendingUniversalLink];
@@ -1321,8 +1312,17 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Sanity check
if (!pathParams.count)
{
MXLogDebug(@"[AppDelegate] Universal link: Error: No path parameters");
return NO;
// Handle simple room links with aliases/identifiers as UniversalLink will not parse these.
NSString* absoluteUrl = [universalLink.url.absoluteString stringByRemovingPercentEncoding];
if ([MXTools isMatrixRoomAlias:absoluteUrl]
|| [MXTools isMatrixRoomIdentifier:absoluteUrl])
{
pathParams = @[absoluteUrl];
}
else {
MXLogDebug(@"[AppDelegate] Universal link: Error: No path parameters");
return NO;
}
}
NSString *roomIdOrAlias;
@@ -1820,18 +1820,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// start the call service
[self.callPresenter start];
// Look for the account related to this session.
NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts;
for (MXKAccount *account in mxAccounts)
{
if (account.mxSession == mxSession)
{
// Enable inApp notifications (if they are allowed for this account).
[self enableInAppNotificationsForAccount:account];
break;
}
}
[self.configuration setupSettingsWhenLoadedFor:mxSession];
// Register to user new device sign in notification
@@ -1888,9 +1876,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Set up push notifications
[self.pushNotificationService registerUserNotificationSettings];
}
// Observe inApp notifications toggle change
[account addObserver:self forKeyPath:@"enableInAppNotifications" options:0 context:nil];
}
[self.delegate legacyAppDelegate:self didAddAccount:account];
@@ -1901,10 +1886,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Remove inApp notifications toggle change
MXKAccount *account = notif.object;
if (!account.isSoftLogout)
{
[account removeObserver:self forKeyPath:@"enableInAppNotifications"];
}
// Clear Modular data
[[WidgetManager sharedManager] deleteDataForUser:account.mxCredentials.userId];
@@ -1984,12 +1965,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Set up push notifications
[self.pushNotificationService registerUserNotificationSettings];
// Observe inApp notifications toggle change for each account
for (MXKAccount *account in mxAccounts)
{
[account addObserver:self forKeyPath:@"enableInAppNotifications" options:0 context:nil];
}
}
}
@@ -2256,10 +2231,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Flush and restore Matrix data
[self reloadMatrixSessions:NO];
}
else if ([@"enableInAppNotifications" isEqualToString:keyPath] && [object isKindOfClass:[MXKAccount class]])
{
[self enableInAppNotificationsForAccount:(MXKAccount*)object];
}
else if (object == [MXKAppSettings standardAppSettings] && [keyPath isEqualToString:@"enableCallKit"])
{
BOOL isCallKitEnabled = [MXKAppSettings standardAppSettings].isCallKitEnabled;
@@ -2656,100 +2627,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
#pragma mark - Matrix Accounts handling
- (void)enableInAppNotificationsForAccount:(MXKAccount*)account
{
if (account.mxSession)
{
if (account.enableInAppNotifications)
{
// Build MXEvent -> NSString formatter
EventFormatter *eventFormatter = [[EventFormatter alloc] initWithMatrixSession:account.mxSession];
eventFormatter.isForSubtitle = YES;
[account listenToNotifications:^(MXEvent *event, MXRoomState *roomState, MXPushRule *rule) {
// Check conditions to display this notification
if (![self.visibleRoomId isEqualToString:event.roomId]
&& !self.window.rootViewController.presentedViewController)
{
MXKEventFormatterError error;
NSString* messageText = [eventFormatter stringFromEvent:event
withRoomState:roomState
andLatestRoomState:nil
error:&error];
if (messageText.length && (error == MXKEventFormatterErrorNone))
{
// Removing existing notification (if any)
if (self.mxInAppNotification)
{
[self.mxInAppNotification dismissViewControllerAnimated:NO completion:nil];
}
// Check whether tweak is required
for (MXPushRuleAction *ruleAction in rule.actions)
{
if (ruleAction.actionType == MXPushRuleActionTypeSetTweak)
{
if ([[ruleAction.parameters valueForKey:@"set_tweak"] isEqualToString:@"sound"])
{
// Play message sound
AudioServicesPlaySystemSound(self->_messageSound);
}
}
}
MXRoomSummary *roomSummary = [account.mxSession roomSummaryWithRoomId:event.roomId];
__weak typeof(self) weakSelf = self;
self.mxInAppNotification = [UIAlertController alertControllerWithTitle:roomSummary.displayname
message:messageText
preferredStyle:UIAlertControllerStyleAlert];
[self.mxInAppNotification addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self.mxInAppNotification = nil;
[account updateNotificationListenerForRoomId:event.roomId ignore:YES];
}
}]];
[self.mxInAppNotification addAction:[UIAlertAction actionWithTitle:[VectorL10n view]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self.mxInAppNotification = nil;
// Show the room
[self showRoom:event.roomId andEventId:nil withMatrixSession:account.mxSession];
}
}]];
[self.window.rootViewController presentViewController:self.mxInAppNotification animated:YES completion:nil];
}
}
}];
}
else
{
[account removeNotificationListener];
}
}
if (self.mxInAppNotification)
{
[self.mxInAppNotification dismissViewControllerAnimated:NO completion:nil];
self.mxInAppNotification = nil;
}
}
- (void)selectMatrixAccount:(void (^)(MXKAccount *selectedAccount))onSelection
{
NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts;
@@ -4375,16 +4252,24 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
- (void)setupUserDefaults
{
// Register "Riot-Defaults.plist" default values
NSString* userDefaults = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UserDefaults"];
NSString *defaultsPathFromApp = [[NSBundle mainBundle] pathForResource:userDefaults ofType:@"plist"];
NSMutableDictionary *defaults = [[NSDictionary dictionaryWithContentsOfFile:defaultsPathFromApp] mutableCopy];
// add pusher ids, as they don't belong to plist anymore
defaults[@"pushKitAppIdProd"] = BuildSettings.pushKitAppIdProd;
defaults[@"pushKitAppIdDev"] = BuildSettings.pushKitAppIdDev;
defaults[@"pusherAppIdProd"] = BuildSettings.pusherAppIdProd;
defaults[@"pusherAppIdDev"] = BuildSettings.pusherAppIdDev;
// Register MatrixKit defaults.
NSDictionary *defaults = @{
@"enableBotCreation": @(BuildSettings.enableBotCreation),
@"maxAllowedMediaCacheSize": @(BuildSettings.maxAllowedMediaCacheSize),
@"presenceColorForOfflineUser": @(BuildSettings.presenceColorForOfflineUser),
@"presenceColorForOnlineUser": @(BuildSettings.presenceColorForOnlineUser),
@"presenceColorForUnavailableUser": @(BuildSettings.presenceColorForUnavailableUser),
@"showAllEventsInRoomHistory": @(BuildSettings.showAllEventsInRoomHistory),
@"showLeftMembersInRoomMemberList": @(BuildSettings.showLeftMembersInRoomMemberList),
@"showRedactionsInRoomHistory": @(BuildSettings.showRedactionsInRoomHistory),
@"showUnsupportedEventsInRoomHistory": @(BuildSettings.showUnsupportedEventsInRoomHistory),
@"sortRoomMembersUsingLastSeenTime": @(BuildSettings.syncLocalContacts),
@"syncLocalContacts": @(BuildSettings.syncLocalContacts),
@"pushKitAppIdProd": BuildSettings.pushKitAppIdProd,
@"pushKitAppIdDev": BuildSettings.pushKitAppIdDev,
@"pusherAppIdProd": BuildSettings.pusherAppIdProd,
@"pusherAppIdDev": BuildSettings.pusherAppIdDev
};
[[NSUserDefaults standardUserDefaults] registerDefaults:defaults];
@@ -0,0 +1,121 @@
//
// Copyright 2022 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
/// `VectorHostingBottomSheetPreferences` defines the bottom sheet behaviour using the `UISheetPresentationController` of the `UIViewController`
class VectorHostingBottomSheetPreferences {
// MARK: - Detent
enum Detent {
case medium
case large
@available(iOS 15, *)
fileprivate func uiSheetDetent() -> UISheetPresentationController.Detent {
switch self {
case .medium: return .medium()
case .large: return .large()
}
}
@available(iOS 15, *)
fileprivate func uiSheetDetentId() -> UISheetPresentationController.Detent.Identifier {
switch self {
case .medium: return .medium
case .large: return .large
}
}
}
// MARK: - Public
// The array of detents that the sheet may rest at.
// This array must have at least one element.
// Detents must be specified in order from smallest to largest height.
// Default: [.medium, .large]
let detents: [Detent]
// The default detent. When nil or the identifier is not found in detents, the sheet is displayed at the smallest detent.
// Default: nil
let defaultDetent: Detent?
// The largest detent that is not dimmed. When nil or the identifier is not found in detents, all detents are dimmed.
// Default: nil
let largestUndimmedDetent: Detent?
let cornerRadius: CGFloat?
// If there is a larger detent to expand to than the selected detent, and a descendent scroll view is scrolled to top, this controls whether scrolling down will expand to a larger detent.
// Useful to set to NO for non-modal sheets, where scrolling in the sheet should not expand the sheet and obscure the content above.
// Default: YES
let prefersScrollingExpandsWhenScrolledToEdge: Bool
// Set to YES to show a grabber at the top of the sheet.
// Default: `nil` -> the grabber is shown if more than one detent is configured
let prefersGrabberVisible: Bool?
// MARK: - Setup
init(detents: [Detent] = [.medium, .large],
defaultDetent: Detent? = nil,
largestUndimmedDetent: Detent? = nil,
prefersGrabberVisible: Bool? = nil,
cornerRadius: CGFloat? = nil,
prefersScrollingExpandsWhenScrolledToEdge: Bool = true) {
self.detents = detents
self.defaultDetent = defaultDetent
self.largestUndimmedDetent = largestUndimmedDetent
self.prefersGrabberVisible = prefersGrabberVisible
self.cornerRadius = cornerRadius
self.prefersScrollingExpandsWhenScrolledToEdge = prefersScrollingExpandsWhenScrolledToEdge
}
// MARK: - Public
func setup(viewController: UIViewController) {
guard #available(iOS 15.0, *) else { return }
guard let sheetController = viewController.sheetPresentationController else {
MXLog.debug("[VectorHostingBottomSheetPreferences] setup: no sheetPresentationController found")
return
}
sheetController.detents = self.uiSheetDetents()
if let prefersGrabberVisible = self.prefersGrabberVisible {
sheetController.prefersGrabberVisible = prefersGrabberVisible
} else {
sheetController.prefersGrabberVisible = self.detents.count > 1
}
sheetController.selectedDetentIdentifier = self.defaultDetent?.uiSheetDetentId()
sheetController.largestUndimmedDetentIdentifier = self.largestUndimmedDetent?.uiSheetDetentId()
sheetController.prefersScrollingExpandsWhenScrolledToEdge = self.prefersScrollingExpandsWhenScrolledToEdge
if let cornerRadius = self.cornerRadius {
sheetController.preferredCornerRadius = cornerRadius
}
}
// MARK: - Private
@available(iOS 15, *)
fileprivate func uiSheetDetents() -> [UISheetPresentationController.Detent] {
var uiSheetDetents: [UISheetPresentationController.Detent] = []
for detent in detents {
uiSheetDetents.append(detent.uiSheetDetent())
}
return uiSheetDetents
}
}
@@ -25,23 +25,34 @@ class VectorHostingController: UIHostingController<AnyView> {
// MARK: Private
var isNavigationBarHidden: Bool = false
var hidesBackTitleWhenPushed: Bool = false
private var theme: Theme
// MARK: Public
/// Wether or not the navigation bar should be hidden. Default `false`
var isNavigationBarHidden: Bool = false
/// Wether or not the title of the back item should be hidden. Default `false`
var hidesBackTitleWhenPushed: Bool = false
/// Defines the behaviour of the `VectorHostingController` as a bottom sheet. Default `nil`
var bottomSheetPreferences: VectorHostingBottomSheetPreferences?
/// Whether or not to use the iOS 15 style scroll edge appearance when the controller has a navigation bar.
var enableNavigationBarScrollEdgeAppearance = false
/// When non-nil, the style will be applied to the status bar.
var statusBarStyle: UIStatusBarStyle?
private let forceZeroSafeAreaInsets: Bool
override var preferredStatusBarStyle: UIStatusBarStyle {
statusBarStyle ?? super.preferredStatusBarStyle
}
init<Content>(rootView: Content) where Content: View {
/// Initializer
/// - Parameter rootView: Root view for the controller.
/// - Parameter forceZeroSafeAreaInsets: Whether to force-set the hosting view's safe area insets to zero. Useful when the view is used as part of a table view.
init<Content>(rootView: Content,
forceZeroSafeAreaInsets: Bool = false) where Content: View {
self.theme = ThemeService.shared().theme
self.forceZeroSafeAreaInsets = forceZeroSafeAreaInsets
super.init(rootView: AnyView(rootView.vectorContent()))
}
@@ -58,6 +69,8 @@ class VectorHostingController: UIHostingController<AnyView> {
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
bottomSheetPreferences?.setup(viewController: self)
}
override func viewWillAppear(_ animated: Bool) {
@@ -88,6 +101,22 @@ class VectorHostingController: UIHostingController<AnyView> {
self.view.invalidateIntrinsicContentSize()
}
}
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
guard forceZeroSafeAreaInsets else {
return
}
let counterSafeAreaInsets = UIEdgeInsets(top: -view.safeAreaInsets.top,
left: -view.safeAreaInsets.left,
bottom: -view.safeAreaInsets.bottom,
right: -view.safeAreaInsets.right)
if additionalSafeAreaInsets != counterSafeAreaInsets, counterSafeAreaInsets != .zero {
additionalSafeAreaInsets = counterSafeAreaInsets
}
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
@@ -98,6 +127,9 @@ class VectorHostingController: UIHostingController<AnyView> {
}
private func update(theme: Theme) {
// Ensure dynamic colors are shown correctly when the theme is the opposite appearance to the system.
overrideUserInterfaceStyle = theme.userInterfaceStyle
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar, withModernScrollEdgeAppearance: enableNavigationBarScrollEdgeAppearance)
}
@@ -96,7 +96,7 @@ class RoundedToastView: UIView, Themable {
}
func update(theme: Theme) {
backgroundColor = theme.colors.background
backgroundColor = theme.colors.system
stackView.arrangedSubviews.first?.tintColor = theme.colors.primaryContent
label.font = theme.fonts.subheadline
label.textColor = theme.colors.primaryContent
@@ -115,6 +115,12 @@ class RoundedToastView: UIView, Themable {
case .success:
imageView.image = Asset.Images.checkmark.image
return imageView
case .failure:
imageView.image = Asset.Images.errorIcon.image
return imageView
case .custom(let icon):
imageView.image = icon?.withRenderingMode(.alwaysTemplate)
return imageView
}
}
}
@@ -20,6 +20,8 @@ struct ToastViewState {
enum Style {
case loading
case success
case failure
case custom(icon: UIImage?)
}
let style: Style
@@ -23,6 +23,8 @@ import UIKit
enum UserIndicatorType {
case loading(label: String, isInteractionBlocking: Bool)
case success(label: String)
case failure(label: String)
case custom(label: String, icon: UIImage?)
}
/// A presenter which can handle `UserIndicatorType` by creating the underlying `UserIndicator`
@@ -75,6 +77,10 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
}
case .success(let label):
return successRequest(label: label)
case .failure(let label):
return failureRequest(label: label)
case .custom(let label, let icon):
return customRequest(label: label, icon: icon)
}
}
@@ -116,4 +122,32 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol {
dismissal: .timeout(1.5)
)
}
private func failureRequest(label: String) -> UserIndicatorRequest {
let presenter = ToastViewPresenter(
viewState: .init(
style: .failure,
label: label
),
presentationContext: presentationContext
)
return UserIndicatorRequest(
presenter: presenter,
dismissal: .timeout(1.5)
)
}
private func customRequest(label: String, icon: UIImage?) -> UserIndicatorRequest {
let presenter = ToastViewPresenter(
viewState: .init(
style: .custom(icon: icon),
label: label
),
presentationContext: presentationContext
)
return UserIndicatorRequest(
presenter: presenter,
dismissal: .manual
)
}
}
@@ -24,6 +24,13 @@ import CommonKit
private let presenter: UserIndicatorTypePresenterProtocol
private var indicators: [UserIndicator]
@objc init(from viewController: UIViewController) {
self.presenter = UserIndicatorTypePresenter(presentingViewController: viewController)
self.indicators = []
super.init()
}
init(presenter: UserIndicatorTypePresenterProtocol) {
self.presenter = presenter
self.indicators = []
@@ -59,4 +66,24 @@ import CommonKit
let indicator = presenter.present(.success(label: label))
indicators.append(indicator)
}
/// Present an error message that will be automatically dismissed after a few seconds.
///
/// Note: This is a convenience function callable by objective-c code
@objc func presentFailure(label: String) {
let indicator = presenter.present(.failure(label: label))
indicators.append(indicator)
}
/// Present an custom message
/// To remove the indicator call the returned `UserIndicatorCancel` function
///
/// Note: This is a convenience function callable by objective-c code
@objc func presentCustom(label: String, icon: UIImage?) -> UserIndicatorCancel {
let indicator = presenter.present(.custom(label: label, icon: icon))
indicators.append(indicator)
return {
indicator.cancel()
}
}
}
@@ -23,7 +23,7 @@ import MatrixSDK
/// It is managed by an `UserIndicator`, meaning the `present` and `dismiss` methods will be called when the parent `UserIndicator` starts or completes.
class ToastViewPresenter: UserIndicatorViewPresentable {
struct Constants {
static let navigationBarPatting = CGFloat(10)
static let navigationBarPatting = CGFloat(12)
}
private let viewState: ToastViewState
@@ -50,7 +50,7 @@ class ToastViewPresenter: UserIndicatorViewPresentable {
navigation.view.addSubview(view)
NSLayoutConstraint.activate([
view.centerXAnchor.constraint(equalTo: navigation.view.centerXAnchor),
view.topAnchor.constraint(equalTo: navigation.navigationBar.safeAreaLayoutGuide.bottomAnchor, constant: -Constants.navigationBarPatting)
view.topAnchor.constraint(equalTo: navigation.navigationBar.safeAreaLayoutGuide.bottomAnchor, constant: Constants.navigationBarPatting)
])
} else {
viewController.view.addSubview(view)
@@ -115,6 +115,15 @@
}];
[self userInterfaceThemeDidChange];
if (@available(iOS 15.0, *))
{
[[_contactsTableView.bottomAnchor constraintEqualToAnchor:self.view.keyboardLayoutGuide.topAnchor] setActive:YES];
}
else
{
[[_contactsTableView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor] setActive:YES];
}
}
- (void)userInterfaceThemeDidChange
@@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -21,8 +19,8 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="onDrag" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="orV-HH-88x">
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
<tableView clipsSubviews="YES" contentMode="scaleToFill" ambiguous="YES" alwaysBounceVertical="YES" keyboardDismissMode="onDrag" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="orV-HH-88x">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="string" keyPath="accessibilityIdentifier" value="ContactsTableVCTableView"/>
@@ -32,15 +30,14 @@
</connections>
</tableView>
</subviews>
<viewLayoutGuide key="safeArea" id="CFH-fI-3e7"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="orV-HH-88x" secondAttribute="bottom" id="BOR-bV-H3D"/>
<constraint firstItem="orV-HH-88x" firstAttribute="top" secondItem="CFH-fI-3e7" secondAttribute="top" id="HI5-Oe-Wq2"/>
<constraint firstAttribute="trailing" secondItem="orV-HH-88x" secondAttribute="trailing" id="RhY-H6-X1P"/>
<constraint firstItem="orV-HH-88x" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="vkm-Hp-89H"/>
</constraints>
<viewLayoutGuide key="safeArea" id="CFH-fI-3e7"/>
<point key="canvasLocation" x="138.40000000000001" y="153.37331334332833"/>
<point key="canvasLocation" x="138.40000000000001" y="152.47376311844079"/>
</view>
</objects>
</document>
@@ -21,7 +21,7 @@ final class EncryptionKeysExportPresenter: NSObject {
// MARK: - Constants
private enum Constants {
static let keyExportFileName = "riot-keys.txt"
static let keyExportFileName = "element-keys.txt"
}
// MARK: - Properties
@@ -17,6 +17,7 @@
*/
import UIKit
import MatrixSDK
final class KeyVerificationVerifyByScanningViewController: UIViewController {
@@ -215,7 +216,12 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController {
private func qrCodeImage(from data: Data) -> UIImage? {
let codeGenerator = QRCodeGenerator()
return codeGenerator.generateCode(from: data, with: self.codeImageView.frame.size)
do {
return try codeGenerator.generateCode(from: data, with: codeImageView.frame.size)
} catch {
MXLog.error("[KeyVerificationVerifyByScanningViewController] qrCodeImage: cannot generate QR code - \(error)")
return nil
}
}
private func presentQRCodeReader(animated: Bool) {
@@ -112,7 +112,7 @@ class LocationManager: NSObject {
switch accuracy {
case .full:
self.locationManager.startUpdatingLocation()
self.locationManager.stopUpdatingLocation()
case .reduced:
self.locationManager.stopMonitoringSignificantLocationChanges()
}
@@ -26,16 +26,16 @@ class UserLocationServiceProvider {
// MARK: - Properties
// UserLocationService per user id
private var locationServices: [String: UserLocationServiceProtocol] = [:]
// MARK: - Setup
private init() {
guard RiotSettings.shared.enableLiveLocationSharing else {
return
}
self.setupOrTeardownLocationServices()
self.registerUserSessionsServiceNotifications()
// Listen to lab flag changes
self.registerRiotSettingsNotifications()
}
// MARK: - Public
@@ -71,7 +71,7 @@ class UserLocationServiceProvider {
MXLog.debug("Start monitoring user live location sharing")
}
func setupUserLocationServiceIfNeeded(for userSession: UserSession) {
private func setupUserLocationServiceIfNeeded(for userSession: UserSession) {
// Be sure Matrix session has is store setup to access beacon info summaries
guard userSession.matrixSession.state.rawValue >= MXSessionState.storeDataReady.rawValue else {
@@ -100,6 +100,30 @@ class UserLocationServiceProvider {
MXLog.debug("Stop monitoring user live location sharing")
}
private func setupOrTeardownLocationServices() {
self.unregisterUserSessionsServiceNotifications()
if RiotSettings.shared.enableLiveLocationSharing {
self.setupUserLocationServiceForAllUsers()
self.registerUserSessionsServiceNotifications()
} else {
self.tearDownUserLocationServiceForAllUsers()
}
}
private func setupUserLocationServiceForAllUsers() {
for userSession in UserSessionsService.shared.userSessions {
self.setupUserLocationService(for: userSession)
}
}
private func tearDownUserLocationServiceForAllUsers() {
for (userId, _) in self.locationServices {
self.tearDownUserLocationService(for: userId)
}
}
// MARK: UserSessions management
private func registerUserSessionsServiceNotifications() {
@@ -111,6 +135,15 @@ class UserLocationServiceProvider {
NotificationCenter.default.addObserver(self, selector: #selector(userSessionsServiceDidRemoveUserSession(_:)), name: UserSessionsService.didRemoveUserSession, object: nil)
}
private func unregisterUserSessionsServiceNotifications() {
NotificationCenter.default.removeObserver(self, name: UserSessionsService.didAddUserSession, object: nil)
NotificationCenter.default.removeObserver(self, name: UserSessionsService.userSessionDidChange, object: nil)
NotificationCenter.default.removeObserver(self, name: UserSessionsService.didRemoveUserSession, object: nil)
}
@objc private func userSessionsServiceDidAddUserSession(_ notification: Notification) {
guard let userInfo = notification.userInfo, let userSession = userInfo[UserSessionsService.NotificationUserInfoKey.userSession] as? UserSession else {
@@ -137,4 +170,17 @@ class UserLocationServiceProvider {
self.tearDownUserLocationService(for: userId)
}
// MARK: - RiotSettings
private func registerRiotSettingsNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(riotSettingsDidUpdateLiveLocationSharingActivation(_:)), name: RiotSettings.didUpdateLiveLocationSharingActivation, object: nil)
}
@objc private func riotSettingsDidUpdateLiveLocationSharingActivation(_ notification: Notification) {
// Lab flag value has changed, check if we should enable or disable location services
self.setupOrTeardownLocationServices()
}
}
@@ -31,17 +31,28 @@
return nil;
}
NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
NSString *message = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
if (!title)
NSString *title;
NSString *message;
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorNotConnectedToInternet)
{
title = [VectorL10n error];
title = [VectorL10n networkOfflineTitle];
message = [VectorL10n networkOfflineMessage];
}
if (!message)
else
{
message = [VectorL10n errorCommonMessage];
title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey];
message = [error.userInfo valueForKey:NSLocalizedDescriptionKey];
if (!title)
{
title = [VectorL10n error];
}
if (!message)
{
message = [VectorL10n errorCommonMessage];
}
}
return [[MXKErrorViewModel alloc] initWithTitle:title message:message];
@@ -1356,9 +1356,22 @@ static NSString *const kHTMLATagRegexPattern = @"<a href=(?:'|\")(.*?)(?:'|\")>(
}
else if ([msgtype isEqualToString:kMXMessageTypeFile])
{
body = body? body : [VectorL10n noticeFileAttachment];
// Check attachment validity
if (![self isSupportedAttachment:event])
if ([self isSupportedAttachment:event])
{
body = body? body : [VectorL10n noticeFileAttachment];
NSDictionary *fileInfo = contentToUse[@"info"];
if (fileInfo)
{
NSNumber *fileSize = fileInfo[@"size"];
if (fileSize)
{
body = [NSString stringWithFormat:@"%@ (%@)", body, [MXTools fileSizeToString: fileSize.longValue]];
}
}
}
else
{
MXLogDebug(@"[MXKEventFormatter] Warning: Unsupported attachment %@", event.description);
body = [VectorL10n noticeInvalidAttachment];
+20 -3
View File
@@ -25,6 +25,14 @@ class PillsFormatter: NSObject {
/// UTType identifier for pills. Should be declared as Document type & Exported type identifier inside Info.plist
static let pillUTType: String = "im.vector.app.pills"
// MARK: - Internal Enums
/// Defines a replacement mode for converting Pills to plain text.
@objc enum PillsReplacementTextMode: Int {
case displayname
case identifier
case markdown
}
// MARK: - Internal Methods
/// Insert text attachments for pills inside given message attributed string.
///
@@ -66,15 +74,24 @@ class PillsFormatter: NSObject {
///
/// - Parameters:
/// - attributedString: attributed string with pills
/// - asMarkdown: wether pill should be replaced by markdown links or raw text
/// - mode: replacement mode for pills (default: displayname)
/// - Returns: string with display names
static func stringByReplacingPills(in attributedString: NSAttributedString, asMarkdown: Bool = false) -> String {
static func stringByReplacingPills(in attributedString: NSAttributedString,
mode: PillsReplacementTextMode = .displayname) -> String {
let newAttr = NSMutableAttributedString(attributedString: attributedString)
newAttr.vc_enumerateAttribute(.attachment) { (attachment: PillTextAttachment, range: NSRange, _) in
if let displayText = attachment.data?.displayText,
let userId = attachment.data?.matrixItemId,
let permalink = MXTools.permalinkToUser(withUserId: userId) {
let pillString = asMarkdown ? "[\(displayText)](\(permalink))" : "\(displayText)"
let pillString: String
switch mode {
case .displayname:
pillString = displayText
case .identifier:
pillString = userId
case .markdown:
pillString = "[\(displayText)](\(permalink))"
}
newAttr.replaceCharacters(in: range, with: pillString)
}
}
+18 -25
View File
@@ -15,36 +15,29 @@
*/
import Foundation
import ZXingObjC
final class QRCodeGenerator {
// MARK: - Constants
private enum Constants {
static let qrCodeGeneratorFilter = "CIQRCodeGenerator"
static let qrCodeInputCorrectionLevel = "M"
enum Error: Swift.Error {
case cannotCreateImage
}
// MARK: - Public
func generateCode(from data: Data, with size: CGSize) -> UIImage? {
guard let filter = CIFilter(name: Constants.qrCodeGeneratorFilter) else {
return nil
func generateCode(from data: Data, with size: CGSize) throws -> UIImage {
let writer = ZXMultiFormatWriter()
let endodedString = String(data: data, encoding: .isoLatin1)
let scale = UIScreen.main.scale
let bitMatrix = try writer.encode(
endodedString,
format: kBarcodeFormatQRCode,
width: Int32(size.width * scale),
height: Int32(size.height * scale),
hints: ZXEncodeHints()
)
guard let cgImage = ZXImage(matrix: bitMatrix).cgimage else {
throw Error.cannotCreateImage
}
filter.setValue(data, forKey: "inputMessage")
filter.setValue(Constants.qrCodeInputCorrectionLevel, forKey: "inputCorrectionLevel") // Be sure to use same error resilience level as other platform
guard let ciImage = filter.outputImage else {
return nil
}
let scaleX = size.width/ciImage.extent.size.width
let scaleY = size.height/ciImage.extent.size.height
let transform = CGAffineTransform(scaleX: scaleX, y: scaleY)
let transformedCIImage = ciImage.transformed(by: transform)
return UIImage(ciImage: transformedCIImage)
return UIImage(cgImage: cgImage)
}
}
@@ -219,7 +219,7 @@ private extension RoomDataSource {
func htmlMessageFromSanitizedAttributedText(_ sanitizedText: NSAttributedString) -> String? {
let rawText: String
if #available(iOS 15.0, *) {
rawText = PillsFormatter.stringByReplacingPills(in: sanitizedText, asMarkdown: true)
rawText = PillsFormatter.stringByReplacingPills(in: sanitizedText, mode: .markdown)
} else {
rawText = sanitizedText.string
}
@@ -49,7 +49,7 @@ struct TimelineLiveLocationViewData {
}
var showMap: Bool {
guard case .started(_, _) = status else {
guard case .started = status else {
return false
}
return true
@@ -200,7 +200,7 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat
}
liveLocationContainerView.isHidden = false
liveLocationContainerView.backgroundColor = theme.colors.background.withAlphaComponent(0.75)
liveLocationContainerView.backgroundColor = theme.colors.background.withAlphaComponent(0.90)
liveLocationIcon.image = Asset.Images.locationLiveCellIcon.image
liveLocationIcon.tintColor = bannerViewData.iconTint
+1 -1
View File
@@ -394,7 +394,7 @@ typedef NS_ENUM(NSUInteger, MXKRoomViewControllerJoinRoomResult) {
@param string to analyse
@return YES if IRC style command has been detected and interpreted.
*/
- (BOOL)isIRCStyleCommand:(NSString*)string;
- (BOOL)sendAsIRCStyleCommandIfPossible:(NSString*)string;
/**
Mention the member display name in the current text of the message composer.
+2 -2
View File
@@ -1250,7 +1250,7 @@
customEventDetailsViewClass = eventDetailsViewClass;
}
- (BOOL)isIRCStyleCommand:(NSString*)string
- (BOOL)sendAsIRCStyleCommandIfPossible:(NSString*)string
{
// Check whether the provided text may be an IRC-style command
if ([string hasPrefix:@"/"] == NO || [string hasPrefix:@"//"] == YES)
@@ -3375,7 +3375,7 @@
- (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView sendTextMessage:(NSString*)textMessage
{
// Handle potential IRC commands in typed string
if ([self isIRCStyleCommand:textMessage] == NO)
if ([self sendAsIRCStyleCommandIfPossible:textMessage] == NO)
{
// Send text message in the current room
[self sendTextMessage:textMessage];
@@ -126,6 +126,7 @@ class ContactsPickerViewModel: NSObject, ContactsPickerViewModelProtocol {
contactsViewController.showSearch(true)
contactsViewController.searchBar.placeholder = VectorL10n.roomParticipantsInviteAnotherUser
contactsViewController.searchBar.resignFirstResponder()
// Apply the search pattern if any
if currentSearchText != nil {
+27 -6
View File
@@ -1228,7 +1228,7 @@ static CGSize kThreadListBarButtonItemImageSize;
}
}
- (BOOL)isIRCStyleCommand:(NSString*)string
- (BOOL)sendAsIRCStyleCommandIfPossible:(NSString*)string
{
// Override the default behavior for `/join` command in order to open automatically the joined room
@@ -1271,7 +1271,7 @@ static CGSize kThreadListBarButtonItemImageSize;
}
return YES;
}
return [super isIRCStyleCommand:string];
return [super sendAsIRCStyleCommandIfPossible:string];
}
- (void)setKeyboardHeight:(CGFloat)keyboardHeight
@@ -4801,7 +4801,28 @@ static CGSize kThreadListBarButtonItemImageSize;
- (void)roomInputToolbarView:(RoomInputToolbarView *)toolbarView sendAttributedTextMessage:(NSAttributedString *)attributedTextMessage
{
[self sendAttributedTextMessage:attributedTextMessage];
BOOL isMessageAHandledCommand = NO;
// "/me" command is supported with Pills in RoomDataSource.
if (![attributedTextMessage.string hasPrefix:kMXKSlashCmdEmote])
{
// Other commands currently work with identifiers (e.g. ban, invite, op, etc).
NSString *message;
if (@available(iOS 15.0, *))
{
message = [PillsFormatter stringByReplacingPillsIn:attributedTextMessage mode:PillsReplacementTextModeIdentifier];
}
else
{
message = attributedTextMessage.string;
}
// Try to send the slash command
isMessageAHandledCommand = [self sendAsIRCStyleCommandIfPossible:message];
}
if (!isMessageAHandledCommand)
{
[self sendAttributedTextMessage:attributedTextMessage];
}
}
#pragma mark - MXKRoomMemberDetailsViewControllerDelegate
@@ -5512,8 +5533,7 @@ static CGSize kThreadListBarButtonItemImageSize;
}
else if ([AppDelegate theDelegate].isOffline)
{
self.activitiesViewExpanded = YES;
[roomActivitiesView displayNetworkErrorNotification:[VectorL10n roomOfflineNotification]];
// Doing nothing here as the offline notification is now handled by the AppCoordinator
}
else if (self.customizedRoomDataSource.roomState.isObsolete)
{
@@ -6820,7 +6840,8 @@ static CGSize kThreadListBarButtonItemImageSize;
{
if (@available(iOS 15.0, *))
{
MXKPasteboardManager.shared.pasteboard.string = [PillsFormatter stringByReplacingPillsIn:attributedTextMessage asMarkdown:YES];
MXKPasteboardManager.shared.pasteboard.string = [PillsFormatter stringByReplacingPillsIn:attributedTextMessage
mode:PillsReplacementTextModeMarkdown];
}
else
{
@@ -22,8 +22,11 @@ class FileWithoutThumbnailBaseBubbleCell: SizableBaseRoomCell, RoomCellReactions
override func render(_ cellData: MXKCellData!) {
super.render(cellData)
self.fileAttachementView?.titleLabel.attributedText = self.suitableAttributedTextMessage
let attributedText = NSMutableAttributedString(attributedString: self.suitableAttributedTextMessage)
attributedText.addAttributes([.foregroundColor: ThemeService.shared().theme.colors.secondaryContent],
range: NSRange(location: 0, length: attributedText.length))
self.fileAttachementView?.titleLabel.attributedText = attributedText
self.update(theme: ThemeService.shared().theme)
}
@@ -23,9 +23,10 @@ final class FileWithoutThumbnailCellContentView: UIView, NibLoadable {
// MARK: Outlets
@IBOutlet private weak var iconBackgroundView: UIView!
@IBOutlet private weak var iconImageView: UIImageView!
@IBOutlet private(set) weak var titleLabel: UILabel!
// MARK: Public
var badgeImage: UIImage? {
@@ -49,17 +50,23 @@ final class FileWithoutThumbnailCellContentView: UIView, NibLoadable {
super.awakeFromNib()
self.layer.masksToBounds = true
self.iconImageView.image = Asset.Images.fileAttachment.image.withRenderingMode(.alwaysTemplate)
self.iconBackgroundView.layer.masksToBounds = true
update(theme: ThemeService.shared().theme)
}
override func layoutSubviews() {
super.layoutSubviews()
self.layer.cornerRadius = BubbleRoomCellLayoutConstants.bubbleCornerRadius
self.iconBackgroundView.layer.cornerRadius = self.iconBackgroundView.bounds.midX
}
// MARK: - Public
func update(theme: Theme) {
self.titleLabel.textColor = theme.textPrimaryColor
self.iconBackgroundView.backgroundColor = theme.roomCellIncomingBubbleBackgroundColor
self.iconImageView.tintColor = theme.colors.secondaryContent
}
}
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -11,43 +11,56 @@
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="8T9-hj-ply" customClass="FileWithoutThumbnailCellContentView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="313" height="42"/>
<rect key="frame" x="0.0" y="0.0" width="313" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="tvT-jE-0OH">
<rect key="frame" x="20" y="15" width="253" height="12"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" image="file_attachment" translatesAutoresizingMaskIntoConstraints="NO" id="Pse-Mu-Gle">
<rect key="frame" x="0.0" y="0.0" width="16" height="12"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Filename.docx" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eCx-a1-1CO">
<rect key="frame" x="32" y="0.0" width="221" height="12"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="systemGray2Color"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mdp-UC-wky">
<rect key="frame" x="12" y="6" width="32" height="32"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" image="file_attachment" translatesAutoresizingMaskIntoConstraints="NO" id="Pse-Mu-Gle">
<rect key="frame" x="12" y="6" width="32" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="V7S-aN-VMm"/>
<constraint firstAttribute="width" constant="32" id="tLW-tC-XhS"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Filename.docx" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eCx-a1-1CO">
<rect key="frame" x="52" y="13.5" width="221" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" systemColor="systemGray2Color"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.95294117649999999" green="0.97254901959999995" blue="0.99215686270000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="tvT-jE-0OH" secondAttribute="trailing" constant="40" id="At8-ah-N7j"/>
<constraint firstItem="tvT-jE-0OH" firstAttribute="top" secondItem="8T9-hj-ply" secondAttribute="top" constant="15" id="C7r-6f-Bqf"/>
<constraint firstItem="tvT-jE-0OH" firstAttribute="leading" secondItem="8T9-hj-ply" secondAttribute="leading" constant="20" id="Yh6-91-Thq"/>
<constraint firstAttribute="bottom" secondItem="tvT-jE-0OH" secondAttribute="bottom" constant="15" id="yHE-fi-Paj"/>
<constraint firstItem="Pse-Mu-Gle" firstAttribute="top" secondItem="8T9-hj-ply" secondAttribute="top" constant="6" id="6DX-oP-MYH"/>
<constraint firstItem="eCx-a1-1CO" firstAttribute="leading" secondItem="Pse-Mu-Gle" secondAttribute="trailing" constant="8" id="73z-aH-1pr"/>
<constraint firstItem="mdp-UC-wky" firstAttribute="trailing" secondItem="Pse-Mu-Gle" secondAttribute="trailing" id="GNo-nG-I21"/>
<constraint firstAttribute="bottom" secondItem="Pse-Mu-Gle" secondAttribute="bottom" constant="6" id="HvW-1P-u2B"/>
<constraint firstItem="mdp-UC-wky" firstAttribute="top" secondItem="Pse-Mu-Gle" secondAttribute="top" id="QF9-xt-bYe"/>
<constraint firstItem="Pse-Mu-Gle" firstAttribute="leading" secondItem="8T9-hj-ply" secondAttribute="leading" constant="12" id="hSp-8S-ZzS"/>
<constraint firstAttribute="trailing" secondItem="eCx-a1-1CO" secondAttribute="trailing" constant="40" id="o0Y-d1-nWF"/>
<constraint firstItem="mdp-UC-wky" firstAttribute="bottom" secondItem="Pse-Mu-Gle" secondAttribute="bottom" id="ubz-2T-7zP"/>
<constraint firstItem="eCx-a1-1CO" firstAttribute="centerY" secondItem="8T9-hj-ply" secondAttribute="centerY" id="xnb-OL-gFh"/>
<constraint firstItem="mdp-UC-wky" firstAttribute="leading" secondItem="Pse-Mu-Gle" secondAttribute="leading" id="zvT-IB-btm"/>
</constraints>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="iconBackgroundView" destination="mdp-UC-wky" id="A7k-W3-TjF"/>
<outlet property="iconImageView" destination="Pse-Mu-Gle" id="pAQ-jI-GDj"/>
<outlet property="titleLabel" destination="eCx-a1-1CO" id="Oj5-X3-Rop"/>
</connections>
<point key="canvasLocation" x="-1454.3478260869567" y="-405.80357142857139"/>
<point key="canvasLocation" x="-1454.3478260869567" y="-402.45535714285711"/>
</view>
</objects>
<resources>
<image name="file_attachment" width="16" height="16"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemGray2Color">
<color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
@@ -0,0 +1,75 @@
//
// Copyright 2022 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 FileWithoutThumbnailPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable, RoomCellThreadSummaryDisplayable {
private(set) var fileAttachementView: FileWithoutThumbnailCellContentView!
override func render(_ cellData: MXKCellData!) {
super.render(cellData)
guard let data = cellData as? RoomBubbleCellData else {
return
}
guard data.attachment.type == .file else {
fatalError("Invalid attachment type passed to a file without thumbnail cell.")
}
let attributedText = NSMutableAttributedString(attributedString: self.suitableAttributedTextMessage)
attributedText.addAttributes([.foregroundColor: ThemeService.shared().theme.colors.secondaryContent],
range: NSRange(location: 0, length: attributedText.length))
self.fileAttachementView.titleLabel.attributedText = attributedText
self.update(theme: ThemeService.shared().theme)
}
override func setupViews() {
super.setupViews()
roomCellContentView?.showSenderInfo = true
roomCellContentView?.showPaginationTitle = false
guard let contentView = roomCellContentView?.innerContentView else {
return
}
fileAttachementView = FileWithoutThumbnailCellContentView.loadFromNib()
contentView.vc_addSubViewMatchingParent(fileAttachementView)
}
override func update(theme: Theme) {
super.update(theme: theme)
guard let fileAttachementView = fileAttachementView else {
return
}
fileAttachementView.update(theme: theme)
fileAttachementView.backgroundColor = theme.colors.quinaryContent
}
override func onContentViewTap(_ sender: UITapGestureRecognizer!) {
if let bubbleData = self.bubbleData, bubbleData.isAttachment {
self.delegate.cell(self, didRecognizeAction: kMXKRoomBubbleCellTapOnAttachmentView, userInfo: nil)
} else {
super.onContentViewTap(sender)
}
}
}
@@ -0,0 +1,25 @@
//
// Copyright 2022 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 FileWithoutThumbnailWithPaginationTitlePlainCell: FileWithoutThumbnailPlainCell {
override func setupViews() {
super.setupViews()
roomCellContentView?.showPaginationTitle = true
}
}
@@ -0,0 +1,25 @@
//
// Copyright 2022 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 FileWithoutThumbnailWithoutSenderInfoPlainCell: FileWithoutThumbnailPlainCell {
override func setupViews() {
super.setupViews()
roomCellContentView?.showSenderInfo = false
}
}
@@ -110,7 +110,9 @@
[self registerPollCellsForTableView:tableView];
[self registerLocationCellsForTableView:tableView];
[self registerFileWithoutThumbnailCellsForTableView:tableView];
[tableView registerClass:RoomEmptyBubbleCell.class forCellReuseIdentifier:RoomEmptyBubbleCell.defaultReuseIdentifier];
[tableView registerClass:RoomSelectedStickerBubbleCell.class forCellReuseIdentifier:RoomSelectedStickerBubbleCell.defaultReuseIdentifier];
@@ -261,6 +263,13 @@
[tableView registerClass:LocationWithPaginationTitlePlainCell.class forCellReuseIdentifier:LocationWithPaginationTitlePlainCell.defaultReuseIdentifier];
}
- (void)registerFileWithoutThumbnailCellsForTableView:(UITableView*)tableView
{
[tableView registerClass:FileWithoutThumbnailPlainCell.class forCellReuseIdentifier:FileWithoutThumbnailPlainCell.defaultReuseIdentifier];
[tableView registerClass:FileWithoutThumbnailWithoutSenderInfoPlainCell.class forCellReuseIdentifier:FileWithoutThumbnailWithoutSenderInfoPlainCell.defaultReuseIdentifier];
[tableView registerClass:FileWithoutThumbnailWithPaginationTitlePlainCell.class forCellReuseIdentifier:FileWithoutThumbnailWithPaginationTitlePlainCell.defaultReuseIdentifier];
}
#pragma mark Cell class association
- (NSDictionary<NSNumber*, Class>*)buildCellClasses
@@ -435,13 +444,13 @@
{
return @{
// Clear
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnail) : RoomIncomingTextMsgBubbleCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailWithoutSenderInfo) : RoomIncomingTextMsgWithoutSenderInfoBubbleCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailWithPaginationTitle) : RoomIncomingTextMsgWithPaginationTitleBubbleCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnail) : FileWithoutThumbnailPlainCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailWithoutSenderInfo) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailWithPaginationTitle) : FileWithoutThumbnailWithPaginationTitlePlainCell.class,
// Encrypted
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncrypted) : RoomIncomingEncryptedTextMsgBubbleCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncryptedWithoutSenderInfo) : RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncryptedWithPaginationTitle) : RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.class
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncrypted) : FileWithoutThumbnailPlainCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncryptedWithoutSenderInfo) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class,
@(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncryptedWithPaginationTitle) : FileWithoutThumbnailWithPaginationTitlePlainCell.class
};
}
@@ -449,13 +458,13 @@
{
return @{
// Clear
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnail) : RoomOutgoingTextMsgBubbleCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailWithoutSenderInfo) : RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailWithPaginationTitle) : RoomOutgoingTextMsgWithPaginationTitleBubbleCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnail) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailWithoutSenderInfo) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailWithPaginationTitle) : FileWithoutThumbnailWithPaginationTitlePlainCell.class,
// Encrypted
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncrypted) : RoomOutgoingEncryptedTextMsgBubbleCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncryptedWithoutSenderInfo) : RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncryptedWithPaginationTitle) : RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.class
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncrypted) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncryptedWithoutSenderInfo) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class,
@(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncryptedWithPaginationTitle) : FileWithoutThumbnailWithPaginationTitlePlainCell.class
};
}
@@ -137,7 +137,7 @@ class VoiceMessagePlaybackView: UIView, NibLoadable, Themable {
}
self.backgroundColor = theme.colors.background
playButton.backgroundColor = theme.colors.background
playButton.backgroundColor = theme.roomCellIncomingBubbleBackgroundColor
playButton.tintColor = theme.colors.secondaryContent
let backgroundViewColor = self.customBackgroundViewColor ?? theme.colors.quinaryContent
@@ -145,7 +145,8 @@ class VoiceMessagePlaybackView: UIView, NibLoadable, Themable {
backgroundView.backgroundColor = backgroundViewColor
_waveformView.primaryLineColor = theme.colors.quarterlyContent
_waveformView.secondaryLineColor = theme.colors.secondaryContent
elapsedTimeLabel.textColor = theme.colors.tertiaryContent
elapsedTimeLabel.textColor = theme.colors.secondaryContent
elapsedTimeLabel.font = theme.fonts.body
}
func getRequiredNumberOfSamples() -> Int {
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -22,7 +22,7 @@
</constraints>
</view>
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="ZQ2-Ij-mYr">
<rect key="frame" x="8" y="0.0" width="411" height="44"/>
<rect key="frame" x="12" y="0.0" width="403" height="44"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="voice_message_record_icon" translatesAutoresizingMaskIntoConstraints="NO" id="REB-gl-h0h">
<rect key="frame" x="0.0" y="17" width="10" height="10"/>
@@ -48,7 +48,7 @@
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7Fl-yZ-dZB">
<rect key="frame" x="94" y="7" width="317" height="30"/>
<rect key="frame" x="94" y="7" width="309" height="30"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<gestureRecognizers/>
</view>
@@ -66,8 +66,8 @@
<constraint firstItem="LPc-i8-8UC" firstAttribute="leading" secondItem="Ugy-Dx-gcs" secondAttribute="leading" id="FnY-Ab-FVL"/>
<constraint firstItem="ZQ2-Ij-mYr" firstAttribute="top" secondItem="cGR-49-HWB" secondAttribute="top" id="KRu-5w-kGE"/>
<constraint firstAttribute="bottom" secondItem="LPc-i8-8UC" secondAttribute="bottom" id="apf-b1-yIb"/>
<constraint firstItem="ZQ2-Ij-mYr" firstAttribute="leading" secondItem="cGR-49-HWB" secondAttribute="leading" constant="8" id="fDO-rh-Jbl"/>
<constraint firstAttribute="trailing" secondItem="ZQ2-Ij-mYr" secondAttribute="trailing" constant="8" id="fM3-nY-rDV"/>
<constraint firstItem="ZQ2-Ij-mYr" firstAttribute="leading" secondItem="cGR-49-HWB" secondAttribute="leading" constant="12" id="fDO-rh-Jbl"/>
<constraint firstAttribute="trailing" secondItem="ZQ2-Ij-mYr" secondAttribute="trailing" constant="12" id="fM3-nY-rDV"/>
<constraint firstItem="LPc-i8-8UC" firstAttribute="top" secondItem="cGR-49-HWB" secondAttribute="top" id="zl5-Sf-qSF"/>
</constraints>
<nil key="simulatedTopBarMetrics"/>
@@ -24,6 +24,7 @@
#import "GeneratedInterface-Swift.h"
@import DesignKit;
enum
{
@@ -26,6 +26,8 @@
#import "GeneratedInterface-Swift.h"
@import DesignKit;
// Dev flag to have more options
//#define CROSS_SIGNING_AND_BACKUP_DEV
@@ -1443,7 +1445,7 @@ TableViewSectionsDelegate>
currentAlert = exportView.alertController;
// Use a temporary file for the export
keyExportsFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"riot-keys.txt"]];
keyExportsFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"element-keys.txt"]];
// Make sure the file is empty
[self deleteKeyExportFile];
@@ -45,6 +45,8 @@
#import "GeneratedInterface-Swift.h"
@import DesignKit;
NSString* const kSettingsViewControllerPhoneBookCountryCellId = @"kSettingsViewControllerPhoneBookCountryCellId";
typedef NS_ENUM(NSUInteger, SECTION_TAG)
@@ -103,6 +105,7 @@ typedef NS_ENUM(NSUInteger, NOTIFICATION_SETTINGS)
{
NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX = 0,
NOTIFICATION_SETTINGS_SYSTEM_SETTINGS,
NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX,
NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT,
NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX,
NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX,
@@ -410,6 +413,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
Section *sectionNotificationSettings = [Section sectionWithTag:SECTION_TAG_NOTIFICATIONS];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SYSTEM_SETTINGS];
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX];
if (RiotSettings.shared.settingsScreenShowNotificationDecodedContentOption)
{
[sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT];
@@ -2076,6 +2080,18 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
[cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme];
cell.selectionStyle = UITableViewCellSelectionStyleDefault;
}
else if (row == NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
labelAndSwitchCell.mxkLabel.text = VectorL10n.settingsEnableInappNotifications;
labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.showInAppNotifications;
labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor;
labelAndSwitchCell.mxkSwitch.enabled = account.pushNotificationServiceIsActive;
[labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleShowInAppNotifications:) forControlEvents:UIControlEventTouchUpInside];
cell = labelAndSwitchCell;
}
else if (row == NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT)
{
MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath];
@@ -3165,6 +3181,11 @@ ChangePasswordCoordinatorBridgePresenterDelegate>
}
}
- (void)toggleShowInAppNotifications:(UISwitch *)sender
{
RiotSettings.shared.showInAppNotifications = sender.isOn;
}
- (void)openSystemSettingsApp
{
NSURL *settingsAppURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
@@ -156,6 +156,18 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType {
self.tabBarCoordinator?.popToHome(animated: animated, completion: completion)
}
func showErroIndicator(with error: Error) {
tabBarCoordinator?.showErroIndicator(with: error)
}
func hideAppStateIndicator() {
tabBarCoordinator?.hideAppStateIndicator()
}
func showAppStateIndicator(with text: String, icon: UIImage?) {
tabBarCoordinator?.showAppStateIndicator(with: text, icon: icon)
}
// MARK: - Private methods
private func createPlaceholderDetailsViewController() -> UIViewController {
@@ -36,4 +36,13 @@ protocol SplitViewCoordinatorType: Coordinator, Presentable {
// TODO: Do not expose publicly this method
/// Remove detail screens and display placeholder if needed
func resetDetails(animated: Bool)
/// Displays an error using a `UserIndicator`. The messages is dimissed automatically.
func showErroIndicator(with error: Error)
/// Displays an message related to the application state using a `UserIndicator`. The message must be dimissed by calling the method `hideAppStateIndicator()`
func showAppStateIndicator(with text: String, icon: UIImage?)
/// Hide the message related to the application state currently displayed.
func hideAppStateIndicator()
}
+36 -1
View File
@@ -29,7 +29,10 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
private let parameters: TabBarCoordinatorParameters
private let activityIndicatorPresenter: ActivityIndicatorPresenterType
private let indicatorPresenter: UserIndicatorTypePresenterProtocol
private let userIndicatorStore: UserIndicatorStore
private var appStateIndicatorCancel: UserIndicatorCancel?
private var appSateIndicator: UserIndicator?
// Indicate if the Coordinator has started once
private var hasStartedOnce: Bool {
return self.masterTabBarController != nil
@@ -84,6 +87,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
self.masterNavigationController = masterNavigationController
self.activityIndicatorPresenter = ActivityIndicatorPresenter()
self.indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: masterNavigationController)
self.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter)
}
// MARK: - Public methods
@@ -190,6 +194,37 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
}
}
func showErroIndicator(with error: Error) {
let error = error as NSError
// Ignore fake error, or connection cancellation error
guard error.domain != NSURLErrorDomain || error.code != NSURLErrorCancelled else {
return
}
// Ignore GDPR Consent not given error. Already caught by kMXHTTPClientUserConsentNotGivenErrorNotification observation
let mxError = MXError.isMXError(error) ? MXError(nsError: error) : nil
guard mxError?.errcode != kMXErrCodeStringConsentNotGiven else {
return
}
let msg = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String
let localizedDescription = error.userInfo[NSLocalizedDescriptionKey] as? String
let title = (error.userInfo[NSLocalizedFailureReasonErrorKey] as? String) ?? (msg ?? (localizedDescription ?? VectorL10n.error))
indicators.append(self.indicatorPresenter.present(.failure(label: title)))
}
func showAppStateIndicator(with text: String, icon: UIImage?) {
hideAppStateIndicator()
appSateIndicator = self.indicatorPresenter.present(.custom(label: text, icon: icon))
}
func hideAppStateIndicator() {
appSateIndicator?.cancel()
appSateIndicator = nil
}
// MARK: - SplitViewMasterPresentable
var selectedNavigationRouter: NavigationRouterType? {
@@ -37,4 +37,13 @@ protocol TabBarCoordinatorType: Coordinator, SplitViewMasterPresentable {
// TODO: Remove this method, this implementation detail should not be exposed
// Release the current selected item (room/contact/group...).
func releaseSelectedItems()
/// Displays an error using a `UserIndicator`. The messages is dimissed automatically.
func showErroIndicator(with error: Error)
/// Displays an message related to the application state using a `UserIndicator`. The message must be dimissed by calling the method `hideAppStateIndicator()`
func showAppStateIndicator(with text: String, icon: UIImage?)
/// Hide the message related to the application state currently displayed.
func hideAppStateIndicator()
}
-2
View File
@@ -139,8 +139,6 @@
<string>im.vector.app.pills</string>
</dict>
</array>
<key>UserDefaults</key>
<string>${PRODUCT_NAME}-Defaults</string>
<key>applicationGroupIdentifier</key>
<string>$(APPLICATION_GROUP_IDENTIFIER)</string>
<key>baseBundleIdentifier</key>
+1 -1
View File
@@ -34,8 +34,8 @@ targets:
- target: RiotShareExtension
- target: SiriIntents
- target: RiotNSE
- target: DesignKit
- target: CommonKit
- package: DesignKit
- package: Mapbox
- package: OrderedCollections
+2
View File
@@ -30,6 +30,8 @@ targets:
RiotShareExtension:
platform: iOS
type: app-extension
dependencies:
- package: DesignKit
configFiles:
Debug: Debug.xcconfig
@@ -38,7 +38,7 @@ enum MockAuthenticationServerSelectionScreenState: MockScreenState, CaseIterable
let viewModel: AuthenticationServerSelectionViewModel
switch self {
case .matrix:
viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "https://matrix.org",
viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "matrix.org",
hasModalPresentation: true)
case .emptyAddress:
viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "",
@@ -48,7 +48,7 @@ enum MockAuthenticationServerSelectionScreenState: MockScreenState, CaseIterable
hasModalPresentation: true)
Task { await viewModel.displayError(.footerMessage(VectorL10n.errorCommonMessage)) }
case .nonModal:
viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "https://matrix.org",
viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "matrix.org",
hasModalPresentation: false)
}
@@ -43,7 +43,7 @@ class AuthenticationServerSelectionUITests: MockScreenTest {
func verifyNormalState() {
let serverTextField = app.textFields.element
XCTAssertEqual(serverTextField.value as? String, "matrix.org", "The server shown should be matrix.org with the https scheme hidden.")
XCTAssertEqual(serverTextField.value as? String, "matrix.org", "The server shown should be matrix.org as passed to the view model init.")
let confirmButton = app.buttons["confirmButton"]
XCTAssertEqual(confirmButton.label, VectorL10n.confirm, "The confirm button should say Confirm when in modal presentation.")
@@ -17,6 +17,8 @@
import Foundation
import UIKit
// TODO: Move into element-design-tokens repo.
// Figma Avatar Sizes: https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1258%3A19678
public enum AvatarSize: Int {
case xxSmall = 16
@@ -49,7 +49,7 @@ struct AvatarImage: View {
mxContentUri: mxContentUri,
matrixItemId: matrixItemId,
displayName: displayName,
colorCount: theme.colors.namesAndAvatars.count,
colorCount: theme.colors.contentAndAvatars.count,
avatarSize: size
)
}
@@ -36,7 +36,7 @@ struct PlaceholderAvatarImage: View {
var body: some View {
ZStack {
theme.colors.namesAndAvatars[colorIndex]
theme.colors.contentAndAvatars[colorIndex]
Text(String(firstCharacter))
.padding(4)
@@ -38,7 +38,7 @@ struct SpaceAvatarImage: View {
.padding(10)
.frame(width: CGFloat(size.rawValue), height: CGFloat(size.rawValue))
.foregroundColor(.white)
.background(theme.colors.namesAndAvatars[colorIndex])
.background(theme.colors.contentAndAvatars[colorIndex])
.clipShape(RoundedRectangle(cornerRadius: 8))
// Make the text resizable (i.e. Make it large and then allow it to scale down)
.font(.system(size: 200))
@@ -55,7 +55,7 @@ struct SpaceAvatarImage: View {
mxContentUri: mxContentUri,
matrixItemId: matrixItemId,
displayName: value,
colorCount: theme.colors.namesAndAvatars.count,
colorCount: theme.colors.contentAndAvatars.count,
avatarSize: size
)
})
@@ -65,7 +65,7 @@ struct SpaceAvatarImage: View {
mxContentUri: mxContentUri,
matrixItemId: matrixItemId,
displayName: displayName,
colorCount: theme.colors.namesAndAvatars.count,
colorCount: theme.colors.contentAndAvatars.count,
avatarSize: size
)
}
@@ -31,7 +31,7 @@ class EffectsScene: SCNScene {
static func confetti(with theme: ThemeSwiftUI) -> EffectsScene? {
guard let scene = EffectsScene(named: Constants.confettiSceneName) else { return nil }
let colors: [[Float]] = theme.colors.namesAndAvatars.compactMap { $0.floatComponents }
let colors: [[Float]] = theme.colors.contentAndAvatars.compactMap { $0.floatComponents }
if let particles = scene.rootNode.childNode(withName: Constants.particlesNodeName, recursively: false)?.particleSystems?.first {
// The particles need a non-zero color variation for the handler to affect the color
@@ -65,9 +65,12 @@ fileprivate extension Color {
///
/// SceneKit works in a colorspace with a linear gamma, which is why this conversion is necessary.
var floatComponents: [Float]? {
// Get the CGColor from a UIColor as it is nil on Color when loaded from an asset catalog.
let cgColor = UIColor(self).cgColor
guard
let colorSpace = CGColorSpace(name: CGColorSpace.extendedLinearSRGB),
let linearColor = cgColor?.converted(to: colorSpace, intent: .defaultIntent, options: nil),
let linearColor = cgColor.converted(to: colorSpace, intent: .defaultIntent, options: nil),
let components = linearColor.components
else { return nil }
@@ -59,3 +59,9 @@ struct EffectsView: UIViewRepresentable {
}
}
}
struct EffectsView_Previews: PreviewProvider {
static var previews: some View {
EffectsView(effect: .confetti)
}
}
@@ -19,6 +19,7 @@ import Foundation
/// The static list of mocked screens in RiotSwiftUI
enum MockAppScreens {
static let appScreens: [MockScreenState.Type] = [
MockLiveLocationLabPromotionScreenState.self,
MockLiveLocationSharingViewerScreenState.self,
MockAuthenticationLoginScreenState.self,
MockAuthenticationReCaptchaScreenState.self,
@@ -14,10 +14,33 @@
// limitations under the License.
//
import Foundation
import SwiftUI
import DesignKit
import DesignTokens
protocol ThemeSwiftUI: ThemeSwiftUIType {
var identifier: ThemeIdentifier { get }
var isDark: Bool { get }
}
/// Theme v2 for SwiftUI.
@available(iOS 14.0, *)
public protocol ThemeSwiftUIType {
/// Colors object
var colors: ElementColors { get }
/// Fonts object
var fonts: ElementFonts { get }
/// may contain more design components in future, like icons, audio files etc.
}
// MARK: - Legacy Colors
public extension ElementColors {
var legacyTile: Color {
let dynamicColor = UIColor { $0.userInterfaceStyle == .light ? .elementLight.tile : .elementDark.tile }
return Color(dynamicColor)
}
}

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