diff --git a/.gitignore b/.gitignore index 695d4cd613..2530cc0a7c 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme index 84ecb908a5..bc93d67495 100644 --- a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme +++ b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000000..d586862d6d --- /dev/null +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,50 @@ +{ + "pins" : [ + { + "identity" : "element-design-tokens", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vector-im/element-design-tokens.git", + "state" : { + "revision" : "4aafdc25ca0e322c0de930d4ec86121f5503023e", + "version" : "0.0.1" + } + }, + { + "identity" : "element-x-ios", + "kind" : "remoteSourceControl", + "location" : "https://github.com/vector-im/element-x-ios", + "state" : { + "branch" : "develop", + "revision" : "272fc5000bfe6e15a0f0ea669ef3088c7d163ce7" + } + }, + { + "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 +} diff --git a/Riot/Managers/Theme/ElementUIColorsResolved.swift b/Riot/Managers/Theme/ElementUIColorsResolved.swift new file mode 100644 index 0000000000..65c6531a83 --- /dev/null +++ b/Riot/Managers/Theme/ElementUIColorsResolved.swift @@ -0,0 +1,91 @@ +// +// 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.globalAzure.resolvedColor(with: traitCollection), + dynamicColors.globalGrape.resolvedColor(with: traitCollection), + dynamicColors.globalVerde.resolvedColor(with: traitCollection), + dynamicColors.globalPolly.resolvedColor(with: traitCollection), + dynamicColors.globalMelon.resolvedColor(with: traitCollection), + dynamicColors.globalAqua.resolvedColor(with: traitCollection), + dynamicColors.globalPrune.resolvedColor(with: traitCollection), + dynamicColors.globalKiwi.resolvedColor(with: traitCollection) + ] + + // 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() + } +} diff --git a/Riot/Managers/Theme/ThemeV2.swift b/Riot/Managers/Theme/ThemeV2.swift index 1dff3cf196..56a97a93a2 100644 --- a/Riot/Managers/Theme/ThemeV2.swift +++ b/Riot/Managers/Theme/ThemeV2.swift @@ -22,30 +22,10 @@ import DesignTokens @objc public protocol ThemeV2 { /// Colors object - var colors: ElementUIColors { get } + var colors: ElementUIColorsResolved { get } /// Fonts object var fonts: ElementUIFonts { get } /// may contain more design components in future, like icons, audio files etc. } - -#warning("Temporary missing colors") -public extension ElementUIColors { - var quarterlyContent: UIColor { quaternaryContent } - var navigation: UIColor { system } - var tile: UIColor { system } - var separator: UIColor { system } - var namesAndAvatars: [UIColor] { - [ - globalAzure, - globalGrape, - globalVerde, - globalPolly, - globalMelon, - globalAqua, - globalPrune, - globalKiwi - ] - } -} diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index 4facd084c1..388105356c 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -182,7 +182,7 @@ class DarkTheme: NSObject, Theme { } // MARK: - Theme v2 - var colors = UIColor.element + var colors = UIColor.elementDark var fonts = UIFont.element diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index 6145b76a3a..7ec7101c89 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -186,7 +186,7 @@ class DefaultTheme: NSObject, Theme { } // MARK: - Theme v2 - var colors = UIColor.element + var colors = UIColor.elementLight var fonts = UIFont.element } diff --git a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift index 97e5ca5d35..de3b7c7d7d 100644 --- a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift +++ b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift @@ -99,6 +99,9 @@ class VectorHostingController: UIHostingController { } 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) } diff --git a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift index affd35eeae..6c0d193acf 100644 --- a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift +++ b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift @@ -67,9 +67,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 } diff --git a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift index a3ca268f18..9f2caa6cf2 100644 --- a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift +++ b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift @@ -60,3 +60,9 @@ struct EffectsView: UIViewRepresentable { } } } + +struct EffectsView_Previews: PreviewProvider { + static var previews: some View { + EffectsView(effect: .confetti) + } +} diff --git a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift index 1b7f87fa8a..b9c330e628 100644 --- a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift @@ -37,11 +37,14 @@ public protocol ThemeSwiftUIType { /// may contain more design components in future, like icons, audio files etc. } -#warning("Temporary missing colors") public extension ElementColors { - var quarterlyContent: Color { quaternaryContent } - var navigation: Color { system } - var tile: Color { system } + // MARK: - Legacy + var legacyTile: Color { + let dynamicColor = UIColor { $0.userInterfaceStyle == .light ? .elementLight.tile : .elementDark.tile } + return Color(dynamicColor) + } + + // TODO: Generate in DesignTokens repo. var namesAndAvatars: [Color] { [ globalAzure, diff --git a/RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift b/RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift index a932ce1933..de06c54ad6 100644 --- a/RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift +++ b/RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift @@ -51,7 +51,7 @@ struct BorderedInputFieldStyle: TextFieldStyle { if (theme.identifier == ThemeIdentifier.dark) { return (isEnabled ? theme.colors.primaryContent : theme.colors.tertiaryContent) } else { - return (isEnabled ? theme.colors.primaryContent : theme.colors.quarterlyContent) + return (isEnabled ? theme.colors.primaryContent : theme.colors.quaternaryContent) } } diff --git a/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift b/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift index 7eb67d39c6..6ab0832acf 100644 --- a/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift +++ b/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift @@ -47,7 +47,7 @@ struct ClearViewModifier: ViewModifier { }) { Image(systemName: "xmark.circle.fill") .renderingMode(.template) - .foregroundColor(theme.colors.quarterlyContent) + .foregroundColor(theme.colors.quaternaryContent) } .padding(.top, alignment == .top ? 8 : 0) .padding(.bottom, alignment == .bottom ? 8 : 0) diff --git a/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift b/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift index 5c38c63f0f..ab16964ea7 100644 --- a/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift +++ b/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift @@ -57,7 +57,7 @@ struct MultilineTextField: View { return theme.colors.accent } - return theme.colors.quarterlyContent + return theme.colors.quaternaryContent } private var borderWidth: CGFloat { diff --git a/RiotSwiftUI/Modules/Common/Util/OptionButton.swift b/RiotSwiftUI/Modules/Common/Util/OptionButton.swift index 9a4ca7b5fc..aea1c415ce 100644 --- a/RiotSwiftUI/Modules/Common/Util/OptionButton.swift +++ b/RiotSwiftUI/Modules/Common/Util/OptionButton.swift @@ -56,7 +56,7 @@ struct OptionButton: View { } } Spacer() - Image(systemName: "chevron.right").font(.system(size: 16, weight: .regular)).foregroundColor(theme.colors.quarterlyContent) + Image(systemName: "chevron.right").font(.system(size: 16, weight: .regular)).foregroundColor(theme.colors.quaternaryContent) } .padding(EdgeInsets(top: 15, leading: 16, bottom: 15, trailing: 16)) .background(theme.colors.quinaryContent) diff --git a/RiotSwiftUI/Modules/Common/Util/SearchBar.swift b/RiotSwiftUI/Modules/Common/Util/SearchBar.swift index f498c24e6e..68ea0cde1e 100644 --- a/RiotSwiftUI/Modules/Common/Util/SearchBar.swift +++ b/RiotSwiftUI/Modules/Common/Util/SearchBar.swift @@ -39,7 +39,7 @@ struct SearchBar: View { } .padding(8) .padding(.horizontal, 25) - .background(theme.colors.navigation) + .background(theme.colors.system) .cornerRadius(8) .padding(.leading) .padding(.trailing, isEditing ? 8 : 16) @@ -47,7 +47,7 @@ struct SearchBar: View { HStack { Image(systemName: "magnifyingglass") .renderingMode(.template) - .foregroundColor(theme.colors.quarterlyContent) + .foregroundColor(theme.colors.quaternaryContent) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) if isEditing && !text.isEmpty { @@ -56,7 +56,7 @@ struct SearchBar: View { }) { Image(systemName: "multiply.circle.fill") .renderingMode(.template) - .foregroundColor(theme.colors.quarterlyContent) + .foregroundColor(theme.colors.quaternaryContent) } } } diff --git a/RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift b/RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift index 2d081dd2de..c2669a22c1 100644 --- a/RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift +++ b/RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift @@ -70,7 +70,7 @@ struct SecondaryActionButtonStyle_Previews: PreviewProvider { Text("Custom") .foregroundColor(theme.colors.secondaryContent) } - .buttonStyle(SecondaryActionButtonStyle(customColor: theme.colors.quarterlyContent)) + .buttonStyle(SecondaryActionButtonStyle(customColor: theme.colors.quaternaryContent)) } .padding() } diff --git a/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift b/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift index b5cd70d1f7..054c0e2a71 100644 --- a/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift +++ b/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift @@ -93,7 +93,7 @@ struct WaitOverlay: ViewModifier { } .padding(12) .background(RoundedRectangle(cornerRadius: 8, style: .continuous) - .fill(theme.colors.navigation.opacity(0.9))) + .fill(theme.colors.system.opacity(0.9))) } .edgesIgnoringSafeArea(.all) .transition(.opacity) diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift index 63e8507a0b..f2306eaf4d 100644 --- a/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift @@ -47,7 +47,7 @@ struct OnboardingSplashScreenPageIndicator: View { ForEach(0..