mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-18 07:28:28 +02:00
Extract and start to split theme.
This commit is contained in:
@@ -16,53 +16,60 @@
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
public protocol DesignKitColorType { }
|
||||
|
||||
extension UIColor: DesignKitColorType { }
|
||||
|
||||
extension Color : DesignKitColorType { }
|
||||
|
||||
/// Colors at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1255%3A1104
|
||||
@objc public protocol Colors {
|
||||
public protocol Colors {
|
||||
|
||||
/// - Focused/Active states
|
||||
/// - CTAs
|
||||
var accent: UIColor { get }
|
||||
var accent: DesignKitColorType { get }
|
||||
|
||||
/// - Error messages
|
||||
/// - Content requiring user attention
|
||||
/// - Notification, alerts
|
||||
var alert: UIColor { get }
|
||||
var alert: DesignKitColorType { get }
|
||||
|
||||
/// - Text
|
||||
/// - Icons
|
||||
var primaryContent: UIColor { get }
|
||||
var primaryContent: DesignKitColorType { get }
|
||||
|
||||
/// - Text
|
||||
/// - Icons
|
||||
var secondaryContent: UIColor { get }
|
||||
var secondaryContent: DesignKitColorType { get }
|
||||
|
||||
/// - Text
|
||||
/// - Icons
|
||||
var tertiaryContent: UIColor { get }
|
||||
var tertiaryContent: DesignKitColorType { get }
|
||||
|
||||
/// - Text
|
||||
/// - Icons
|
||||
var quarterlyContent: UIColor { get }
|
||||
var quarterlyContent: DesignKitColorType { get }
|
||||
|
||||
/// - Text
|
||||
/// - Icons
|
||||
var quinaryContent: UIColor { get }
|
||||
var quinaryContent: DesignKitColorType { get }
|
||||
|
||||
/// Separating line
|
||||
var separator: UIColor { get }
|
||||
var separator: DesignKitColorType { get }
|
||||
|
||||
// Cards, tiles
|
||||
var tile: UIColor { get }
|
||||
var tile: DesignKitColorType { get }
|
||||
|
||||
/// Top navigation background on iOS
|
||||
var navigation: UIColor { get }
|
||||
var navigation: DesignKitColorType { get }
|
||||
|
||||
/// Background UI color
|
||||
var background: UIColor { get }
|
||||
var background: DesignKitColorType { get }
|
||||
|
||||
/// - Names in chat timeline
|
||||
/// - Avatars default states that include first name letter
|
||||
var namesAndAvatars: [UIColor] { get }
|
||||
var namesAndAvatars: [DesignKitColorType] { get }
|
||||
|
||||
}
|
||||
|
||||
@@ -15,69 +15,77 @@
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
public protocol DesignKitFontType { }
|
||||
|
||||
extension UIFont: DesignKitFontType { }
|
||||
|
||||
extension Font : DesignKitFontType { }
|
||||
|
||||
|
||||
/// 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
|
||||
@objc public protocol Fonts {
|
||||
public protocol Fonts {
|
||||
|
||||
/// The font for large titles.
|
||||
var largeTitle: UIFont { get }
|
||||
var largeTitle: DesignKitFontType { get }
|
||||
|
||||
/// `largeTitle` with a Bold weight.
|
||||
var largeTitleB: UIFont { get }
|
||||
var largeTitleB: DesignKitFontType { get }
|
||||
|
||||
/// The font for first-level hierarchical headings.
|
||||
var title1: UIFont { get }
|
||||
var title1: DesignKitFontType { get }
|
||||
|
||||
/// `title1` with a Bold weight.
|
||||
var title1B: UIFont { get }
|
||||
var title1B: DesignKitFontType { get }
|
||||
|
||||
/// The font for second-level hierarchical headings.
|
||||
var title2: UIFont { get }
|
||||
var title2: DesignKitFontType { get }
|
||||
|
||||
/// `title2` with a Bold weight.
|
||||
var title2B: UIFont { get }
|
||||
var title2B: DesignKitFontType { get }
|
||||
|
||||
/// The font for third-level hierarchical headings.
|
||||
var title3: UIFont { get }
|
||||
var title3: DesignKitFontType { get }
|
||||
|
||||
/// `title3` with a Semi Bold weight.
|
||||
var title3SB: UIFont { get }
|
||||
var title3SB: DesignKitFontType { get }
|
||||
|
||||
/// The font for headings.
|
||||
var headline: UIFont { get }
|
||||
var headline: DesignKitFontType { get }
|
||||
|
||||
/// The font for subheadings.
|
||||
var subheadline: UIFont { get }
|
||||
var subheadline: DesignKitFontType { get }
|
||||
|
||||
/// The font for body text.
|
||||
var body: UIFont { get }
|
||||
var body: DesignKitFontType { get }
|
||||
|
||||
/// `body` with a Semi Bold weight.
|
||||
var bodySB: UIFont { get }
|
||||
var bodySB: DesignKitFontType { get }
|
||||
|
||||
/// The font for callouts.
|
||||
var callout: UIFont { get }
|
||||
var callout: DesignKitFontType { get }
|
||||
|
||||
/// `callout` with a Semi Bold weight.
|
||||
var calloutSB: UIFont { get }
|
||||
var calloutSB: DesignKitFontType { get }
|
||||
|
||||
/// The font for footnotes.
|
||||
var footnote: UIFont { get }
|
||||
var footnote: DesignKitFontType { get }
|
||||
|
||||
/// `footnote` with a Semi Bold weight.
|
||||
var footnoteSB: UIFont { get }
|
||||
var footnoteSB: DesignKitFontType { get }
|
||||
|
||||
/// The font for standard captions.
|
||||
var caption1: UIFont { get }
|
||||
var caption1: DesignKitFontType { get }
|
||||
|
||||
/// `caption1` with a Semi Bold weight.
|
||||
var caption1SB: UIFont { get }
|
||||
var caption1SB: DesignKitFontType { get }
|
||||
|
||||
/// The font for alternate captions.
|
||||
var caption2: UIFont { get }
|
||||
var caption2: DesignKitFontType { get }
|
||||
|
||||
/// `caption2` with a Semi Bold weight.
|
||||
var caption2SB: UIFont { get }
|
||||
var caption2SB: DesignKitFontType { get }
|
||||
}
|
||||
|
||||
@@ -5143,13 +5143,13 @@ extension VectorL10n {
|
||||
static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
|
||||
let format = NSLocalizedString(key, tableName: table, bundle: Bundle(for: BundleToken.self), comment: "")
|
||||
let locale: Locale
|
||||
if let localeIdentifier = Bundle.mxk_language() {
|
||||
locale = Locale(identifier: localeIdentifier)
|
||||
} else if let fallbackLocaleIdentifier = Bundle.mxk_fallbackLanguage() {
|
||||
locale = Locale(identifier: fallbackLocaleIdentifier)
|
||||
} else {
|
||||
// if let localeIdentifier = Bundle.mxk_language() {
|
||||
// locale = Locale(identifier: localeIdentifier)
|
||||
// } else if let fallbackLocaleIdentifier = Bundle.mxk_fallbackLanguage() {
|
||||
// locale = Locale(identifier: fallbackLocaleIdentifier)
|
||||
// } else {
|
||||
locale = Locale.current
|
||||
}
|
||||
// }
|
||||
|
||||
return String(format: format, locale: locale, arguments: args)
|
||||
}
|
||||
|
||||
22
Riot/Modules/Common/Avatar/AvatarInputOption.swift
Normal file
22
Riot/Modules/Common/Avatar/AvatarInputOption.swift
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
enum AvatarInputOption {
|
||||
case swiftUI(AvatarInputType)
|
||||
case uiKit(AvatarViewDataProtocol)
|
||||
}
|
||||
@@ -19,14 +19,6 @@ import MatrixSDK
|
||||
import Combine
|
||||
import DesignKit
|
||||
|
||||
/**
|
||||
Provides a simple api to retrieve and cache avatar images
|
||||
*/
|
||||
protocol AvatarServiceType {
|
||||
@available(iOS 14.0, *)
|
||||
func avatarImage(mxContentUri: String, avatarSize: AvatarSize) -> Future<UIImage, Error>
|
||||
}
|
||||
|
||||
enum AvatarServiceError: Error {
|
||||
case pathNotfound
|
||||
case loadingImageFailed(Error?)
|
||||
@@ -17,7 +17,7 @@
|
||||
import Foundation
|
||||
|
||||
/// AvatarViewDataProtocol describe a view data that should be given to an AvatarView sublcass
|
||||
protocol AvatarViewDataProtocol {
|
||||
protocol AvatarViewDataProtocol: AvatarType {
|
||||
/// Matrix item identifier (user id or room id)
|
||||
var matrixItemId: String { get }
|
||||
|
||||
|
||||
84
Riot/Modules/Settings/Notifications/MXPushRuleMatching.swift
Normal file
84
Riot/Modules/Settings/Notifications/MXPushRuleMatching.swift
Normal file
@@ -0,0 +1,84 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
|
||||
fileprivate extension MXPushRule {
|
||||
|
||||
/*
|
||||
Given a rule, check it match the actions in the static definition.
|
||||
*/
|
||||
private func maches(targetRule: NotificationStandardActions?) -> Bool {
|
||||
guard let targetRule = targetRule else {
|
||||
return false
|
||||
}
|
||||
if !enabled && targetRule == .disabled {
|
||||
return true
|
||||
}
|
||||
|
||||
if enabled,
|
||||
let actions = targetRule.actions,
|
||||
highlight == actions.highlight,
|
||||
sound == actions.sound,
|
||||
notify == actions.notify,
|
||||
dontNotify == !actions.notify {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getAction(actionType: MXPushRuleActionType, tweakType: String? = nil) -> MXPushRuleAction? {
|
||||
guard let actions = actions as? [MXPushRuleAction] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return actions.first { action in
|
||||
var match = action.actionType == actionType
|
||||
MXLog.debug("action \(action)")
|
||||
if let tweakType = tweakType,
|
||||
let actionTweak = action.parameters?["set_tweak"] as? String {
|
||||
match = match && (tweakType == actionTweak)
|
||||
}
|
||||
return match
|
||||
}
|
||||
}
|
||||
|
||||
var highlight: Bool {
|
||||
guard let action = getAction(actionType: MXPushRuleActionTypeSetTweak, tweakType: "highlight") else {
|
||||
return false
|
||||
}
|
||||
if let highlight = action.parameters["value"] as? Bool {
|
||||
return highlight
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var sound: String? {
|
||||
guard let action = getAction(actionType: MXPushRuleActionTypeSetTweak, tweakType: "sound") else {
|
||||
return nil
|
||||
}
|
||||
return action.parameters["value"] as? String
|
||||
}
|
||||
|
||||
var notify: Bool {
|
||||
return getAction(actionType: MXPushRuleActionTypeNotify) != nil
|
||||
}
|
||||
|
||||
var dontNotify: Bool {
|
||||
return getAction(actionType: MXPushRuleActionTypeDontNotify) != nil
|
||||
}
|
||||
}
|
||||
@@ -17,45 +17,6 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
/**
|
||||
A service for changing notification settings and keywords
|
||||
*/
|
||||
@available(iOS 14.0, *)
|
||||
protocol NotificationSettingsServiceType {
|
||||
/**
|
||||
Publisher of all push rules.
|
||||
*/
|
||||
var rulesPublisher: AnyPublisher<[MXPushRule], Never> { get }
|
||||
/**
|
||||
Publisher of content rules.
|
||||
*/
|
||||
var contentRulesPublisher: AnyPublisher<[MXPushRule], Never> { get }
|
||||
/**
|
||||
Adds a keyword.
|
||||
|
||||
- Parameters:
|
||||
- keyword: The keyword to add.
|
||||
- enabled: Whether the keyword should be added in the enabled or disabled state.
|
||||
*/
|
||||
func add(keyword: String, enabled: Bool)
|
||||
/**
|
||||
Removes a keyword.
|
||||
|
||||
- Parameters:
|
||||
- keyword: The keyword to remove.
|
||||
*/
|
||||
func remove(keyword: String)
|
||||
/**
|
||||
Updates the push rule actions.
|
||||
|
||||
- Parameters:
|
||||
- ruleId: The id of the rule.
|
||||
- enabled: Whether the rule should be enabled or disabled.
|
||||
- actions: The actions to update with.
|
||||
*/
|
||||
func updatePushRuleActions(for ruleId: String, enabled: Bool, actions: NotificationActions?)
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
class NotificationSettingsService: NotificationSettingsServiceType {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import DesignKit
|
||||
import UIKit
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
class MockAvatarService: AvatarServiceType {
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol AvatarInputType {
|
||||
protocol AvatarInputType: AvatarType {
|
||||
var mxContentUri: String? { get }
|
||||
var matrixItemId: String { get }
|
||||
var displayName: String? { get }
|
||||
@@ -27,8 +27,3 @@ struct AvatarInput: AvatarInputType {
|
||||
var matrixItemId: String
|
||||
let displayName: String?
|
||||
}
|
||||
|
||||
enum AvatarInputOption {
|
||||
case swiftUI(AvatarInputType)
|
||||
case uiKit(AvatarViewDataProtocol)
|
||||
}
|
||||
19
Riot/ModulesSwiftUI/Common/Avatar/Model/AvatarType.swift
Normal file
19
Riot/ModulesSwiftUI/Common/Avatar/Model/AvatarType.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
protocol AvatarType { }
|
||||
@@ -29,3 +29,8 @@ class ThemeObserver: ObservableObject {
|
||||
}
|
||||
@Published var theme: Theme = ThemeService.shared().theme
|
||||
}
|
||||
|
||||
|
||||
class ThemePublisher: ObservableObject {
|
||||
static let shared = ThemePublisher()
|
||||
}
|
||||
28
Riot/ModulesSwiftUI/Common/ViewModel/AvatarServiceType.swift
Normal file
28
Riot/ModulesSwiftUI/Common/ViewModel/AvatarServiceType.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright 2021 New Vector Ltd
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import DesignKit
|
||||
import Combine
|
||||
import UIKit
|
||||
|
||||
/**
|
||||
Provides a simple api to retrieve and cache avatar images
|
||||
*/
|
||||
protocol AvatarServiceType {
|
||||
@available(iOS 14.0, *)
|
||||
func avatarImage(mxContentUri: String, avatarSize: AvatarSize) -> Future<UIImage, Error>
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class AvatarViewModel: InjectableObject, ObservableObject {
|
||||
avatarService.avatarImage(mxContentUri: mxContentUri, avatarSize: avatarSize)
|
||||
.sink { completion in
|
||||
guard case let .failure(error) = completion else { return }
|
||||
MXLog.error("[AvatarService] Failed to retrieve avatar: \(error)")
|
||||
// MXLog.error("[AvatarService] Failed to retrieve avatar: \(error)")
|
||||
// TODO: Report non-fatal error when we have Sentry or similar.
|
||||
} receiveValue: { image in
|
||||
self.viewState = .avatar(image)
|
||||
@@ -15,7 +15,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import UIKit
|
||||
enum AvatarViewState {
|
||||
case empty
|
||||
case placeholder(String, Int)
|
||||
@@ -75,7 +75,7 @@ struct RoomNotificationSettings_Previews: PreviewProvider {
|
||||
|
||||
static let mockViewModel = RoomNotificationSettingsSwiftUIViewModel(
|
||||
roomNotificationService: MockRoomNotificationSettingsService.example,
|
||||
avatarData: .swiftUI(MockAvatarInput.example),
|
||||
avatarData: MockAvatarInput.example,
|
||||
displayName: MockAvatarInput.example.displayName,
|
||||
roomEncrypted: true
|
||||
)
|
||||
@@ -55,7 +55,7 @@ class RoomNotificationSettingsViewModel: RoomNotificationSettingsViewModelType {
|
||||
|
||||
convenience init(
|
||||
roomNotificationService: RoomNotificationSettingsServiceType,
|
||||
avatarData: AvatarInputOption?,
|
||||
avatarData: AvatarType?,
|
||||
displayName: String?,
|
||||
roomEncrypted: Bool
|
||||
) {
|
||||
@@ -23,7 +23,7 @@ struct RoomNotificationSettingsViewState: RoomNotificationSettingsViewStateType
|
||||
let roomEncrypted: Bool
|
||||
var saving: Bool
|
||||
var notificationState: RoomNotificationState
|
||||
var avatarData: AvatarInputOption?
|
||||
var avatarData: AvatarType?
|
||||
var displayName: String?
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ protocol RoomNotificationSettingsViewStateType {
|
||||
var roomEncrypted: Bool { get }
|
||||
var notificationOptions: [RoomNotificationState] { get }
|
||||
var notificationState: RoomNotificationState { get }
|
||||
var avatarData: AvatarInputOption? { get }
|
||||
var avatarData: AvatarType? { get }
|
||||
var displayName: String? { get }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
struct MockNotificationPushRule: NotificationPushRule {
|
||||
var ruleId: String
|
||||
var enabled: Bool
|
||||
func matches(standardActions: NotificationStandardActions?) -> Bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -22,10 +22,10 @@ class MockNotificationSettingsService: NotificationSettingsServiceType, Observab
|
||||
static let example = MockNotificationSettingsService()
|
||||
|
||||
@Published var keywords = Set<String>()
|
||||
@Published var rules = [MXPushRule]()
|
||||
@Published var contentRules = [MXPushRule]()
|
||||
@Published var rules = [NotificationPushRule]()
|
||||
@Published var contentRules = [NotificationPushRule]()
|
||||
|
||||
var contentRulesPublisher: AnyPublisher<[MXPushRule], Never> {
|
||||
var contentRulesPublisher: AnyPublisher<[NotificationPushRule], Never> {
|
||||
$contentRules.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ class MockNotificationSettingsService: NotificationSettingsServiceType, Observab
|
||||
$keywords.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
var rulesPublisher: AnyPublisher<[MXPushRule], Never> {
|
||||
var rulesPublisher: AnyPublisher<[NotificationPushRule], Never> {
|
||||
$rules.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
protocol NotificationPushRule {
|
||||
var ruleId: String { get }
|
||||
var enabled: Bool { get }
|
||||
func matches(standardActions: NotificationStandardActions?) -> Bool
|
||||
}
|
||||
@@ -56,7 +56,7 @@ struct BorderedInputFieldStyle: TextFieldStyle {
|
||||
}
|
||||
|
||||
private var backgroundColor: Color {
|
||||
if !isEnabled && (theme is DarkTheme) {
|
||||
if !isEnabled && (theme.identifier == ThemeIdentifier.dark.rawValue) {
|
||||
return Color(theme.colors.quinaryContent)
|
||||
}
|
||||
return Color(theme.colors.background)
|
||||
@@ -34,7 +34,7 @@ struct FormInputFieldStyle: TextFieldStyle {
|
||||
}
|
||||
|
||||
private var backgroundColor: Color {
|
||||
if !isEnabled && (theme is DarkTheme) {
|
||||
if !isEnabled && (theme.identifier == ThemeIdentifier.dark.rawValue) {
|
||||
return Color(theme.colors.quinaryContent)
|
||||
}
|
||||
return Color(theme.colors.background)
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// 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 Combine
|
||||
|
||||
/**
|
||||
A service for changing notification settings and keywords
|
||||
*/
|
||||
@available(iOS 14.0, *)
|
||||
protocol NotificationSettingsServiceType {
|
||||
/**
|
||||
Publisher of all push rules.
|
||||
*/
|
||||
var rulesPublisher: AnyPublisher<[NotificationPushRule], Never> { get }
|
||||
/**
|
||||
Publisher of content rules.
|
||||
*/
|
||||
var contentRulesPublisher: AnyPublisher<[NotificationPushRule], Never> { get }
|
||||
/**
|
||||
Adds a keyword.
|
||||
|
||||
- Parameters:
|
||||
- keyword: The keyword to add.
|
||||
- enabled: Whether the keyword should be added in the enabled or disabled state.
|
||||
*/
|
||||
func add(keyword: String, enabled: Bool)
|
||||
/**
|
||||
Removes a keyword.
|
||||
|
||||
- Parameters:
|
||||
- keyword: The keyword to remove.
|
||||
*/
|
||||
func remove(keyword: String)
|
||||
/**
|
||||
Updates the push rule actions.
|
||||
|
||||
- Parameters:
|
||||
- ruleId: The id of the rule.
|
||||
- enabled: Whether the rule should be enabled or disabled.
|
||||
- actions: The actions to update with.
|
||||
*/
|
||||
func updatePushRuleActions(for ruleId: String, enabled: Bool, actions: NotificationActions?)
|
||||
}
|
||||
@@ -155,7 +155,7 @@ final class NotificationSettingsViewModel: NotificationSettingsViewModelType, Ob
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
private func rulesUpdated(newRules: [MXPushRule]) {
|
||||
private func rulesUpdated(newRules: [NotificationPushRule]) {
|
||||
for rule in newRules {
|
||||
guard let ruleId = NotificationPushRuleId(rawValue: rule.ruleId),
|
||||
ruleIds.contains(ruleId) else { continue }
|
||||
@@ -174,11 +174,11 @@ final class NotificationSettingsViewModel: NotificationSettingsViewModelType, Ob
|
||||
Matcing is done by comparing the rule against the static definitions for that rule.
|
||||
The same logic is used on android.
|
||||
*/
|
||||
private func isChecked(rule: MXPushRule) -> Bool {
|
||||
private func isChecked(rule: NotificationPushRule) -> Bool {
|
||||
guard let ruleId = NotificationPushRuleId(rawValue: rule.ruleId) else { return false }
|
||||
|
||||
let firstIndex = NotificationIndex.allCases.first { nextIndex in
|
||||
return ruleMaches(rule: rule, targetRule: ruleId.standardActions(for: nextIndex))
|
||||
return rule.matches(standardActions: ruleId.standardActions(for: nextIndex))
|
||||
}
|
||||
|
||||
guard let index = firstIndex else {
|
||||
@@ -187,69 +187,5 @@ final class NotificationSettingsViewModel: NotificationSettingsViewModelType, Ob
|
||||
|
||||
return index.enabled
|
||||
}
|
||||
/*
|
||||
Given a rule, check it match the actions in the static definition.
|
||||
*/
|
||||
private func ruleMaches(rule: MXPushRule, targetRule: NotificationStandardActions?) -> Bool {
|
||||
guard let targetRule = targetRule else {
|
||||
return false
|
||||
}
|
||||
if !rule.enabled && targetRule == .disabled {
|
||||
return true
|
||||
}
|
||||
|
||||
if rule.enabled,
|
||||
let actions = targetRule.actions,
|
||||
rule.highlight == actions.highlight,
|
||||
rule.sound == actions.sound,
|
||||
rule.notify == actions.notify,
|
||||
rule.dontNotify == !actions.notify {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate extension MXPushRule {
|
||||
func getAction(actionType: MXPushRuleActionType, tweakType: String? = nil) -> MXPushRuleAction? {
|
||||
guard let actions = actions as? [MXPushRuleAction] else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return actions.first { action in
|
||||
var match = action.actionType == actionType
|
||||
MXLog.debug("action \(action)")
|
||||
if let tweakType = tweakType,
|
||||
let actionTweak = action.parameters?["set_tweak"] as? String {
|
||||
match = match && (tweakType == actionTweak)
|
||||
}
|
||||
return match
|
||||
}
|
||||
}
|
||||
|
||||
var highlight: Bool {
|
||||
guard let action = getAction(actionType: MXPushRuleActionTypeSetTweak, tweakType: "highlight") else {
|
||||
return false
|
||||
}
|
||||
if let highlight = action.parameters["value"] as? Bool {
|
||||
return highlight
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var sound: String? {
|
||||
guard let action = getAction(actionType: MXPushRuleActionTypeSetTweak, tweakType: "sound") else {
|
||||
return nil
|
||||
}
|
||||
return action.parameters["value"] as? String
|
||||
}
|
||||
|
||||
var notify: Bool {
|
||||
return getAction(actionType: MXPushRuleActionTypeNotify) != nil
|
||||
}
|
||||
|
||||
var dontNotify: Bool {
|
||||
return getAction(actionType: MXPushRuleActionTypeDontNotify) != nil
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,18 @@ schemes:
|
||||
- RiotTests
|
||||
|
||||
targets:
|
||||
RiotSwiftUI:
|
||||
type: framework
|
||||
platform: iOS
|
||||
dependencies:
|
||||
- target: DesignKit
|
||||
sources:
|
||||
- path: ModulesSwiftUI
|
||||
- path: Generated/Strings.swift
|
||||
- path: Generated/Images.swift
|
||||
- path: Managers/Theme/Theme.swift
|
||||
- path: Managers/Theme/ThemeIdentifier.swift
|
||||
- path: Categories/UIColor.swift
|
||||
Riot:
|
||||
type: application
|
||||
platform: iOS
|
||||
@@ -35,6 +47,7 @@ targets:
|
||||
- target: SiriIntents
|
||||
- target: RiotNSE
|
||||
- target: DesignKit
|
||||
- target: RiotSwiftUI
|
||||
|
||||
configFiles:
|
||||
Debug: Debug.xcconfig
|
||||
|
||||
Reference in New Issue
Block a user