diff --git a/WordPress/Classes/Extensions/Colors and Styles/MurielColor.swift b/WordPress/Classes/Extensions/Colors and Styles/MurielColor.swift index 9ec538d8ea10..7bfc3b35c5a0 100644 --- a/WordPress/Classes/Extensions/Colors and Styles/MurielColor.swift +++ b/WordPress/Classes/Extensions/Colors and Styles/MurielColor.swift @@ -12,6 +12,7 @@ enum MurielColorName: String, CustomStringConvertible { case red case yellow case jetpackGreen + case wooCommercePurple var description: String { // can't use .capitalized because it lowercases the P and B in "wordPressBlue" @@ -58,12 +59,9 @@ struct MurielColor { } // MARK: - Muriel's semantic colors - static let accent = AppStyleGuide.accent - static let brand = AppStyleGuide.brand static let divider = AppStyleGuide.divider static let error = AppStyleGuide.error static let gray = AppStyleGuide.gray - static let primary = AppStyleGuide.primary static let success = AppStyleGuide.success static let text = AppStyleGuide.text static let textSubtle = AppStyleGuide.textSubtle diff --git a/WordPress/Classes/Extensions/Colors and Styles/UIColor+MurielColors.swift b/WordPress/Classes/Extensions/Colors and Styles/UIColor+MurielColors.swift index e650c89b521a..4bfc6ec4ee66 100644 --- a/WordPress/Classes/Extensions/Colors and Styles/UIColor+MurielColors.swift +++ b/WordPress/Classes/Extensions/Colors and Styles/UIColor+MurielColors.swift @@ -32,19 +32,38 @@ extension UIColor { return muriel(color: newColor) } } + // MARK: - Basic Colors extension UIColor { + + /// - Note: We decided to replace `primary`, `accent` and `brand` colors with user customizable `AppColor.accentColor`, + /// however long term goal here is also to unify these 3 colors into a single `appAccent` color. + + /// Muriel primary color + static var primary: UIColor { + UIColor(AppColor.accentColor) + } + class func primary(_ shade: MurielColorShade) -> UIColor { + UIColor(AppColor.accentColor(shade)) + } + static var primaryLight: UIColor { + primary(.shade30) + } + static var primaryDark: UIColor { + primary(.shade70) + } + /// Muriel accent color - static var accent = muriel(color: .accent) - static var accentDark = muriel(color: .accent, .shade70) + static var accent: UIColor { + UIColor(AppColor.accentColor) + } class func accent(_ shade: MurielColorShade) -> UIColor { - return muriel(color: .accent, shade) + UIColor(AppColor.accentColor(shade)) } /// Muriel brand color - static var brand = muriel(color: .brand) - class func brand(_ shade: MurielColorShade) -> UIColor { - return muriel(color: .brand, shade) + static var brand: UIColor { + UIColor(AppColor.accentColor) } /// Muriel error color @@ -54,14 +73,6 @@ extension UIColor { return muriel(color: .error, shade) } - /// Muriel primary color - static var primary = muriel(color: .primary) - static var primaryLight = muriel(color: .primary, .shade30) - static var primaryDark = muriel(color: .primary, .shade70) - class func primary(_ shade: MurielColorShade) -> UIColor { - return muriel(color: .primary, shade) - } - /// Muriel editor primary color static var editorPrimary = muriel(color: .editorPrimary) class func editorPrimary(_ shade: MurielColorShade) -> UIColor { @@ -239,12 +250,18 @@ extension UIColor { } static var barButtonItemTitle: UIColor { - return UIColor(light: UIColor.primary(.shade50), dark: UIColor.primary(.shade30)) + return UIColor(light: primary(.shade50), dark: primary(.shade30)) } -// MARK: - WP Fancy Buttons - static var primaryButtonBackground = primary - static var primaryButtonDownBackground = muriel(color: .primary, .shade80) + // MARK: - WP Fancy Buttons + + static var primaryButtonBackground: UIColor { + primary + } + + static var primaryButtonDownBackground: UIColor { + primary(.shade80) + } static var secondaryButtonBackground: UIColor { return UIColor(light: .white, dark: .systemGray5) diff --git a/WordPress/Classes/System/WordPressAppDelegate.swift b/WordPress/Classes/System/WordPressAppDelegate.swift index 22df1d5a5285..0e4f6361ceea 100644 --- a/WordPress/Classes/System/WordPressAppDelegate.swift +++ b/WordPress/Classes/System/WordPressAppDelegate.swift @@ -138,7 +138,6 @@ class WordPressAppDelegate: UIResponder, UIApplicationDelegate { InteractiveNotificationsManager.shared.registerForUserNotifications() setupPingHub() setupBackgroundRefresh(application) - setupComponentsAppearance() disableAnimationsForUITests(application) // This was necessary to properly load fonts for the Stories editor. I believe external libraries may require this call to access fonts. @@ -951,6 +950,7 @@ extension WordPressAppDelegate { /// It's necessary to target `PostCategoriesViewController` a second time to "reset" the UI element's `tintColor` for use in the app's Site Settings screen. UIView.appearance(whenContainedInInstancesOf: [PostCategoriesViewController.self, WPSplitViewController.self]).tintColor = .primary + setupComponentsAppearance() } } diff --git a/WordPress/Classes/Utility/App Configuration/AppStyleGuide.swift b/WordPress/Classes/Utility/App Configuration/AppStyleGuide.swift index a52e9f8c6760..b832d3de3f92 100644 --- a/WordPress/Classes/Utility/App Configuration/AppStyleGuide.swift +++ b/WordPress/Classes/Utility/App Configuration/AppStyleGuide.swift @@ -13,12 +13,9 @@ struct AppStyleGuide { // MARK: - Colors extension AppStyleGuide { - static let accent = MurielColor(name: .pink) - static let brand = MurielColor(name: .wordPressBlue) static let divider = MurielColor(name: .gray, shade: .shade10) static let error = MurielColor(name: .red) static let gray = MurielColor(name: .gray) - static let primary = MurielColor(name: .blue) static let success = MurielColor(name: .green) static let text = MurielColor(name: .gray, shade: .shade80) static let textSubtle = MurielColor(name: .gray, shade: .shade50) diff --git a/WordPress/Classes/Utility/WPImmuTableRows.swift b/WordPress/Classes/Utility/WPImmuTableRows.swift index 25bf3ea1ab98..306a2b016304 100644 --- a/WordPress/Classes/Utility/WPImmuTableRows.swift +++ b/WordPress/Classes/Utility/WPImmuTableRows.swift @@ -2,7 +2,6 @@ import Foundation import WordPressShared import Gridicons - struct NavigationItemRow: ImmuTableRow { static let cell = ImmuTableCell.class(WPTableViewCellValue1.self) diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Prompts/DashboardPromptsCardCell.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Prompts/DashboardPromptsCardCell.swift index 26f1513a987b..f20ea75a95ad 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Prompts/DashboardPromptsCardCell.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Prompts/DashboardPromptsCardCell.swift @@ -326,12 +326,19 @@ class DashboardPromptsCardCell: UICollectionViewCell, Reusable { super.init(frame: frame) setupViews() observeManagedObjectsChange() + + NotificationCenter.default + .addObserver(self, selector: #selector(updateAppearance), name: .appColorDidUpdateAccent, object: nil) } required init?(coder: NSCoder) { super.init(coder: coder) } + deinit { + NotificationCenter.default.removeObserver(self) + } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { // refresh when the appearance style changed so the placeholder images are correctly recolored. if let previousTraitCollection = previousTraitCollection, @@ -417,6 +424,11 @@ private extension DashboardPromptsCardCell { presenterViewController?.collectionView.collectionViewLayout.invalidateLayout() } + @objc + private func updateAppearance() { + answerButton.setTitleColor(WPStyleGuide.BloggingPrompts.buttonTitleColor, for: .normal) + } + // MARK: - Managed object observer func observeManagedObjectsChange() { diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Stats/DashboardStatsNudgeView.swift b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Stats/DashboardStatsNudgeView.swift index a9e40200d3f4..ddabae8e0a31 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Stats/DashboardStatsNudgeView.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Dashboard/Cards/Stats/DashboardStatsNudgeView.swift @@ -19,13 +19,30 @@ final class DashboardStatsNudgeView: UIView { return label }() + private let title: String + private let hint: String? + // MARK: - Init - convenience init(title: String, hint: String?, insets: UIEdgeInsets = Constants.margins) { - self.init(frame: .zero) + init(title: String, hint: String?, insets: UIEdgeInsets = Constants.margins) { + self.title = title + self.hint = hint + + super.init(frame: .zero) setup(insets: insets) - setTitle(title: title, hint: hint) + updateTitleLabel() + + NotificationCenter.default + .addObserver(self, selector: #selector(updateTitleLabel), name: .appColorDidUpdateAccent, object: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + NotificationCenter.default.removeObserver(self) } // MARK: - View setup @@ -37,11 +54,7 @@ final class DashboardStatsNudgeView: UIView { prepareForVoiceOver() } - @objc private func buttonTapped() { - onTap?() - } - - private func setTitle(title: String, hint: String?) { + @objc private func updateTitleLabel() { let externalAttachment = NSTextAttachment(image: UIImage.gridicon(.external, size: Constants.iconSize).withTintColor(.primary)) externalAttachment.bounds = Constants.iconBounds @@ -61,6 +74,10 @@ final class DashboardStatsNudgeView: UIView { titleLabel.attributedText = titleString } + @objc private func buttonTapped() { + onTap?() + } + private enum Constants { static let iconSize = CGSize(width: 16, height: 16) static let iconBounds = CGRect(x: 0, y: -2, width: 16, height: 16) diff --git a/WordPress/Classes/ViewRelated/Blog/Blog Details/Detail Header/BlogDetailHeaderView.swift b/WordPress/Classes/ViewRelated/Blog/Blog Details/Detail Header/BlogDetailHeaderView.swift index 9e3592c4e1cb..f214ef6ec2c8 100644 --- a/WordPress/Classes/ViewRelated/Blog/Blog Details/Detail Header/BlogDetailHeaderView.swift +++ b/WordPress/Classes/ViewRelated/Blog/Blog Details/Detail Header/BlogDetailHeaderView.swift @@ -113,12 +113,19 @@ class BlogDetailHeaderView: UIView { super.init(frame: .zero) setupChildViews(items: items) + + NotificationCenter.default + .addObserver(self, selector: #selector(updateAppearance), name: .appColorDidUpdateAccent, object: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + deinit { + NotificationCenter.default.removeObserver(self) + } + // MARK: - Child View Initialization private func setupChildViews(items: [ActionRow.Item]) { @@ -145,6 +152,11 @@ class BlogDetailHeaderView: UIView { setupConstraintsForChildViews(showsActionRow) } + @objc + private func updateAppearance() { + titleView.subtitleButton.setTitleColor(.primary, for: .normal) + } + // MARK: - Constraints private var topActionRowConstraint: NSLayoutConstraint? diff --git a/WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController.swift b/WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController.swift index 071a644c9963..ee6af58e7290 100644 --- a/WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController.swift +++ b/WordPress/Classes/ViewRelated/Blog/My Site/MySiteViewController.swift @@ -104,6 +104,9 @@ class MySiteViewController: UIViewController, NoResultsViewHost { self.mySiteSettings = mySiteSettings super.init(nibName: nil, bundle: nil) + + NotificationCenter.default + .addObserver(self, selector: #selector(updateAppearance), name: .appColorDidUpdateAccent, object: nil) } required init?(coder: NSCoder) { @@ -281,6 +284,19 @@ class MySiteViewController: UIViewController, NoResultsViewHost { object: nil) } + @objc + func updateAppearance() { + guard AppConfiguration.isWordPress else { + return + } + /// - Note: workaround to update navigation bar tint + navigationController?.isNavigationBarHidden = true + DispatchQueue.main.async { + self.navigationController?.isNavigationBarHidden = false + self.setupNavBarAppearance() + } + } + func updateNavigationTitle(for blog: Blog) { let blogName = blog.settings?.name let title = blogName != nil && blogName?.isEmpty == false diff --git a/WordPress/Classes/ViewRelated/Blog/WPStyleGuide+BloggingPrompts.swift b/WordPress/Classes/ViewRelated/Blog/WPStyleGuide+BloggingPrompts.swift index 98ea3ccca530..2ed2deae6dcd 100644 --- a/WordPress/Classes/ViewRelated/Blog/WPStyleGuide+BloggingPrompts.swift +++ b/WordPress/Classes/ViewRelated/Blog/WPStyleGuide+BloggingPrompts.swift @@ -6,7 +6,9 @@ extension WPStyleGuide { static let answerInfoButtonFont = WPStyleGuide.fontForTextStyle(.caption1) static let answerInfoButtonColor = UIColor.textSubtle static let buttonTitleFont = WPStyleGuide.fontForTextStyle(.subheadline) - static let buttonTitleColor = UIColor.primary + static var buttonTitleColor: UIColor { + .primary + } static let answeredLabelColor = UIColor.muriel(name: .green, .shade50) } } diff --git a/WordPress/Classes/ViewRelated/Domains/Views/ShapeWithTextView.swift b/WordPress/Classes/ViewRelated/Domains/Views/ShapeWithTextView.swift index 9ef91e9fb407..7a4b42958e6d 100644 --- a/WordPress/Classes/ViewRelated/Domains/Views/ShapeWithTextView.swift +++ b/WordPress/Classes/ViewRelated/Domains/Views/ShapeWithTextView.swift @@ -35,7 +35,9 @@ struct ShapeWithTextView: View { // large rounded rectangle static let largeRoundedRectangleCornerRadius: CGFloat = 8.0 static let largeRoundedRectangleTextPadding: CGFloat = 12.0 - static let largeRoundedRectangleDefaultTextColor = Color(UIColor.muriel(color: .primary)) + static var largeRoundedRectangleDefaultTextColor: Color { + .init(UIColor.primary) + } // small rounded rectangle static let smallRoundedRectangleCornerRadius: CGFloat = 4.0 static let smallRoundedRectangleInsets = EdgeInsets(top: 4.0, leading: 8.0, bottom: 4.0, trailing: 8.0) diff --git a/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/Collapsable Header Collection View Cell/CollapsableHeaderCollectionViewCell.swift b/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/Collapsable Header Collection View Cell/CollapsableHeaderCollectionViewCell.swift index 3316164a7442..3827b1dbd39f 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/Collapsable Header Collection View Cell/CollapsableHeaderCollectionViewCell.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/Collapsable Header Collection View Cell/CollapsableHeaderCollectionViewCell.swift @@ -12,7 +12,7 @@ class CollapsableHeaderCollectionViewCell: UICollectionViewCell { @IBOutlet weak var checkmarkImageView: UIImageView! { didSet { checkmarkImageView.image = UIImage(systemName: "checkmark.circle.fill") - checkmarkImageView.tintColor = accentColor + checkmarkImageView.tintColor = .primary } } @@ -34,16 +34,6 @@ class CollapsableHeaderCollectionViewCell: UICollectionViewCell { stopGhostAnimation() } - var accentColor: UIColor { - return UIColor { (traitCollection: UITraitCollection) -> UIColor in - if traitCollection.userInterfaceStyle == .dark { - return UIColor.muriel(color: .primary, .shade40) - } else { - return UIColor.muriel(color: .primary, .shade50) - } - } - } - var borderColor: UIColor { return UIColor.black.withAlphaComponent(0.08) } @@ -94,7 +84,7 @@ class CollapsableHeaderCollectionViewCell: UICollectionViewCell { } private func styleSelectedBorder(animated: Bool = false) { - let imageBorderColor = isSelected ? accentColor.cgColor : borderColor.cgColor + let imageBorderColor = isSelected ? UIColor.primary.cgColor : borderColor.cgColor let imageBorderWidth = isSelected ? 2 : borderWith guard animated else { imageView.layer.borderColor = imageBorderColor diff --git a/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/CollapsableHeaderViewController.swift b/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/CollapsableHeaderViewController.swift index d63f085bf29a..8fffc5503e27 100644 --- a/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/CollapsableHeaderViewController.swift +++ b/WordPress/Classes/ViewRelated/Gutenberg/Collapsable Header/CollapsableHeaderViewController.swift @@ -154,16 +154,6 @@ class CollapsableHeaderViewController: UIViewController, NoResultsViewHost { } private var minHeaderHeight: CGFloat = 0 - private var accentColor: UIColor { - return UIColor { (traitCollection: UITraitCollection) -> UIColor in - if traitCollection.userInterfaceStyle == .dark { - return UIColor.muriel(color: .primary, .shade40) - } else { - return UIColor.muriel(color: .primary, .shade50) - } - } - } - // MARK: - Static Helpers public static func closeButton(target: Any?, action: Selector) -> UIBarButtonItem { let closeButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30)) @@ -422,7 +412,7 @@ class CollapsableHeaderViewController: UIViewController, NoResultsViewHost { primaryActionButton.titleLabel?.font = WPStyleGuide.fontForTextStyle(.body, fontWeight: .medium) primaryActionButton.titleLabel?.adjustsFontSizeToFitWidth = true primaryActionButton.titleLabel?.adjustsFontForContentSizeCategory = true - primaryActionButton.backgroundColor = accentColor + primaryActionButton.backgroundColor = .primary primaryActionButton.layer.cornerRadius = 8 } diff --git a/WordPress/Classes/ViewRelated/Me/App Settings/App Color/AppColor.swift b/WordPress/Classes/ViewRelated/Me/App Settings/App Color/AppColor.swift new file mode 100644 index 000000000000..5b857cdefd89 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Me/App Settings/App Color/AppColor.swift @@ -0,0 +1,226 @@ +import SwiftUI + +enum AppColor { + + enum Accent: String, CaseIterable { + case pink + case red + case orange + case yellow + case celadon + case wooCommercePurple + case jetpackGreen + case wordPressBlue + } + + // MARK: API + + static private(set) var accent: Accent = savedAccent ?? defaultAccent { + didSet { + if accent != oldValue { + savedAccent = accent + + NotificationCenter.default + .post(name: .appColorDidUpdateAccent, object: accent) + } + } + } + + static var accentColor: Color { + accent.color + } + + static func accentColor(_ shade: MurielColorShade) -> Color { + .muriel(accent.murielName, shade: shade) + } + + static func updateAccent(with newAccent: Accent) { + accent = newAccent + } + + // MARK: Helpers + + fileprivate static var isDarkMode: Bool { + UITraitCollection.current.userInterfaceStyle == .dark + } + + private static var defaultAccent: Accent { + switch appConfig { + case .wordpress: + return .wordPressBlue + case .jetpack: + return .jetpackGreen + case .unknown: + return .red + } + } + + private static var savedAccent: Accent? { + get { + guard let rawValue = defaults.string(forKey: savedAccentKey) else { + return nil + } + return Accent(rawValue: rawValue) + } + set { + if let newValue { + defaults.set(newValue.rawValue, forKey: savedAccentKey) + } + } + } + + private static var defaults: UserDefaults { + .init(suiteName: WPAppGroupName) ?? .standard + } + + private static var savedAccentKey: String { + "\(appConfig.rawValue).AppColor.Accent" + } + + private static var appConfig: AppConfig { + if AppConfiguration.isWordPress { + return .wordpress + } else if AppConfiguration.isJetpack { + return .jetpack + } else { + assertionFailure("unsupported configuration") + return .unknown + } + } + + private enum AppConfig: String { + case wordpress = "Wordpress" + case jetpack = "Jetpack" + case unknown = "Unknown" + } + +} + +extension AppColor.Accent: Identifiable, CustomStringConvertible { + + static var allCasesSorted: [Self] { + var all = allCases + let firstAccent = AppColor.defaultAccent + all.removeAll(where: { $0 == firstAccent }) + all.insert(firstAccent, at: 0) + return all + } + + var id: String { + rawValue + } + + var description: String { + switch self { + case .pink: + return AppLocalizedString("color.pink", value: "Pink", comment: "Title for the `Pink` app accent color") + case .red: + return AppLocalizedString("color.red", value: "Red", comment: "Title for the `Red` app accent color") + case .orange: + return AppLocalizedString("color.orange", value: "Orange", comment: "Title for the `Orange` app accent color") + case .yellow: + return AppLocalizedString("color.yellow", value: "Yellow", comment: "Title for the `Yellow` app accent color") + case .celadon: + return AppLocalizedString("color.celadon", value: "Celadon", comment: "Title for the `Celadon` app accent color") + case .wooCommercePurple: + return AppLocalizedString("color.wooCommercePurple", value: "WooCommerce Purple", comment: "Title for the `WooCommerce Purple` app accent color") + case .jetpackGreen: + return AppLocalizedString("color.jetpackGreen", value: "Jetpack Green", comment: "Title for the `Jetpack Green` app accent color") + case .wordPressBlue: + return AppLocalizedString("color.wordPressBlue", value: "WordPress Blue", comment: "Title for the `WordPress Blue` app accent color") + } + } + + var color: Color { + switch self { + case .pink: + return .dynamic( + light: .muriel(murielName, shade: .shade60), + dark: .muriel(murielName, shade: .shade30) + ) + case .red: + return .dynamic( + light: .muriel(murielName, shade: .shade60), + dark: .muriel(murielName, shade: .shade30) + ) + case .orange: + return .dynamic( + light: .muriel(murielName, shade: .shade50), + dark: .muriel(murielName, shade: .shade30) + ) + case .yellow: + return .dynamic( + light: .muriel(murielName, shade: .shade50), + dark: .muriel(murielName, shade: .shade20) + ) + case .celadon: + return .dynamic( + light: .muriel(murielName, shade: .shade60), + dark: .muriel(murielName, shade: .shade20) + ) + case .wooCommercePurple: + return .dynamic( + light: .muriel(murielName, shade: .shade60), + dark: .muriel(murielName, shade: .shade20) + ) + case .jetpackGreen: + return .dynamic( + light: .muriel(murielName, shade: .shade50), + dark: .muriel(murielName, shade: .shade30) + ) + case .wordPressBlue: + return .dynamic( + light: .muriel(murielName, shade: .shade50), + dark: .muriel(murielName, shade: .shade50) + ) + } + } + + fileprivate var murielName: MurielColorName { + switch self { + case .pink: + return .pink + case .red: + return .red + case .orange: + return .orange + case .yellow: + return .yellow + case .celadon: + return .celadon + case .wooCommercePurple: + return .wooCommercePurple + case .jetpackGreen: + return AppColor.isDarkMode ? .green : .jetpackGreen + case .wordPressBlue: + return AppColor.isDarkMode ? .blue : .wordPressBlue + } + } + +} + +extension Foundation.Notification.Name { + static let appColorDidUpdateAccent = Self("appColorDidUpdateAccent") +} + +@objc extension NSNotification { + static var appColorDidUpdateAccent: NSString { + Foundation.Notification.Name.appColorDidUpdateAccent.rawValue as NSString + } +} + +private extension Color { + static func muriel(_ name: MurielColorName, shade: MurielColorShade) -> Self { + .init( + UIColor.muriel(color: .init(name: name, shade: shade)) + ) + } + + static func dynamic(light: Color, dark: Color) -> Color { + Color(UIColor( + dynamicProvider: { + $0.userInterfaceStyle == .dark ? UIColor(dark) : UIColor(light) + }) + ) + } +} diff --git a/WordPress/Classes/ViewRelated/Me/App Settings/App Color/AppColorListView.swift b/WordPress/Classes/ViewRelated/Me/App Settings/App Color/AppColorListView.swift new file mode 100644 index 000000000000..012ce89547d5 --- /dev/null +++ b/WordPress/Classes/ViewRelated/Me/App Settings/App Color/AppColorListView.swift @@ -0,0 +1,66 @@ +import SwiftUI + +struct AppColorListView: View { + @SwiftUI.Environment(\.colorScheme) var colorScheme + + @StateObject var vm = AppColorListViewModel() + + var body: some View { + List(vm.allAccents) { accent in + Button(action: { + vm.selectedAccent = accent + }, label: { + makeRow(with: accent) + }) + } + } + + private func makeRow(with accent: AppColor.Accent) -> some View { + HStack(spacing: 0) { + Circle() + .fill(accent.color) + .frame(width: 32, height: 32) + .padding(.horizontal) + .padding(.vertical, 4) + + Text(accent.description) + .foregroundColor(.primary) + + Spacer() + if accent == vm.selectedAccent { + Image(systemName: "checkmark") + .foregroundColor(accent.color) + } + } + } + +} + +final class AppColorListViewModel: ObservableObject { + + var allAccents: [AppColor.Accent] { + AppColor.Accent.allCasesSorted + } + + @Published var selectedAccent = AppColor.accent { + didSet { + if selectedAccent != oldValue { + updateAccent(selectedAccent) + } + } + } + + private func updateAccent(_ accent: AppColor.Accent) { + AppColor.updateAccent(with: accent) + + let appDelegate = UIApplication.shared.delegate as? WordPressAppDelegate + appDelegate?.customizeAppearance() + } + +} + +struct AppColorListView_Previews: PreviewProvider { + static var previews: some View { + AppColorListView() + } +} diff --git a/WordPress/Classes/ViewRelated/Me/App Settings/AppSettingsViewController.swift b/WordPress/Classes/ViewRelated/Me/App Settings/AppSettingsViewController.swift index 78e0c560c4c1..f0ea646415dc 100644 --- a/WordPress/Classes/ViewRelated/Me/App Settings/AppSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Me/App Settings/AppSettingsViewController.swift @@ -4,6 +4,7 @@ import Gridicons import WordPressShared import SVProgressHUD import WordPressFlux +import SwiftUI class AppSettingsViewController: UITableViewController { enum Sections: Int { @@ -81,6 +82,7 @@ class AppSettingsViewController: UITableViewController { let tableSections = [ mediaTableSection(), privacyTableSection(), + appearanceTableSection(), otherTableSection() ] return ImmuTable(optionalSections: tableSections) @@ -203,7 +205,7 @@ class AppSettingsViewController: UITableViewController { } } - func pushAppearanceSettings() -> ImmuTableAction { + func pushColorModeSettings() -> ImmuTableAction { return { [weak self] row in let values = UIUserInterfaceStyle.allStyles @@ -257,6 +259,14 @@ class AppSettingsViewController: UITableViewController { } } + func pushAppColorList() -> ImmuTableAction { + return { [weak self] row in + let controller = UIHostingController(rootView: AppColorListView()) + controller.title = self?.accentColorTitle + self?.navigationController?.pushViewController(controller, animated: true) + } + } + func openPrivacySettings() -> ImmuTableAction { return { [weak self] _ in WPAnalytics.track(.privacySettingsOpened) @@ -475,58 +485,84 @@ private extension AppSettingsViewController { ) } - func otherTableSection() -> ImmuTableSection { - let otherHeader = NSLocalizedString("Other", comment: "Link to About section (contains info about the app)") + func appearanceTableSection() -> ImmuTableSection { + var rows: [ImmuTableRow] = [] - let debugRow = NavigationItemRow( - title: NSLocalizedString("Debug", comment: "Navigates to debug menu only available in development builds"), - icon: .gridicon(.bug), - action: pushDebugMenu() + let colorModeRow = NavigationItemRow( + title: NSLocalizedString("settings.color.mode.title", value: "Color Mode", comment: "The title for the app 'Color Mode' setting."), + detail: AppAppearance.current.appearanceDescription, + action: pushColorModeSettings() ) + rows.append(colorModeRow) - let iconRow = NavigationItemRow( - title: NSLocalizedString("App Icon", comment: "Navigates to picker screen to change the app's icon"), - action: pushAppIconSwitcher() + if AppConfiguration.allowsCustomAppIcons && UIApplication.shared.supportsAlternateIcons { + let appIconRow = NavigationItemRow( + title: NSLocalizedString("App Icon", comment: "Navigates to picker screen to change the app's icon"), + action: pushAppIconSwitcher() + ) + rows.append(appIconRow) + } + + let appColorRow = NavigationItemRow( + title: accentColorTitle, + detail: AppColor.accent.description, + action: pushAppColorList() + ) + rows.append(appColorRow) + + return ImmuTableSection( + headerText: NSLocalizedString("Appearance", comment: "The title of the app appearance settings section"), + rows: rows, + footerText: nil ) + } + + var accentColorTitle: String { + NSLocalizedString("settings.accent.color.title", value: "Accent Color", comment: "The title for the app 'Accent Color' setting.") + } + + func otherTableSection() -> ImmuTableSection { + var rows: [ImmuTableRow] = [] let settingsRow = NavigationItemRow( title: NSLocalizedString("Open Device Settings", comment: "Opens iOS's Device Settings for WordPress App"), action: openApplicationSettings() ) - - var rows: [ImmuTableRow] = [settingsRow] - - if AppConfiguration.allowsCustomAppIcons && UIApplication.shared.supportsAlternateIcons { - // We don't show custom icons for Jetpack - rows.insert(iconRow, at: 0) - } + rows.append(settingsRow) if JetpackFeaturesRemovalCoordinator.jetpackFeaturesEnabled() { - let initialScreen = NavigationItemRow(title: NSLocalizedString("Initial Screen", comment: "Title of the option to change the default initial screen"), detail: MySiteSettings().defaultSection.title, action: pushInitialScreenSettings()) - + let initialScreen = NavigationItemRow( + title: NSLocalizedString("Initial Screen", comment: "Title of the option to change the default initial screen"), + detail: MySiteSettings().defaultSection.title, + action: pushInitialScreenSettings() + ) rows.append(initialScreen) } if FeatureFlag.debugMenu.enabled { + let debugRow = NavigationItemRow( + title: NSLocalizedString("Debug", comment: "Navigates to debug menu only available in development builds"), + icon: .gridicon(.bug), + action: pushDebugMenu() + ) rows.append(debugRow) } if let presenter = RootViewCoordinator.shared.whatIsNewScenePresenter as? WhatIsNewScenePresenter, presenter.versionHasAnnouncements, AppConfiguration.showsWhatIsNew { - let whatIsNewRow = NavigationItemRow(title: AppConstants.Settings.whatIsNewTitle, - action: presentWhatIsNew()) + let whatIsNewRow = NavigationItemRow( + title: AppConstants.Settings.whatIsNewTitle, + action: presentWhatIsNew() + ) rows.append(whatIsNewRow) } - let appearanceRow = NavigationItemRow(title: NSLocalizedString("Appearance", comment: "The title of the app appearance settings screen"), detail: AppAppearance.current.appearanceDescription, action: pushAppearanceSettings()) - - rows.insert(appearanceRow, at: 0) - return ImmuTableSection( - headerText: otherHeader, + headerText: NSLocalizedString("Other", comment: "Link to About section (contains info about the app)"), rows: rows, - footerText: nil) + footerText: nil + ) } } diff --git a/WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift b/WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift index 2f3de784edee..3c9b37780dd3 100644 --- a/WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift +++ b/WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift @@ -28,6 +28,10 @@ class MeViewController: UITableViewController { fatalError("init(coder:) has not been implemented") } + deinit { + NotificationCenter.default.removeObserver(self) + } + override func viewDidLoad() { super.viewDidLoad() @@ -43,7 +47,10 @@ class MeViewController: UITableViewController { handler = ImmuTableViewHandler(takeOver: self) WPStyleGuide.configureAutomaticHeightRows(for: tableView) - NotificationCenter.default.addObserver(self, selector: #selector(MeViewController.accountDidChange), name: NSNotification.Name.WPAccountDefaultWordPressComAccountChanged, object: nil) + NotificationCenter.default + .addObserver(self, selector: #selector(MeViewController.accountDidChange), name: NSNotification.Name.WPAccountDefaultWordPressComAccountChanged, object: nil) + NotificationCenter.default + .addObserver(self, selector: #selector(updateAppearance), name: .appColorDidUpdateAccent, object: nil) WPStyleGuide.configureColors(view: view, tableView: tableView) tableView.accessibilityIdentifier = "Me Table" @@ -72,6 +79,18 @@ class MeViewController: UITableViewController { registerUserActivity() } + @objc + private func updateAppearance() { + guard AppConfiguration.isWordPress else { + return + } + /// - Note: workaround to update navigation bar tint + navigationController?.isNavigationBarHidden = true + DispatchQueue.main.async { + self.navigationController?.isNavigationBarHidden = false + } + } + @objc fileprivate func accountDidChange() { reloadViewModel() } diff --git a/WordPress/Classes/ViewRelated/NUX/SplashPrologueStyleGuide.swift b/WordPress/Classes/ViewRelated/NUX/SplashPrologueStyleGuide.swift index c571837503df..1d78f337f447 100644 --- a/WordPress/Classes/ViewRelated/NUX/SplashPrologueStyleGuide.swift +++ b/WordPress/Classes/ViewRelated/NUX/SplashPrologueStyleGuide.swift @@ -15,10 +15,14 @@ struct SplashPrologueStyleGuide { } /// Use the same shade for light and dark modes - private static let primaryButtonColor: UIColor = .muriel(color: .primary) + private static var primaryButtonColor: UIColor { + .primary .resolvedColor(with: UITraitCollection(userInterfaceStyle: .light)) - private static let primaryButtonHighlightedColor: UIColor = .muriel(color: .primary, .shade60) + } + private static var primaryButtonHighlightedColor: UIColor { + .primary(.shade60) .resolvedColor(with: UITraitCollection(userInterfaceStyle: .light)) + } private static let secondaryButtonColor: UIColor = .white private static let secondaryButtonHighlightedColor: UIColor = .muriel(color: .gray, .shade5) diff --git a/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationsViewController.swift b/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationsViewController.swift index 3464ac894884..a5c6d559edb0 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationsViewController.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Controllers/NotificationsViewController.swift @@ -195,6 +195,7 @@ class NotificationsViewController: UIViewController, UIViewControllerRestoration // While we're onscreen, please, update rows with animations tableViewHandler.updateRowAnimation = .fade + reloadVisibleCells() // Tracking WPAnalytics.track(WPAnalyticsStat.openedNotificationsList) @@ -598,6 +599,15 @@ private extension NotificationsViewController { tableViewHandler = handler } + func reloadVisibleCells() { + guard let visibleIndexPaths = tableView.indexPathsForVisibleRows else { + return + } + tableView.beginUpdates() + tableView.reloadRows(at: visibleIndexPaths, with: .none) + tableView.endUpdates() + } + func setupInlinePrompt() { precondition(inlinePromptView != nil) diff --git a/WordPress/Classes/ViewRelated/Notifications/Style/WPStyleGuide+Notifications.swift b/WordPress/Classes/ViewRelated/Notifications/Style/WPStyleGuide+Notifications.swift index 6e9e198f5733..5ce7a00c93e1 100644 --- a/WordPress/Classes/ViewRelated/Notifications/Style/WPStyleGuide+Notifications.swift +++ b/WordPress/Classes/ViewRelated/Notifications/Style/WPStyleGuide+Notifications.swift @@ -19,7 +19,9 @@ extension WPStyleGuide { } // ListTableViewCell - public static let unreadIndicatorColor = UIColor.primaryLight + public static var unreadIndicatorColor: UIColor { + .primaryLight + } // Notification cells public static let noticonFont = UIFont(name: "Noticons", size: 16) diff --git a/WordPress/Classes/ViewRelated/People/WPStyleGuide+People.swift b/WordPress/Classes/ViewRelated/People/WPStyleGuide+People.swift index cb4c6ab35b81..157565d6341c 100644 --- a/WordPress/Classes/ViewRelated/People/WPStyleGuide+People.swift +++ b/WordPress/Classes/ViewRelated/People/WPStyleGuide+People.swift @@ -19,7 +19,7 @@ extension WPStyleGuide { } // MARK: Colors - public static let superAdminColor = UIColor.accentDark + public static let superAdminColor = UIColor.accent(.shade70) public static let adminColor = UIColor.neutral(.shade70) public static let editorColor = UIColor.primaryDark public static let otherRoleColor: UIColor = .primary diff --git a/WordPress/Classes/ViewRelated/Reader/Select Interests/ReaderInterestsStyleGuide.swift b/WordPress/Classes/ViewRelated/Reader/Select Interests/ReaderInterestsStyleGuide.swift index 1382017aa3a8..4c405885de02 100644 --- a/WordPress/Classes/ViewRelated/Reader/Select Interests/ReaderInterestsStyleGuide.swift +++ b/WordPress/Classes/ViewRelated/Reader/Select Interests/ReaderInterestsStyleGuide.swift @@ -21,7 +21,7 @@ class ReaderInterestsStyleGuide { public class func applyCellLabelStyle(label: UILabel, isSelected: Bool) { label.font = WPStyleGuide.fontForTextStyle(.body) label.textColor = isSelected ? .white : .text - label.backgroundColor = isSelected ? .muriel(color: .primary, .shade40) : .quaternaryBackground + label.backgroundColor = isSelected ? .primary(.shade40) : .quaternaryBackground } // MARK: - Compact Collection View Cell Styles diff --git a/WordPress/Classes/ViewRelated/Site Creation/Design Selection/Preview/TemplatePreviewViewController.swift b/WordPress/Classes/ViewRelated/Site Creation/Design Selection/Preview/TemplatePreviewViewController.swift index ce751926e2f5..ce4faa2c567b 100644 --- a/WordPress/Classes/ViewRelated/Site Creation/Design Selection/Preview/TemplatePreviewViewController.swift +++ b/WordPress/Classes/ViewRelated/Site Creation/Design Selection/Preview/TemplatePreviewViewController.swift @@ -44,16 +44,6 @@ class TemplatePreviewViewController: UIViewController, NoResultsViewHost, UIPopo return ghost }() - private var accentColor: UIColor { - return UIColor { (traitCollection: UITraitCollection) -> UIColor in - if traitCollection.userInterfaceStyle == .dark { - return UIColor.muriel(color: .primary, .shade40) - } else { - return UIColor.muriel(color: .primary, .shade50) - } - } - } - init(demoURL: String, selectedPreviewDevice: PreviewDevice?, onDismissWithDeviceSelected: ((PreviewDevice) -> ())?) { self.demoURL = demoURL self.selectedPreviewDevice = selectedPreviewDevice ?? PreviewDevice.default @@ -101,7 +91,7 @@ class TemplatePreviewViewController: UIViewController, NoResultsViewHost, UIPopo private func styleButtons() { primaryActionButton.titleLabel?.font = WPStyleGuide.fontForTextStyle(.body, fontWeight: .medium) - primaryActionButton.backgroundColor = accentColor + primaryActionButton.backgroundColor = .primary primaryActionButton.layer.cornerRadius = 8 primaryActionButton.setTitle(NSLocalizedString("Choose", comment: "Title for the button to progress with the selected site homepage design"), for: .normal) } diff --git a/WordPress/Classes/ViewRelated/System/FilterTabBar.swift b/WordPress/Classes/ViewRelated/System/FilterTabBar.swift index 7cd9dd8b8253..1e79591dbedd 100644 --- a/WordPress/Classes/ViewRelated/System/FilterTabBar.swift +++ b/WordPress/Classes/ViewRelated/System/FilterTabBar.swift @@ -216,6 +216,9 @@ class FilterTabBar: UIControl { } private func commonInit() { + NotificationCenter.default + .addObserver(self, selector: #selector(updateAppearance), name: .appColorDidUpdateAccent, object: nil) + tabBarHeightConstraint = heightAnchor.constraint(equalToConstant: tabBarHeight) tabBarHeightConstraint?.isActive = true @@ -260,10 +263,19 @@ class FilterTabBar: UIControl { ]) } + deinit { + NotificationCenter.default.removeObserver(self) + } + // MARK: - Tabs private var tabs: [UIButton] = [] + @objc + private func updateAppearance() { + WPStyleGuide.configureFilterTabBar(self) + } + private func refreshTabs() { tabs.forEach({ $0.removeFromSuperview() }) tabs = items.map(makeTab) diff --git a/WordPress/Classes/ViewRelated/System/Floating Create Button/FloatingActionButton.swift b/WordPress/Classes/ViewRelated/System/Floating Create Button/FloatingActionButton.swift index f3e8af77c2e2..3a23f9836a86 100644 --- a/WordPress/Classes/ViewRelated/System/Floating Create Button/FloatingActionButton.swift +++ b/WordPress/Classes/ViewRelated/System/Floating Create Button/FloatingActionButton.swift @@ -17,21 +17,34 @@ class FloatingActionButton: UIButton { override init(frame: CGRect) { super.init(frame: frame) - layer.backgroundColor = UIColor.primary.cgColor - tintColor = .white - refreshShadow() + updateAppearance() + + NotificationCenter.default + .addObserver(self, selector: #selector(updateAppearance), name: .appColorDidUpdateAccent, object: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + deinit { + NotificationCenter.default.removeObserver(self) + } + override func draw(_ rect: CGRect) { super.draw(rect) layer.cornerRadius = rect.size.width / 2 } + @objc + private func updateAppearance() { + layer.backgroundColor = UIColor.primary.cgColor + tintColor = .white + + refreshShadow() + } + private func refreshShadow() { layer.shadowColor = Constants.shadowColor.cgColor layer.shadowOffset = .zero @@ -41,6 +54,7 @@ class FloatingActionButton: UIButton { override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) - refreshShadow() + + updateAppearance() } } diff --git a/WordPress/Classes/ViewRelated/System/WPTabBarController+Swift.swift b/WordPress/Classes/ViewRelated/System/WPTabBarController+Swift.swift index 5a83bfb9aae6..97efe97739f9 100644 --- a/WordPress/Classes/ViewRelated/System/WPTabBarController+Swift.swift +++ b/WordPress/Classes/ViewRelated/System/WPTabBarController+Swift.swift @@ -92,5 +92,6 @@ extension WPTabBarController { /// Set up the tab bar's colors @objc func setupColors() { tabBar.isTranslucent = false + tabBar.tintColor = .tabSelected } } diff --git a/WordPress/Classes/ViewRelated/System/WPTabBarController.m b/WordPress/Classes/ViewRelated/System/WPTabBarController.m index a79be8cb8c63..b85c08829058 100644 --- a/WordPress/Classes/ViewRelated/System/WPTabBarController.m +++ b/WordPress/Classes/ViewRelated/System/WPTabBarController.m @@ -116,6 +116,11 @@ - (instancetype)init name:WordPressAuthenticator.WPSigninDidFinishNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(setupColors) + name:NSNotification.appColorDidUpdateAccent + object:nil]; + // Watch for application badge number changes [[UIApplication sharedApplication] addObserver:self forKeyPath:WPApplicationIconBadgeNumberKeyPath @@ -132,6 +137,7 @@ - (instancetype)init - (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; [self.tabBar removeObserver:self forKeyPath:WPTabBarFrameKeyPath]; [self stopWatchingQuickTours]; [[UIApplication sharedApplication] removeObserver:self forKeyPath:WPApplicationIconBadgeNumberKeyPath]; diff --git a/WordPress/Jetpack/AppStyleGuide.swift b/WordPress/Jetpack/AppStyleGuide.swift index 0f9343cc21ca..efab86bdad22 100644 --- a/WordPress/Jetpack/AppStyleGuide.swift +++ b/WordPress/Jetpack/AppStyleGuide.swift @@ -14,12 +14,9 @@ struct AppStyleGuide { // MARK: - Colors extension AppStyleGuide { - static let accent = MurielColor(name: .jetpackGreen) - static let brand = MurielColor(name: .jetpackGreen) static let divider = MurielColor(name: .gray, shade: .shade10) static let error = MurielColor(name: .red) static let gray = MurielColor(name: .gray) - static let primary = MurielColor(name: .jetpackGreen) static let success = MurielColor(name: .green) static let text = MurielColor(name: .gray, shade: .shade80) static let textSubtle = MurielColor(name: .gray, shade: .shade50) diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj index 505350ebb05e..8f36208ef7fc 100644 --- a/WordPress/WordPress.xcodeproj/project.pbxproj +++ b/WordPress/WordPress.xcodeproj/project.pbxproj @@ -2055,6 +2055,17 @@ 8B939F4323832E5D00ACCB0F /* PostListViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B939F4223832E5D00ACCB0F /* PostListViewControllerTests.swift */; }; 8BA125EB27D8F5E4008B779F /* UIView+PinSubviewPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */; }; 8BA125EC27D8F5E4008B779F /* UIView+PinSubviewPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */; }; + 8BA6F7BC29B9F8D000026408 /* AppColorListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BB29B9F8D000026408 /* AppColorListView.swift */; }; + 8BA6F7BE29BA19F200026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; + 8BA6F7C429BB54D000026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; + 8BA6F7C529BB54D400026408 /* AppColorListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BB29B9F8D000026408 /* AppColorListView.swift */; }; + 8BA6F7C729BB6B4900026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; + 8BA6F7C829BB6B4A00026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; + 8BA6F7C929BB6B4A00026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; + 8BA6F7CA29BB6B4B00026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; + 8BA6F7CB29BB6B5300026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; + 8BA6F7CC29BB6B5400026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; + 8BA6F7CD29BB6B5400026408 /* AppColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA6F7BD29BA19F200026408 /* AppColor.swift */; }; 8BA77BCB2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8BA77BCA2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib */; }; 8BA77BCD248340CE00E1EBBF /* ReaderDetailToolbar.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8BA77BCC248340CE00E1EBBF /* ReaderDetailToolbar.xib */; }; 8BA77BCF2483415400E1EBBF /* ReaderDetailToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA77BCE2483415400E1EBBF /* ReaderDetailToolbar.swift */; }; @@ -7295,6 +7306,8 @@ 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageListSectionHeaderView.swift; sourceTree = ""; }; 8B939F4223832E5D00ACCB0F /* PostListViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListViewControllerTests.swift; sourceTree = ""; }; 8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+PinSubviewPriority.swift"; sourceTree = ""; }; + 8BA6F7BB29B9F8D000026408 /* AppColorListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppColorListView.swift; sourceTree = ""; }; + 8BA6F7BD29BA19F200026408 /* AppColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppColor.swift; sourceTree = ""; }; 8BA77BCA2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReaderCardDiscoverAttributionView.xib; sourceTree = ""; }; 8BA77BCC248340CE00E1EBBF /* ReaderDetailToolbar.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReaderDetailToolbar.xib; sourceTree = ""; }; 8BA77BCE2483415400E1EBBF /* ReaderDetailToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderDetailToolbar.swift; sourceTree = ""; }; @@ -10748,6 +10761,7 @@ 3F29EB6E240420C3005313DE /* App Settings */ = { isa = PBXGroup; children = ( + 8BA6F7BA29B9F8B400026408 /* App Color */, F44293D428E3B39400D340AF /* App Icons */, 3F29EB6824041F6D005313DE /* About */, FFA162301CB7031A00E2E110 /* AppSettingsViewController.swift */, @@ -13410,6 +13424,15 @@ path = Filter; sourceTree = ""; }; + 8BA6F7BA29B9F8B400026408 /* App Color */ = { + isa = PBXGroup; + children = ( + 8BA6F7BD29BA19F200026408 /* AppColor.swift */, + 8BA6F7BB29B9F8D000026408 /* AppColorListView.swift */, + ); + path = "App Color"; + sourceTree = ""; + }; 8BADF16324801B4B005AD038 /* Detail */ = { isa = PBXGroup; children = ( @@ -20397,6 +20420,7 @@ 0107E0BF28F97D5000DE87DB /* FeatureFlagOverrideStore.swift in Sources */, 0107E11528FD7FE500DE87DB /* AppConfiguration.swift in Sources */, 0107E0C028F97D5000DE87DB /* WordPressHomeWidgetToday.swift in Sources */, + 8BA6F7CD29BB6B5400026408 /* AppColor.swift in Sources */, 0107E0C128F97D5000DE87DB /* FlexibleCard.swift in Sources */, 0107E0C228F97D5000DE87DB /* VerticalCard.swift in Sources */, 0107E0C328F97D5000DE87DB /* ThisWeekWidgetStats.swift in Sources */, @@ -21506,6 +21530,7 @@ 93C4864F181043D700A24725 /* ActivityLogDetailViewController.m in Sources */, B57B99DE19A2DBF200506504 /* NSObject+Helpers.m in Sources */, E1E49CE41C4902EE002393A4 /* ImmuTableViewController.swift in Sources */, + 8BA6F7BC29B9F8D000026408 /* AppColorListView.swift in Sources */, D88A6496208D7B0B008AE9BC /* NullStockPhotosService.swift in Sources */, 9A162F2321C26D7500FDC035 /* RevisionPreviewViewController.swift in Sources */, 467D3DFA25E4436000EB9CB0 /* SitePromptView.swift in Sources */, @@ -21797,6 +21822,7 @@ C81CCD82243BF7A600A83E27 /* TenorResultsPage.swift in Sources */, FACB36F11C5C2BF800C6DF4E /* ThemeWebNavigationDelegate.swift in Sources */, E1F47D4D1FE0290C00C1D44E /* PluginListCell.swift in Sources */, + 8BA6F7BE29BA19F200026408 /* AppColor.swift in Sources */, 3F5C865D25C9EBEF00BABE64 /* HomeWidgetAllTimeData.swift in Sources */, D829C33B21B12EFE00B09F12 /* UIView+Borders.swift in Sources */, F1A75B9B2732EF3700784A70 /* AboutScreenTracker.swift in Sources */, @@ -22251,6 +22277,7 @@ 3F6BC05C25B24773007369D3 /* FeatureFlagOverrideStore.swift in Sources */, 3FD675EA25C87A25009AB3C1 /* WordPressHomeWidgetToday.swift in Sources */, 3F568A2F254216550048A9E4 /* FlexibleCard.swift in Sources */, + 8BA6F7C929BB6B4A00026408 /* AppColor.swift in Sources */, 3F568A1F254213B60048A9E4 /* VerticalCard.swift in Sources */, 3F8B306825D1D4B8005A2903 /* ThisWeekWidgetStats.swift in Sources */, 3F5C861A25C9EA2500BABE64 /* HomeWidgetAllTimeData.swift in Sources */, @@ -22361,6 +22388,7 @@ 433ADC1B223B2A7E00ED9DE1 /* TextBundleWrapper.m in Sources */, 4326191622FCB9F8003C7642 /* MurielColor.swift in Sources */, 73B05D2921374F1E0073ECAA /* Constants.m in Sources */, + 8BA6F7CA29BB6B4B00026408 /* AppColor.swift in Sources */, 736584EB2137533A0029C9A4 /* NotificationContentView.swift in Sources */, 73D5AC63212622B200ADDDD2 /* NotificationViewController.swift in Sources */, 436110DE22C41B02000773AD /* BuildConfiguration.swift in Sources */, @@ -22461,6 +22489,7 @@ 74021A1E202E164D006CC39F /* CocoaLumberjack.swift in Sources */, 74E44AD52031E83C00556205 /* ExtensionNotificationManager.swift in Sources */, 74448F552044BC7600BD4CDA /* CategoryTree.swift in Sources */, + 8BA6F7C829BB6B4A00026408 /* AppColor.swift in Sources */, FAD2539126116A1600EDAF88 /* AppStyleGuide.swift in Sources */, 74021A09202E1323006CC39F /* ShareSegueHandler.swift in Sources */, 74021A01202E12F4006CC39F /* SharedCoreDataStack.swift in Sources */, @@ -22537,6 +22566,7 @@ 809620EF28E540D700940A5D /* ExtensionPresentationAnimator.swift in Sources */, 809620F028E540D700940A5D /* String+RegEx.swift in Sources */, 809620F128E540D700940A5D /* AppStyleGuide.swift in Sources */, + 8BA6F7CB29BB6B5300026408 /* AppColor.swift in Sources */, 809620F228E540D700940A5D /* CocoaLumberjack.swift in Sources */, 809620F328E540D700940A5D /* SharedCoreDataStack.swift in Sources */, 809620F428E540D700940A5D /* ShareExtensionAbstractViewController.swift in Sources */, @@ -22613,6 +22643,7 @@ 8096215028E55C9400940A5D /* ExtensionNotificationManager.swift in Sources */, 8096215128E55C9400940A5D /* CategoryTree.swift in Sources */, 8096215228E55C9400940A5D /* AppStyleGuide.swift in Sources */, + 8BA6F7CC29BB6B5400026408 /* AppColor.swift in Sources */, 8096215328E55C9400940A5D /* ShareSegueHandler.swift in Sources */, 8096215428E55C9400940A5D /* SharedCoreDataStack.swift in Sources */, 8096215528E55C9400940A5D /* FeatureFlagOverrideStore.swift in Sources */, @@ -22753,6 +22784,7 @@ 4020B2BE2007AC850002C963 /* WPStyleGuide+Gridicon.swift in Sources */, 74402F2E2005344700A1D4A2 /* ExtensionPresentationAnimator.swift in Sources */, 74D06FE91FE0782000AF1788 /* String+RegEx.swift in Sources */, + 8BA6F7C729BB6B4900026408 /* AppColor.swift in Sources */, FAD2539026116A1600EDAF88 /* AppStyleGuide.swift in Sources */, 938CF3DD1EF1BE7F00AF838E /* CocoaLumberjack.swift in Sources */, 746D6B251FBF701F003C45BE /* SharedCoreDataStack.swift in Sources */, @@ -23511,6 +23543,7 @@ FABB21A32602FC2C00C8785C /* CommentsViewController.m in Sources */, FABB21A42602FC2C00C8785C /* SiteSettingsViewController+SiteManagement.swift in Sources */, FABB21A52602FC2C00C8785C /* Follow.swift in Sources */, + 8BA6F7C529BB54D400026408 /* AppColorListView.swift in Sources */, FABB21A62602FC2C00C8785C /* BlogJetpackSettingsService.swift in Sources */, FABB21A72602FC2C00C8785C /* MenuItemTypeViewController.m in Sources */, 80535DC3294BDE2B00873161 /* BlogDetailsViewController+JetpackBrandingMenuCard.swift in Sources */, @@ -24936,6 +24969,7 @@ FABB25DC2602FC2C00C8785C /* ThemeIdHelper.swift in Sources */, FABB25DD2602FC2C00C8785C /* BottomScrollAnalyticsTracker.swift in Sources */, FABB25DE2602FC2C00C8785C /* UserProfile.swift in Sources */, + 8BA6F7C429BB54D000026408 /* AppColor.swift in Sources */, FABB25DF2602FC2C00C8785C /* PickerTableViewCell.swift in Sources */, FABB25E02602FC2C00C8785C /* StoryEditor.swift in Sources */, 2481B180260D4D4E00AE59DB /* WPAccount+Lookup.swift in Sources */,