Skip to content

Move AppColor to DesignSystem #24227

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Modules/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,15 @@ let package = Package(
.product(name: "Gifu", package: "Gifu"),
]),
.target(name: "BuildSettingsKit"),
.target(name: "DesignSystem", swiftSettings: [.swiftLanguageMode(.v5)]),
.target(
name: "DesignSystem",
dependencies: [
"BuildSettingsKit",
.product(name: "ColorStudio", package: "color-studio"),
],
resources: [.process("Resources")],
swiftSettings: [.swiftLanguageMode(.v5)]
),
.target(name: "JetpackStatsWidgetsCore", swiftSettings: [.swiftLanguageMode(.v5)]),
// SFHFKeychainUtils is an old Objective-C keychain wrapper.
// The implementatoin predates ARC, hence the dedicated target with ARC disabled, for the time being.
Expand Down Expand Up @@ -96,7 +104,7 @@ let package = Package(
.target(name: "WordPressTesting", resources: [.process("Resources")]),
.target(
name: "WordPressUI",
dependencies: ["AsyncImageKit", "WordPressShared"],
dependencies: ["AsyncImageKit", "DesignSystem", "WordPressShared"],
resources: [.process("Resources")],
swiftSettings: [.swiftLanguageMode(.v5)]
),
Expand Down Expand Up @@ -175,7 +183,6 @@ enum XcodeSupport {
.product(name: "Reachability", package: "Reachability"),
.product(name: "SVProgressHUD", package: "SVProgressHUD"),
.product(name: "ZIPFoundation", package: "ZIPFoundation"),
.product(name: "ColorStudio", package: "color-studio"),
.product(name: "Aztec", package: "AztecEditor-iOS"),
.product(name: "WordPressEditor", package: "AztecEditor-iOS"),
]
Expand Down Expand Up @@ -252,7 +259,6 @@ enum XcodeSupport {
"WordPressUI",
.product(name: "CocoaLumberjackSwift", package: "CocoaLumberjack"),
.product(name: "WordPressAPI", package: "wordpress-rs"),
.product(name: "ColorStudio", package: "color-studio"),
]),
.xcodeTarget("XcodeTarget_Intents", dependencies: [
"BuildSettingsKit",
Expand Down
1 change: 1 addition & 0 deletions Modules/Sources/BuildSettingsKit/BuildSettings+Live.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ extension BuildSettings {
static let live = BuildSettings(bundle: .app)

init(bundle: Bundle) {
brand = AppBrand(rawValue: bundle.infoValue(forKey: "WPAppBrand"))!
pushNotificationAppID = bundle.infoValue(forKey: "WPPushNotificationAppID")
appGroupName = bundle.infoValue(forKey: "WPAppGroupName")
appKeychainAccessGroup = bundle.infoValue(forKey: "WPAppKeychainAccessGroup")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Foundation
/// The container for Xcode previews.
extension BuildSettings {
nonisolated(unsafe) static var preview = BuildSettings(
brand: .jetpack,
pushNotificationAppID: "xcpreview_push_notification_id",
appGroupName: "xcpreview_app_group_name",
appKeychainAccessGroup: "xcpreview_app_keychain_access_group"
Expand Down
14 changes: 12 additions & 2 deletions Modules/Sources/BuildSettingsKit/BuildSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,24 @@ import Foundation
/// - **Test** – `BuildSettings` are not available when running unit tests as
/// they are incompatible with parallelized tests and are generally not recommended.
public struct BuildSettings: Sendable {
public var brand: AppBrand
public var pushNotificationAppID: String
public var appGroupName: String
public var appKeychainAccessGroup: String

public static var current: BuildSettings {
switch BuildSettingsEnvironment.current {
case .live: .live
case .preview: .preview
case .live:
return .live
case .preview:
return .preview
case .test:
fatalError("BuildSettings are unavailable when running unit tests. Make sure to inject the values manually in system under test.")
}
}
}

public enum AppBrand: String, Sendable {
case wordpress
case jetpack
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import Foundation

enum BuildSettingsEnvironment {
public enum BuildSettingsEnvironment: Sendable {
case live
case preview
case test

static let current: BuildSettingsEnvironment = {
public static let current: BuildSettingsEnvironment = {
#if DEBUG
let processInfo = ProcessInfo.processInfo
if processInfo.isXcodePreview {
return .preview
}
if processInfo.isTesting {
fatalError("BuildSettings are unavailable when running unit tests. Make sure to inject the values manually in system under test.")
return .test
}
#endif
return .live
Expand Down
7 changes: 6 additions & 1 deletion Modules/Sources/DesignSystem/Components/DSButtonStyle.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import SwiftUI
import BuildSettingsKit

public struct DSButtonStyle {
public enum Emphasis: CaseIterable {
Expand All @@ -17,7 +18,11 @@ public struct DSButtonStyle {
public let size: Size
public let isJetpack: Bool

public init(emphasis: Emphasis, size: Size, isJetpack: Bool) {
public init(
emphasis: Emphasis,
size: Size,
isJetpack: Bool = BuildSettings.current.brand == .jetpack
) {
self.emphasis = emphasis
self.size = size
self.isJetpack = isJetpack
Expand Down
157 changes: 157 additions & 0 deletions Modules/Sources/DesignSystem/Foundation/AppColor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import UIKit
import SwiftUI
import ColorStudio
import BuildSettingsKit

public enum UIAppColor {
/// A tint color used in places like navigation bars.
///
/// - note: The Jetpack app uses
public static var tint: UIColor {
switch AppBrand.current {
case .wordpress: primary
case .jetpack: UIColor.label
}
}

public static var primary: UIColor {
switch AppBrand.current {
case .wordpress: UIColor(light: CSColor.Blue.base, dark: primary(.shade40))
case .jetpack: UIColor(light: CSColor.JetpackGreen.shade(.shade40), dark: CSColor.JetpackGreen.shade(.shade30))
}
}

public static func primary(_ shade: ColorStudioShade) -> UIColor {
switch AppBrand.current {
case .wordpress: CSColor.Blue.shade(shade)
case .jetpack: CSColor.JetpackGreen.shade(shade)
}
}
}

extension UIAppColor {
public static func accent(_ shade: ColorStudioShade) -> UIColor {
CSColor.Pink.shade(shade)
}

public static func error(_ shade: ColorStudioShade) -> UIColor {
CSColor.Red.shade(shade)
}

public static func warning(_ shade: ColorStudioShade) -> UIColor {
CSColor.Yellow.shade(shade)
}

public static func success(_ shade: ColorStudioShade) -> UIColor {
CSColor.Green.shade(shade)
}

public static func gray(_ shade: ColorStudioShade) -> UIColor {
CSColor.Gray.shade(shade)
}

public static func blue(_ shade: ColorStudioShade) -> UIColor {
CSColor.Blue.shade(shade)
}

public static func green(_ shade: ColorStudioShade) -> UIColor {
CSColor.Green.shade(shade)
}

public static func red(_ shade: ColorStudioShade) -> UIColor {
CSColor.Red.shade(shade)
}

public static func pink(_ shade: ColorStudioShade) -> UIColor {
CSColor.Pink.shade(shade)
}

public static func yellow(_ shade: ColorStudioShade) -> UIColor {
CSColor.Yellow.shade(shade)
}

public static func purple(_ shade: ColorStudioShade) -> UIColor {
CSColor.Purple.shade(shade)
}

public static func orange(_ shade: ColorStudioShade) -> UIColor {
CSColor.Orange.shade(shade)
}

public static func celadon(_ shade: ColorStudioShade) -> UIColor {
CSColor.Celadon.shade(shade)
}

public static func wordPressBlue(_ shade: ColorStudioShade) -> UIColor {
CSColor.WordPressBlue.shade(shade)
}

public static func jetpackGreen(_ shade: ColorStudioShade) -> UIColor {
CSColor.JetpackGreen.shade(shade)
}

public static let primaryLight: UIColor = primary(.shade30)
public static let primaryDark: UIColor = primary(.shade70)

public static func neutral(_ shade: ColorStudioShade) -> UIColor {
return switch shade {
case .shade0: UIColor(light: gray(.shade0), dark: gray(.shade100))
case .shade5: UIColor(light: gray(.shade5), dark: gray(.shade90))
case .shade10: UIColor(light: gray(.shade10), dark: gray(.shade80))
case .shade20: UIColor(light: gray(.shade20), dark: gray(.shade70))
case .shade30: UIColor(light: gray(.shade30), dark: gray(.shade60))
case .shade40: UIColor(light: gray(.shade40), dark: gray(.shade50))
case .shade50: UIColor(light: gray(.shade50), dark: gray(.shade40))
case .shade60: UIColor(light: gray(.shade60), dark: gray(.shade30))
case .shade70: UIColor(light: gray(.shade70), dark: gray(.shade20))
case .shade80: UIColor(light: gray(.shade80), dark: gray(.shade10))
case .shade90: UIColor(light: gray(.shade90), dark: gray(.shade5))
case .shade100: UIColor(light: gray(.shade100), dark: gray(.shade0))
}
}

public static let accent = CSColor.Pink.base
public static let divider = CSColor.Gray.shade(.shade10)
public static let error = CSColor.Red.base
public static let gray = CSColor.Gray.base
public static let blue = CSColor.Blue.base

public static let success = CSColor.Green.base
public static let text = CSColor.Gray.shade(.shade80)
public static let textSubtle = CSColor.Gray.shade(.shade50)
public static let warning = CSColor.Yellow.base
public static let jetpackGreen = CSColor.JetpackGreen.base
public static let editorPrimary = CSColor.Blue.base
public static let neutral = CSColor.Gray.base

public static let statsPrimaryHighlight = UIColor(light: accent(.shade30), dark: accent(.shade60))
public static let statsSecondaryHighlight = UIColor(light: accent(.shade60), dark: accent(.shade30))

// TODO : These should be customized for WP and JP
public static let appBarTint = UIColor.systemOrange
public static let appBarText = UIColor.systemOrange

public static let placeholderElement = UIColor(light: .systemGray5, dark: .systemGray4)
public static let placeholderElementFaded: UIColor = UIColor(light: .systemGray6, dark: .systemGray5)

public static let prologueBackground = UIColor(light: blue(.shade0), dark: .systemBackground)

public static let switchStyle: SwitchToggleStyle = SwitchToggleStyle(tint: Color(UIAppColor.primary))
}

public enum AppColor {
public static var tint: Color { Color(UIAppColor.tint) }
public static var primary: Color { Color(UIAppColor.primary) }
}

private extension UIColor {
convenience init(light: UIColor, dark: UIColor) {
self.init { traitCollection in
if traitCollection.userInterfaceStyle == .dark {
return dark
} else {
return light
}
}
}
}
12 changes: 12 additions & 0 deletions Modules/Sources/DesignSystem/Foundation/BuildSettings+Brand.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation
import BuildSettingsKit

extension AppBrand {
static var current: AppBrand {
// TODO: remove this when unit tests not longer rely on `BuildSettings.current`.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, I had to add this as there are too many unit tests that test UI components in the app.

if BuildSettingsEnvironment.current == .test {
return .jetpack
}
return BuildSettings.current.brand
}
}
3 changes: 3 additions & 0 deletions Modules/Sources/WordPressUI/WordPressUI+Exports.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@_exported import DesignSystem
@_exported import AsyncImageKit
@_exported import WordPressShared
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import UIKit
import WordPressUI

/// Objective-C *only* API for the Muriel colors
@objc extension UIColor {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import UIKit
import WordPressUI

extension UIColor {

static var invertedSystem5: UIColor {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import UIKit
import Gridicons
import WordPressShared
import WordPressUI

extension WPStyleGuide {
static let aztecFormatBarInactiveColor: UIColor = .secondaryLabel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import UIKit
import WordPressUI

extension WPStyleGuide {
@objc class func configureFilterTabBar(_ filterTabBar: FilterTabBar) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import UIKit
import WordPressShared
import WordPressUI

extension UITableViewCell {
/// Enable cell interaction
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import UIKit
import WordPressUI

/// Encapsulates logic to approve a comment
class ApproveComment: DefaultNotificationActionCommand {
enum TitleStrings {
Expand Down
3 changes: 3 additions & 0 deletions WordPress/Classes/Models/Notifications/Actions/Follow.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import UIKit
import WordPressUI

/// Encapsulates logic to follow a blog
final class Follow: DefaultNotificationActionCommand {
static let title = NSLocalizedString("notifications.action.subscribe.title", value: "Subscribe", comment: "Prompt to subscribe to a blog.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import UIKit
import WordPressUI

/// Base Notification Action Command.
class DefaultNotificationActionCommand: FormattableContentActionCommand {
var on: Bool
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import UIKit
import WordPressUI

/// Encapsulates logic to trash a comment
class TrashComment: DefaultNotificationActionCommand {
static let title = NSLocalizedString("Trash", comment: "Trashes the comment")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import UIKit
import WordPressUI

extension DiffAbstractValue {
var attributes: [NSAttributedString.Key: Any]? {
switch operation {
Expand Down
Loading