mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-24 00:16:39 +02:00
Merge pull request #6414 from vector-im/release/1.8.21/release
Release 1.8.21
This commit is contained in:
+7
-1
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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) })
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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 %@:";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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" = "スペースの作成を停止しますか?";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -6,3 +6,4 @@
|
||||
"NSCalendarsUsageDescription" = "Просматривайте запланированные встречи в приложении.";
|
||||
"NSFaceIDUsageDescription" = "Face ID используется для доступа к вашему приложению.";
|
||||
"NSLocationWhenInUseUsageDescription" = "Когда вы делитесь с людьми своим местоположением, Element необходим доступ, чтобы показать им карту.";
|
||||
"NSLocationAlwaysAndWhenInUseUsageDescription" = "Когда вы сообщаете людям свое местоположение, Element будет необходим доступ, чтобы показать им карту.";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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" = "Записати голосове повідомлення";
|
||||
|
||||
@@ -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
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
+7
-1
@@ -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];
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
+5
-2
@@ -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)
|
||||
}
|
||||
|
||||
+9
-2
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+35
-22
@@ -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>
|
||||
|
||||
+75
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -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
|
||||
}
|
||||
}
|
||||
+25
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
@@ -34,8 +34,8 @@ targets:
|
||||
- target: RiotShareExtension
|
||||
- target: SiriIntents
|
||||
- target: RiotNSE
|
||||
- target: DesignKit
|
||||
- target: CommonKit
|
||||
- package: DesignKit
|
||||
- package: Mapbox
|
||||
- package: OrderedCollections
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ targets:
|
||||
RiotShareExtension:
|
||||
platform: iOS
|
||||
type: app-extension
|
||||
dependencies:
|
||||
- package: DesignKit
|
||||
|
||||
configFiles:
|
||||
Debug: Debug.xcconfig
|
||||
|
||||
+2
-2
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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.")
|
||||
|
||||
+2
@@ -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
Reference in New Issue
Block a user