diff --git a/V2er/Assets.xcassets/Colors/AppBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/AppBackground.colorset/Contents.json new file mode 100644 index 0000000..3d8ee2e --- /dev/null +++ b/V2er/Assets.xcassets/Colors/AppBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.980", + "green" : "0.980", + "red" : "0.980" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.110", + "green" : "0.110", + "red" : "0.110" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/BackgroundColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/BackgroundColor.colorset/Contents.json new file mode 100644 index 0000000..3d84274 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/BackgroundColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.800", + "blue" : "0.886", + "green" : "0.886", + "red" : "0.886" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.800", + "blue" : "0.118", + "green" : "0.110", + "red" : "0.110" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/BorderColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/BorderColor.colorset/Contents.json new file mode 100644 index 0000000..462bf3e --- /dev/null +++ b/V2er/Assets.xcassets/Colors/BorderColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.800", + "blue" : "0.910", + "green" : "0.910", + "red" : "0.910" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.800", + "blue" : "0.247", + "green" : "0.235", + "red" : "0.235" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/DimColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/DimColor.colorset/Contents.json new file mode 100644 index 0000000..f6c9cf5 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/DimColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.600", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.600", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/ItemBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/ItemBackground.colorset/Contents.json new file mode 100644 index 0000000..a431dd9 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/ItemBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1E", + "green" : "0x1C", + "red" : "0x1C" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/LightGray.colorset/Contents.json b/V2er/Assets.xcassets/Colors/LightGray.colorset/Contents.json new file mode 100644 index 0000000..1beb2f7 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/LightGray.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF5", + "green" : "0xF5", + "red" : "0xF5" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x2E", + "green" : "0x2C", + "red" : "0x2C" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/PrimaryText.colorset/Contents.json b/V2er/Assets.xcassets/Colors/PrimaryText.colorset/Contents.json new file mode 100644 index 0000000..c4fd6eb --- /dev/null +++ b/V2er/Assets.xcassets/Colors/PrimaryText.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x00", + "green" : "0x00", + "red" : "0x00" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/SecondaryBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SecondaryBackground.colorset/Contents.json new file mode 100644 index 0000000..cca56c7 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/SecondaryBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.961", + "green" : "0.961", + "red" : "0.961" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.153", + "green" : "0.153", + "red" : "0.153" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/SecondaryText.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SecondaryText.colorset/Contents.json new file mode 100644 index 0000000..41b70f4 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/SecondaryText.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x55", + "green" : "0x55", + "red" : "0x55" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xAA", + "green" : "0xAA", + "red" : "0xAA" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/SelectionColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/SelectionColor.colorset/Contents.json new file mode 100644 index 0000000..da7da1f --- /dev/null +++ b/V2er/Assets.xcassets/Colors/SelectionColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.900", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.900", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/Separator.colorset/Contents.json b/V2er/Assets.xcassets/Colors/Separator.colorset/Contents.json new file mode 100644 index 0000000..dbb0d23 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/Separator.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xE8", + "green" : "0xE8", + "red" : "0xE8" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x2E", + "green" : "0x2C", + "red" : "0x2C" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/TertiaryBackground.colorset/Contents.json b/V2er/Assets.xcassets/Colors/TertiaryBackground.colorset/Contents.json new file mode 100644 index 0000000..65deedd --- /dev/null +++ b/V2er/Assets.xcassets/Colors/TertiaryBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xF5", + "green" : "0xF5", + "red" : "0xF5" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x1A", + "green" : "0x1A", + "red" : "0x1A" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/TertiaryText.colorset/Contents.json b/V2er/Assets.xcassets/Colors/TertiaryText.colorset/Contents.json new file mode 100644 index 0000000..3b03d34 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/TertiaryText.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x99", + "green" : "0x99", + "red" : "0x99" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x66", + "green" : "0x66", + "red" : "0x66" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/TintColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/TintColor.colorset/Contents.json new file mode 100644 index 0000000..db7950d --- /dev/null +++ b/V2er/Assets.xcassets/Colors/TintColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x38", + "green" : "0x38", + "red" : "0x38" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xFF", + "green" : "0xFF", + "red" : "0xFF" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/URLColor.colorset/Contents.json b/V2er/Assets.xcassets/Colors/URLColor.colorset/Contents.json new file mode 100644 index 0000000..84b6cc8 --- /dev/null +++ b/V2er/Assets.xcassets/Colors/URLColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0x87", + "green" : "0x80", + "red" : "0x77" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0xB8", + "green" : "0xA3", + "red" : "0x94" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/V2er/Assets.xcassets/Colors/bodyText.colorset/Contents.json b/V2er/Assets.xcassets/Colors/bodyText.colorset/Contents.json index 6e8d0a5..94e7045 100644 --- a/V2er/Assets.xcassets/Colors/bodyText.colorset/Contents.json +++ b/V2er/Assets.xcassets/Colors/bodyText.colorset/Contents.json @@ -4,10 +4,28 @@ "color" : { "color-space" : "srgb", "components" : { - "alpha" : "1.000", - "blue" : "0x55", - "green" : "0x55", - "red" : "0x55" + "alpha" : "0.750", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "0.850", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" } }, "idiom" : "universal" @@ -17,4 +35,4 @@ "author" : "xcode", "version" : 1 } -} +} \ No newline at end of file diff --git a/V2er/General/Color.swift b/V2er/General/Color.swift index e98129a..9016edf 100644 --- a/V2er/General/Color.swift +++ b/V2er/General/Color.swift @@ -26,24 +26,62 @@ extension Color { self.frame(width: .infinity) } - public static let border = hex(0xE8E8E8, alpha: 0.8) - static let lightGray = hex(0xF5F5F5) - static let almostClear = hex(0xFFFFFF, alpha: 0.000001) - static let debugColor = hex(0xFF0000, alpha: 0.1) -// static let bodyText = hex(0x555555) - static let bodyText = hex(0x000000, alpha: 0.75) - static let tintColor = hex(0x383838) - static let bgColor = hex(0xE2E2E2, alpha: 0.8) - static let itemBg: Color = .white - static let dim = hex(0x000000, alpha: 0.6) -// static let url = hex(0x60c2d4) - static let url = hex(0x778087) + // MARK: - Adaptive Colors for Dark Mode + + // Background Colors + public static let background = Color("AppBackground") + public static let secondaryBackground = Color("SecondaryBackground") + public static let tertiaryBackground = Color("TertiaryBackground") + public static let itemBackground = Color("ItemBackground") + + // Text Colors + public static let primaryText = Color("PrimaryText") + public static let secondaryText = Color("SecondaryText") + public static let tertiaryText = Color("TertiaryText") + + // UI Element Colors + public static let separator = Color("Separator") + public static let tint = Color("TintColor") + public static let selection = Color("SelectionColor") + + // Legacy colors with dark mode support + public static let border = Color("BorderColor") + public static let lightGray = Color("LightGray") + public static let almostClear = hex(0xFFFFFF, alpha: 0.000001) + public static let debugColor = hex(0xFF0000, alpha: 0.1) + public static let bodyText = Color("BodyText") + public static let tintColor = Color("TintColor") + public static let bgColor = Color("BackgroundColor") + public static let itemBg = Color("ItemBackground") + public static let dim = Color("DimColor") + public static let url = Color("URLColor") public var uiColor: UIColor { return UIColor(self) } } +// MARK: - Dynamic Color Creation +extension Color { + static func dynamic(light: Color, dark: Color) -> Color { + return Color(UIColor { traitCollection in + switch traitCollection.userInterfaceStyle { + case .dark: + return UIColor(dark) + default: + return UIColor(light) + } + }) + } + + static func dynamicHex(light: Int, dark: Int, alpha: CGFloat = 1.0) -> Color { + return dynamic( + light: Color.hex(light, alpha: alpha), + dark: Color.hex(dark, alpha: alpha) + ) + } +} + extension UIColor { convenience init(hex: Int, alpha: CGFloat = 1.0) { let components = ( @@ -55,18 +93,47 @@ extension UIColor { } } - - +// MARK: - Preview struct Color_Previews: PreviewProvider { static var previews: some View { - VStack { - Color.hex(0xFBFBFB).frame(width: 100, height: 100) - Color.hex(0x00FF00, alpha: 0.2).frame(width: 100, height: 100) - Color.hex(0xFF00FF).frame(width: 100, height: 100) - Color.tintColor.frame(width: 100, height: 100) - Color.lightGray.frame(width: 100, height: 100) - Color.border.frame(width: 100, height: 100).opacity(0.5) + Group { + // Light Mode Preview + VStack(spacing: 20) { + Text("Light Mode") + .font(.title) + HStack { + Color.background.frame(width: 80, height: 80) + Color.secondaryBackground.frame(width: 80, height: 80) + Color.itemBackground.frame(width: 80, height: 80) + } + HStack { + Color.primaryText.frame(width: 80, height: 80) + Color.secondaryText.frame(width: 80, height: 80) + Color.tintColor.frame(width: 80, height: 80) + } + } + .padding() + .background(Color.background) + .environment(\.colorScheme, .light) + + // Dark Mode Preview + VStack(spacing: 20) { + Text("Dark Mode") + .font(.title) + HStack { + Color.background.frame(width: 80, height: 80) + Color.secondaryBackground.frame(width: 80, height: 80) + Color.itemBackground.frame(width: 80, height: 80) + } + HStack { + Color.primaryText.frame(width: 80, height: 80) + Color.secondaryText.frame(width: 80, height: 80) + Color.tintColor.frame(width: 80, height: 80) + } + } + .padding() + .background(Color.background) + .environment(\.colorScheme, .dark) } - } -} +} \ No newline at end of file diff --git a/V2er/General/V2erApp.swift b/V2er/General/V2erApp.swift index 737d201..f35d97b 100644 --- a/V2er/General/V2erApp.swift +++ b/V2er/General/V2erApp.swift @@ -14,20 +14,56 @@ struct V2erApp: App { public static var rootViewController: UIViewController? public static var statusBarState: UIStatusBarStyle = .darkContent public static var window: UIWindow? + @StateObject private var store = Store.shared init() { setupApperance() } private func setupApperance() { + // Navigation bar appearance will be set dynamically based on color scheme + let navAppearance = UINavigationBar.appearance() + navAppearance.isTranslucent = true + } + + var body: some Scene { + WindowGroup { + RootView { + RootHostView() + .environmentObject(store) + .preferredColorScheme(store.appState.settingState.appearance.colorScheme) + .onAppear { + updateNavigationBarAppearance(for: store.appState.settingState.appearance) + updateWindowInterfaceStyle(for: store.appState.settingState.appearance) + } + .onChange(of: store.appState.settingState.appearance) { newValue in + updateNavigationBarAppearance(for: newValue) + updateWindowInterfaceStyle(for: newValue) + } + } + } + } + + private func updateNavigationBarAppearance(for appearance: AppearanceMode) { let navbarAppearance = UINavigationBarAppearance() - let tintColor = UIColor.black + + // Determine if we should use dark mode + let isDarkMode: Bool + switch appearance { + case .light: + isDarkMode = false + case .dark: + isDarkMode = true + case .system: + isDarkMode = UITraitCollection.current.userInterfaceStyle == .dark + } + + let tintColor = isDarkMode ? UIColor.white : UIColor.black navbarAppearance.titleTextAttributes = [.foregroundColor: tintColor] navbarAppearance.largeTitleTextAttributes = [.foregroundColor: tintColor] navbarAppearance.backgroundColor = .clear let navAppearance = UINavigationBar.appearance() - navAppearance.isTranslucent = true navAppearance.standardAppearance = navbarAppearance navAppearance.compactAppearance = navbarAppearance navAppearance.scrollEdgeAppearance = navbarAppearance @@ -35,12 +71,23 @@ struct V2erApp: App { navAppearance.tintColor = tintColor } - var body: some Scene { - WindowGroup { - RootView { - RootHostView() - .environmentObject(Store.shared) - } + private func updateWindowInterfaceStyle(for appearance: AppearanceMode) { + // Get all windows and update their interface style + guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene else { return } + + let style: UIUserInterfaceStyle + switch appearance { + case .light: + style = .light + case .dark: + style = .dark + case .system: + style = .unspecified + } + + // Update all windows in the scene + windowScene.windows.forEach { window in + window.overrideUserInterfaceStyle = style } } diff --git a/V2er/Info.plist b/V2er/Info.plist index bf7762a..df25606 100644 --- a/V2er/Info.plist +++ b/V2er/Info.plist @@ -54,8 +54,6 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - UIUserInterfaceStyle - Light UIViewControllerBasedStatusBarAppearance diff --git a/V2er/State/DataFlow/Reducers/SettingReducer.swift b/V2er/State/DataFlow/Reducers/SettingReducer.swift index 631fec2..ec3613e 100644 --- a/V2er/State/DataFlow/Reducers/SettingReducer.swift +++ b/V2er/State/DataFlow/Reducers/SettingReducer.swift @@ -9,7 +9,20 @@ import Foundation func settingStateReducer(_ state: SettingState, _ action: Action) -> (SettingState, Action?) { - return (SettingState(), nil) + var state = state + var followingAction: Action? = action + + switch action { + case let action as SettingActions.ChangeAppearanceAction: + state.appearance = action.appearance + // Save to UserDefaults + UserDefaults.standard.set(action.appearance.rawValue, forKey: "appearanceMode") + followingAction = nil + default: + break + } + + return (state, followingAction) } diff --git a/V2er/State/DataFlow/State/SettingState.swift b/V2er/State/DataFlow/State/SettingState.swift index 8070bc2..29b6e58 100644 --- a/V2er/State/DataFlow/State/SettingState.swift +++ b/V2er/State/DataFlow/State/SettingState.swift @@ -7,7 +7,54 @@ // import Foundation +import SwiftUI struct SettingState: FluxState { + var appearance: AppearanceMode = .system + init() { + // Load saved preference + if let savedMode = UserDefaults.standard.string(forKey: "appearanceMode"), + let mode = AppearanceMode(rawValue: savedMode) { + self.appearance = mode + } + } } + +enum AppearanceMode: String, CaseIterable { + case light = "light" + case dark = "dark" + case system = "system" + + var displayName: String { + switch self { + case .light: + return "浅色" + case .dark: + return "深色" + case .system: + return "跟随系统" + } + } + + var colorScheme: ColorScheme? { + switch self { + case .light: + return .light + case .dark: + return .dark + case .system: + return nil + } + } +} + +// Temporary: Define actions here until SettingActions.swift is properly added to project +struct SettingActions { + private static let R: Reducer = .setting + + struct ChangeAppearanceAction: Action { + var target: Reducer = R + let appearance: AppearanceMode + } +} \ No newline at end of file diff --git a/V2er/View/Explore/SearchPage.swift b/V2er/View/Explore/SearchPage.swift index 5abc39a..5be5579 100644 --- a/V2er/View/Explore/SearchPage.swift +++ b/V2er/View/Explore/SearchPage.swift @@ -47,7 +47,7 @@ struct SearchPage: StateView { HStack(spacing: 0) { HStack { Image(systemName: "magnifyingglass") - .foregroundColor(.gray) + .foregroundColor(.secondaryText) TextField("sov2ex", text: bindingState.keyword) .disableAutocorrection(true) .autocapitalization(.none) @@ -83,7 +83,7 @@ struct SearchPage: StateView { Divider() } .visualBlur() - .background(Color.gray.opacity(0.35)) + .background(Color.secondaryText.opacity(0.35)) } @ViewBuilder diff --git a/V2er/View/Feed/FeedItemView.swift b/V2er/View/Feed/FeedItemView.swift index 8fc602b..30e6e47 100644 --- a/V2er/View/Feed/FeedItemView.swift +++ b/V2er/View/Feed/FeedItemView.swift @@ -30,13 +30,14 @@ struct FeedItemView: View { } Text(data.title.safe) // .fontWeight(.medium) - .foregroundColor(.bodyText) + .foregroundColor(.primaryText) .greedyWidth(.leading) .lineLimit(2) .padding(.top, 6) .padding(.vertical, 4) Text("评论\(data.replyNum.safe)") .font(.footnote) + .foregroundColor(.secondaryText) .greedyWidth(.trailing) } .padding(12) diff --git a/V2er/View/Feed/FeedPage.swift b/V2er/View/Feed/FeedPage.swift index dd782c5..bbc5ca0 100644 --- a/V2er/View/Feed/FeedPage.swift +++ b/V2er/View/Feed/FeedPage.swift @@ -33,17 +33,23 @@ struct FeedPage: BaseHomePageView { @ViewBuilder private var contentView: some View { - LazyVStack(spacing: 0) { - ForEach(state.feedInfo.items) { item in - NavigationLink(destination: FeedDetailPage(initData: item)) { - FeedItemView(data: item) + VStack(spacing: 0) { + LazyVStack(spacing: 0) { + ForEach(state.feedInfo.items) { item in + NavigationLink(destination: FeedDetailPage(initData: item)) { + FeedItemView(data: item) + } } } } .updatable(autoRefresh: state.showProgressView, hasMoreData: state.hasMoreData, scrollTop(tab: .feed)) { - await run(action: FeedActions.FetchData.Start()) + if AccountState.hasSignIn() { + await run(action: FeedActions.FetchData.Start()) + } } loadMore: { - await run(action: FeedActions.LoadMore.Start(state.willLoadPage)) + if AccountState.hasSignIn() { + await run(action: FeedActions.LoadMore.Start(state.willLoadPage)) + } } .background(Color.bgColor) } diff --git a/V2er/View/FeedDetail/AuthorInfoView.swift b/V2er/View/FeedDetail/AuthorInfoView.swift index d8f875f..2085e0d 100644 --- a/V2er/View/FeedDetail/AuthorInfoView.swift +++ b/V2er/View/FeedDetail/AuthorInfoView.swift @@ -60,7 +60,7 @@ struct AuthorInfoView: View { NavigationLink(destination: TagDetailPage(tag: tag, tagId: tagId)) { Text(tag) .font(.footnote) - .foregroundColor(.black) + .foregroundColor(.primaryText) .lineLimit(1) .padding(.horizontal, 14) .padding(.vertical, 8) diff --git a/V2er/View/FeedDetail/FeedDetailPage.swift b/V2er/View/FeedDetail/FeedDetailPage.swift index 667a906..a378bb1 100644 --- a/V2er/View/FeedDetail/FeedDetailPage.swift +++ b/V2er/View/FeedDetail/FeedDetailPage.swift @@ -150,7 +150,7 @@ struct FeedDetailPage: StateView, KeyboardReadable, InstanceIdentifiable { .padding(.bottom, isKeyboardVisiable ? 0 : topSafeAreaInset().bottom * 0.9) .padding(.top, 10) .padding(.horizontal, 10) - .background(Color.white) + .background(Color.itemBg) } } diff --git a/V2er/View/FeedDetail/HtmlView.swift b/V2er/View/FeedDetail/HtmlView.swift index c0c1e02..c1805a7 100644 --- a/V2er/View/FeedDetail/HtmlView.swift +++ b/V2er/View/FeedDetail/HtmlView.swift @@ -27,7 +27,8 @@ struct HtmlView: View { var body: some View { GeometryReader { geo in - Webview(html: html, imgs: imgs,height: $height, rendered: $rendered) + Webview(html: html, imgs: imgs, height: $height, rendered: $rendered) + .environmentObject(Store.shared) } .frame(height: height) } @@ -39,6 +40,8 @@ fileprivate struct Webview: UIViewRepresentable, WebViewHandlerDelegate { @Binding var height: CGFloat @Binding var rendered: Bool @State var loaded: Bool = false + @EnvironmentObject var store: Store + @Environment(\.colorScheme) var colorScheme // Make a coordinator to co-ordinate with WKWebView's default delegate functions func makeCoordinator() -> Coordinator { @@ -70,8 +73,17 @@ fileprivate struct Webview: UIViewRepresentable, WebViewHandlerDelegate { print("------updateUIView--------: \(self.rendered)") // if rendered { return } var content = Bundle.readString(name: "v2er", type: "html") - // TODO: dark mode - let isDark = false + // Determine dark mode based on app appearance setting + let isDark: Bool + let appearance = store.appState.settingState.appearance + switch appearance { + case .dark: + isDark = true + case .light: + isDark = false + case .system: + isDark = colorScheme == .dark + } let fontSize = 16 let params = "\(isDark), \(fontSize)" content = content?.replace(segs: "{injecttedContent}", with: html ?? .empty) diff --git a/V2er/View/FeedDetail/ReplyItemView.swift b/V2er/View/FeedDetail/ReplyItemView.swift index ba54266..1736dc1 100644 --- a/V2er/View/FeedDetail/ReplyItemView.swift +++ b/V2er/View/FeedDetail/ReplyItemView.swift @@ -22,7 +22,7 @@ struct ReplyItemView: View { .font(.system(size: 8)) .padding(.horizontal, 4) .padding(.vertical, 2) - .cornerBorder(radius: 3, borderWidth: 0.8, color: .black) + .cornerBorder(radius: 3, borderWidth: 0.8, color: .primaryText) .padding(.top, 2) .hide(!info.isOwner) } diff --git a/V2er/View/Login/LoginPage.swift b/V2er/View/Login/LoginPage.swift index fe08e1a..5fc0108 100644 --- a/V2er/View/Login/LoginPage.swift +++ b/V2er/View/Login/LoginPage.swift @@ -91,8 +91,8 @@ struct LoginPage: StateView { .submitLabel(.continue) .padding(.horizontal, padding) .frame(maxWidth: .infinity, maxHeight: height) - Color.gray - .opacity(0.2) + Color.separator + .opacity(0.5) .padding(.vertical, 14) .frame(width: 1.5, height: height) .padding(.horizontal, 2) @@ -116,8 +116,8 @@ struct LoginPage: StateView { .submitLabel(.go) .keyboardType(.asciiCapable) .disableAutocorrection(true) - Color.gray - .opacity(0.2) + Color.separator + .opacity(0.5) .padding(.vertical, 14) .frame(width: 1.5, height: height) .padding(.horizontal, 2) @@ -152,7 +152,7 @@ struct LoginPage: StateView { } label: { Text("Login") .font(.headline) - .foregroundColor(.white) + .foregroundColor(Color.itemBackground) .padding() .greedyWidth() .background(Color.tintColor) diff --git a/V2er/View/Login/TwoStepLoginPage.swift b/V2er/View/Login/TwoStepLoginPage.swift index d951ebd..d33fac6 100644 --- a/V2er/View/Login/TwoStepLoginPage.swift +++ b/V2er/View/Login/TwoStepLoginPage.swift @@ -19,26 +19,34 @@ struct TwoStepLoginPage: View { VStack(spacing: 16) { Text("两步验证") .font(.subheadline) + .foregroundColor(.primaryText) TextField("2FA码", text: $twoStepCode) + .textFieldStyle(RoundedBorderTextFieldStyle()) + .keyboardType(.numberPad) + .foregroundColor(.primaryText) .padding(.vertical, 6) .padding(.horizontal) - .background(Color.white.opacity(0.8)) - .cornerBorder(radius: 8) HStack(spacing: 16) { Spacer() Button { dispatch(LoginActions.TwoStepLoginCancel()) - } label: { Text("取消").opacity(0.8) } + } label: { + Text("取消") + .foregroundColor(.secondaryText) + } Button { dispatch(LoginActions.TwoStepLogin(input: twoStepCode)) - } label: { Text("确定") } + } label: { + Text("确定") + .foregroundColor(.tintColor) + .fontWeight(.medium) + } .disabled(twoStepCode.isEmpty) } - .foregroundColor(.bodyText) } .padding(20) - .visualBlur() - .cornerBorder(radius: 20, borderWidth: 0) + .background(Color.secondaryBackground) + .cornerRadius(20) .padding(50) } diff --git a/V2er/View/Me/CreateTopicPage.swift b/V2er/View/Me/CreateTopicPage.swift index e84a51c..a83cf3f 100644 --- a/V2er/View/Me/CreateTopicPage.swift +++ b/V2er/View/Me/CreateTopicPage.swift @@ -66,7 +66,7 @@ struct CreateTopicPage: StateView { } label: { Text(isPreviewing ? "编辑" : "预览") .font(.callout) - .foregroundColor(Color.white) + .foregroundColor(Color.background) .padding(.horizontal, 12) .padding(.vertical, 8) .background(Color.tintColor) @@ -106,7 +106,7 @@ struct CreateTopicPage: StateView { // show placeholder Text("如果标题能够表达完整内容, 此处可为空") .greedyFrame(.topLeading) - .foregroundColor(.gray) + .foregroundColor(.secondaryText) .debug() } else if isPreviewing { Text(state.content.attributedString) @@ -135,7 +135,7 @@ struct CreateTopicPage: StateView { } label: { Text("发布主题") .font(.callout) - .foregroundColor(Color.white) + .foregroundColor(Color.background) .padding(.horizontal, 12) .padding(.vertical, 8) .background(Color.tintColor) @@ -156,12 +156,12 @@ struct CreateTopicPage: StateView { private var sectionItemView: some View { HStack { Image(systemName: "grid.circle") - .foregroundColor(.gray) + .foregroundColor(.secondaryText) Text(state.selectedNode?.text ?? "选择节点") Spacer() Image(systemName: "chevron.right") .font(.body.weight(.regular)) - .foregroundColor(.gray) + .foregroundColor(.secondaryText) .padding(.trailing) } .padding() diff --git a/V2er/View/Me/MePage.swift b/V2er/View/Me/MePage.swift index 0732387..336f01a 100644 --- a/V2er/View/Me/MePage.swift +++ b/V2er/View/Me/MePage.swift @@ -31,17 +31,17 @@ struct MePage: BaseHomePageView { if !AccountState.hasSignIn() { VStack { Text("登录查看更多") - .foregroundColor(.white) + .foregroundColor(.primaryText) .font(.title2) Button { dispatch(LoginActions.ShowLoginPageAction()) } label: { Text("登录") .font(.headline) - .foregroundColor(.white) + .foregroundColor(Color.itemBackground) .padding() .padding(.horizontal, 50) - .background(Color.black) + .background(Color.tintColor) .cornerRadius(15) } } @@ -71,12 +71,12 @@ struct MePage: BaseHomePageView { .foregroundColor(Color.bodyText) Image(systemName: "chevron.right") .font(.body.weight(.regular)) - .foregroundColor(Color.gray) + .foregroundColor(Color.secondaryText) } } .padding(.horizontal, 12) .padding(.vertical, 16) - .background(.white) + .background(Color.itemBackground) .padding(.bottom, 8) .to { UserDetailPage(userId: AccountState.userName) diff --git a/V2er/View/Me/NodeChooserPage.swift b/V2er/View/Me/NodeChooserPage.swift index e9014f8..3b4308b 100644 --- a/V2er/View/Me/NodeChooserPage.swift +++ b/V2er/View/Me/NodeChooserPage.swift @@ -63,7 +63,7 @@ struct NodeChooserPage: View { HStack { HStack { Image(systemName: "magnifyingglass") - .foregroundColor(.gray) + .foregroundColor(.secondaryText) TextField("Search ...", text: $filterText) .disableAutocorrection(true) .autocapitalization(.none) diff --git a/V2er/View/Me/UserDetailPage.swift b/V2er/View/Me/UserDetailPage.swift index 1a9b225..536df15 100644 --- a/V2er/View/Me/UserDetailPage.swift +++ b/V2er/View/Me/UserDetailPage.swift @@ -42,7 +42,7 @@ struct UserDetailPage: StateView { } var foreGroundColor: SwiftUI.Color { - return shouldHideNavbar ? .white.opacity(0.9) : .tintColor + return shouldHideNavbar ? Color.primaryText.opacity(0.9) : .tintColor } var body: some View { @@ -77,7 +77,7 @@ struct UserDetailPage: StateView { .fade(duration: 0.25) .resizable() .blur(radius: 80, opaque: true) - .overlay(Color.black.opacity(withAnimation {shouldHideNavbar ? 0.3 : 0.1})) + .overlay(Color.dynamic(light: .black, dark: .white).opacity(withAnimation {shouldHideNavbar ? 0.3 : 0.1})) .frame(maxWidth: .infinity, maxHeight: height) Spacer().background(.clear) } @@ -172,7 +172,7 @@ struct UserDetailPage: StateView { AvatarView(url: model.avatar, size: heightOfNodeImage) HStack(alignment: .center,spacing: 4) { Circle() - .fill(state.model.isOnline ? .green : .gray) + .fill(state.model.isOnline ? .green : Color.secondaryText) .frame(width: 8, height: 8) Text(model.userName) .font(.headline.weight(.semibold)) @@ -203,7 +203,7 @@ struct UserDetailPage: StateView { .background(Color.lightGray, in: RoundedRectangle(cornerRadius: 10)) .padding(.horizontal, 12) .padding(.vertical, 10) - .background(.white) + .background(Color.itemBackground) .clipCorner(12, corners: [.topLeft, .topRight]) } @@ -298,7 +298,7 @@ struct UserDetailPage: StateView { NavigationLink(destination: TagDetailPage()) { Text(data.tag) .font(.footnote) - .foregroundColor(.black) + .foregroundColor(Color.primaryText) .lineLimit(1) .padding(.horizontal, 14) .padding(.vertical, 8) @@ -339,14 +339,14 @@ struct UserDetailPage: StateView { } label: { Text(title) .fontWeight(.bold) - .foregroundColor(isSelected ? .white.opacity(0.9) : .tintColor) + .foregroundColor(isSelected ? Color.itemBackground.opacity(0.9) : .tintColor) .frame(maxWidth: .infinity) .padding(.vertical, 8) .background { VStack { if isSelected { RoundedRectangle(cornerRadius: 10) - .fill(.black) + .fill(Color.primaryText) .matchedGeometryEffect(id: "TAB", in: animation) } } diff --git a/V2er/View/Settings/AppearanceSettingView.swift b/V2er/View/Settings/AppearanceSettingView.swift index 095b0aa..a531739 100644 --- a/V2er/View/Settings/AppearanceSettingView.swift +++ b/V2er/View/Settings/AppearanceSettingView.swift @@ -9,22 +9,91 @@ import SwiftUI struct AppearanceSettingView: View { + @EnvironmentObject private var store: Store + @State private var selectedAppearance: AppearanceMode = .system + var body: some View { formView .navBar("外观设置") + .onAppear { + selectedAppearance = store.appState.settingState.appearance + } } @ViewBuilder private var formView: some View { ScrollView { - SectionItemView("字体大小") -// .to {} + VStack(spacing: 0) { + // Dark Mode Section + VStack(alignment: .leading, spacing: 12) { + Text("主题") + .font(.caption) + .foregroundColor(.secondaryText) + .padding(.horizontal) + .padding(.top, 8) + + VStack(spacing: 0) { + ForEach(AppearanceMode.allCases, id: \.self) { mode in + Button(action: { + selectedAppearance = mode + dispatch(SettingActions.ChangeAppearanceAction(appearance: mode)) + }) { + HStack { + Text(mode.displayName) + .foregroundColor(.primaryText) + Spacer() + if selectedAppearance == mode { + Image(systemName: "checkmark") + .foregroundColor(.tintColor) + .font(.system(size: 14, weight: .semibold)) + } + } + .padding(.horizontal) + .padding(.vertical, 12) + .background(Color.itemBackground) + } + + if mode != AppearanceMode.allCases.last { + Divider() + .padding(.leading) + } + } + } + .background(Color.itemBackground) + .cornerRadius(10) + .padding(.horizontal) + } + .padding(.top) + + // Font Size Section + VStack(alignment: .leading, spacing: 12) { + Text("字体") + .font(.caption) + .foregroundColor(.secondaryText) + .padding(.horizontal) + .padding(.top, 24) + + SectionItemView("字体大小") + .background(Color.itemBackground) + .cornerRadius(10) + .padding(.horizontal) + } + } } + .background(Color.background.ignoresSafeArea()) } } struct AppearanceSettingView_Previews: PreviewProvider { static var previews: some View { - AppearanceSettingView() + Group { + AppearanceSettingView() + .environmentObject(Store.shared) + .environment(\.colorScheme, .light) + + AppearanceSettingView() + .environmentObject(Store.shared) + .environment(\.colorScheme, .dark) + } } -} +} \ No newline at end of file diff --git a/V2er/View/Settings/SettingsPage.swift b/V2er/View/Settings/SettingsPage.swift index 5ab607a..932f293 100644 --- a/V2er/View/Settings/SettingsPage.swift +++ b/V2er/View/Settings/SettingsPage.swift @@ -22,8 +22,11 @@ struct SettingsPage: View { private var formView: some View { ScrollView { VStack(spacing: 0) { - SectionItemView("通用设置", showDivider: false) + SectionItemView("外观设置", showDivider: false) .padding(.top, 8) + .to { AppearanceSettingView() } + + SectionItemView("通用设置") .to { OtherSettingsView() } SectionItemView("帮助与反馈") @@ -43,7 +46,7 @@ struct SettingsPage: View { .foregroundColor(Color.tintColor) Image(systemName: "chevron.right") .font(.body.weight(.regular)) - .foregroundColor(.gray) + .foregroundColor(.secondaryText) .padding(.trailing, 16) } } diff --git a/V2er/View/Tag/TagDetailPage.swift b/V2er/View/Tag/TagDetailPage.swift index dcd5aa7..4f00162 100644 --- a/V2er/View/Tag/TagDetailPage.swift +++ b/V2er/View/Tag/TagDetailPage.swift @@ -40,7 +40,7 @@ struct TagDetailPage: StateView, InstanceIdentifiable { } private var foreGroundColor: Color { - shouldHideNavbar ? .white.opacity(0.9) : .tintColor + shouldHideNavbar ? Color.primaryText.opacity(0.9) : .tintColor } private var statusBarStyle: UIStatusBarStyle { @@ -77,7 +77,7 @@ struct TagDetailPage: StateView, InstanceIdentifiable { .fade(duration: 0.25) .resizable() .blur(radius: 80, opaque: true) - .overlay(Color.black.opacity(withAnimation {shouldHideNavbar ? 0.3 : 0.1})) + .overlay(Color.dynamic(light: .black, dark: .white).opacity(withAnimation {shouldHideNavbar ? 0.3 : 0.1})) .frame(height: bannerViewHeight * 1.2 + max(scrollY, 0)) Spacer() } @@ -206,7 +206,7 @@ struct TagDetailPage: StateView, InstanceIdentifiable { } } } - .background(.white) + .background(Color.itemBackground) .clipCorner(12, corners: [.topLeft, .topRight]) } diff --git a/V2er/View/Widget/FlowStack.swift b/V2er/View/Widget/FlowStack.swift index 178e734..93f9ef4 100644 --- a/V2er/View/Widget/FlowStack.swift +++ b/V2er/View/Widget/FlowStack.swift @@ -98,7 +98,7 @@ struct FlowStack_Previews: PreviewProvider { { Text($0) .font(.footnote) - .foregroundColor(.black) + .foregroundColor(.primaryText) .lineLimit(1) .padding(.horizontal, 14) .padding(.vertical, 8) diff --git a/V2er/View/Widget/NodeView.swift b/V2er/View/Widget/NodeView.swift index 6c68252..19f8641 100644 --- a/V2er/View/Widget/NodeView.swift +++ b/V2er/View/Widget/NodeView.swift @@ -25,11 +25,11 @@ struct NodeView: View { } label: { Text(name) .font(.footnote) - .foregroundColor(.black) + .foregroundColor(Color.dynamic(light: .hex(0x666666), dark: .hex(0xCCCCCC))) .lineLimit(1) .padding(.horizontal, 14) .padding(.vertical, 8) - .background(Color.lightGray) + .background(Color.dynamic(light: Color.hex(0xF5F5F5), dark: Color.hex(0x2C2C2E))) } } diff --git a/V2er/View/Widget/RichTextView/Webview.swift b/V2er/View/Widget/RichTextView/Webview.swift index b592ea0..9c0c8ce 100644 --- a/V2er/View/Widget/RichTextView/Webview.swift +++ b/V2er/View/Widget/RichTextView/Webview.swift @@ -105,25 +105,31 @@ struct Webview : UIViewRepresentable { } + private let lightTextColor = "#1C1C1E" + private let darkTextColor = "#F2F2F7" + func css(colorScheme: colorScheme) -> String { + let imgStyle = "img{max-height: 100%; min-height: 100%; height:auto; max-width: 100%; width:auto;margin-bottom:5px; border-radius: \(imageRadius)px;}" + let iframeStyle = "iframe{width:100%; height:250px;}" + let textStyleBase = "h1, h2, h3, h4, h5, h6, p, dl, ol, ul, pre, blockquote {text-align:left|right|center; line-height: \(lineHeight)%; font-family: '\(fontName(fontType: self.fontType))'; color: " + let importantSuffix = colorImportant == false ? "" : "!important" + switch colorScheme { case .light: return """ """ - case .dark : + case .dark: return """ """ @@ -131,14 +137,14 @@ struct Webview : UIViewRepresentable { return """ diff --git a/V2er/View/Widget/SectionItemView.swift b/V2er/View/Widget/SectionItemView.swift index 5fdec03..9688df9 100644 --- a/V2er/View/Widget/SectionItemView.swift +++ b/V2er/View/Widget/SectionItemView.swift @@ -27,7 +27,7 @@ struct SectionItemView: View { SectionView(title, icon: icon, showDivider: showDivider) { Image(systemName: "chevron.right") .font(.body.weight(.regular)) - .foregroundColor(.gray) + .foregroundColor(.secondaryText) .padding(.trailing, paddingH) } } @@ -65,6 +65,6 @@ struct SectionView: View { .padding(.vertical, 17) .divider(showDivider ? 0.8 : 0.0) } - .background(.white) + .background(Color.itemBackground) } } diff --git a/V2er/View/Widget/Toast.swift b/V2er/View/Widget/Toast.swift index 463e48f..dd2ad97 100644 --- a/V2er/View/Widget/Toast.swift +++ b/V2er/View/Widget/Toast.swift @@ -44,7 +44,7 @@ struct DefaultToastView: View { var body: some View { Label(title, systemImage: icon) - .foregroundColor(.bodyText) + .foregroundColor(.primaryText) .padding(.horizontal, 20) .padding(.vertical, 12) } @@ -58,9 +58,9 @@ extension View { self if isPresented.wrappedValue { content() - .visualBlur(bg: .white.opacity(0.95)) + .background(Color.secondaryBackground.opacity(0.98)) .cornerRadius(99) - .shadow(color: .black.opacity(0.2), radius: 1.5) + .shadow(color: Color.primaryText.opacity(0.3), radius: 3) .padding(.top, paddingTop) .transition(AnyTransition.move(edge: .top)) .zIndex(1) diff --git a/website b/website index c3bbc04..b5b4f70 160000 --- a/website +++ b/website @@ -1 +1 @@ -Subproject commit c3bbc04bb0d0c1f262b1b6e28a464c8b1144e199 +Subproject commit b5b4f70430003ea84edf40eaea25dc484076505a