Add dark mode support with appearance picker and fix hardcoded colors
Add user-facing appearance setting (System/Light/Dark) wired via preferredColorScheme at the app root. Replace hardcoded .black tints and foreground styles with .primary so toolbar buttons and text remain visible in dark mode. Remove profile picture from settings and SwiftSoup from acknowledgements. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.DS_Store
|
||||||
|
Nextcloud Cookbook iOS Client.xcodeproj/project.xcworkspace/xcuserdata/hendrik.hogertz.xcuserdatad/UserInterfaceState.xcuserstate
|
||||||
|
Nextcloud Cookbook iOS Client.xcodeproj/xcuserdata/hendrik.hogertz.xcuserdatad/xcschemes/xcschememanagement.plist
|
||||||
@@ -69,6 +69,7 @@
|
|||||||
C1F0AB022D0B000100000001 /* ImportURLSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F0AB012D0B000100000001 /* ImportURLSheet.swift */; };
|
C1F0AB022D0B000100000001 /* ImportURLSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F0AB012D0B000100000001 /* ImportURLSheet.swift */; };
|
||||||
A9E78A2B2BE7799F00206866 /* JsonAny.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E78A2A2BE7799F00206866 /* JsonAny.swift */; };
|
A9E78A2B2BE7799F00206866 /* JsonAny.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E78A2A2BE7799F00206866 /* JsonAny.swift */; };
|
||||||
A9FA2AB62B5079B200A43702 /* alarm_sound_0.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = A9FA2AB52B5079B200A43702 /* alarm_sound_0.mp3 */; };
|
A9FA2AB62B5079B200A43702 /* alarm_sound_0.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = A9FA2AB52B5079B200A43702 /* alarm_sound_0.mp3 */; };
|
||||||
|
A1B2C3D52F0A000100000001 /* AppearanceMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D42F0A000100000001 /* AppearanceMode.swift */; };
|
||||||
D1A0CE012D0A000100000001 /* GroceryListMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A0CE002D0A000100000001 /* GroceryListMode.swift */; };
|
D1A0CE012D0A000100000001 /* GroceryListMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A0CE002D0A000100000001 /* GroceryListMode.swift */; };
|
||||||
D1A0CE032D0A000200000002 /* RemindersGroceryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A0CE022D0A000200000002 /* RemindersGroceryStore.swift */; };
|
D1A0CE032D0A000200000002 /* RemindersGroceryStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A0CE022D0A000200000002 /* RemindersGroceryStore.swift */; };
|
||||||
D1A0CE052D0A000300000003 /* GroceryListManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A0CE042D0A000300000003 /* GroceryListManager.swift */; };
|
D1A0CE052D0A000300000003 /* GroceryListManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1A0CE042D0A000300000003 /* GroceryListManager.swift */; };
|
||||||
@@ -165,6 +166,7 @@
|
|||||||
A9DA25D42B82096B0061FC2B /* Nextcloud-Cookbook-iOS-Client-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Nextcloud-Cookbook-iOS-Client-Info.plist"; sourceTree = SOURCE_ROOT; };
|
A9DA25D42B82096B0061FC2B /* Nextcloud-Cookbook-iOS-Client-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Nextcloud-Cookbook-iOS-Client-Info.plist"; sourceTree = SOURCE_ROOT; };
|
||||||
A9E78A2A2BE7799F00206866 /* JsonAny.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsonAny.swift; sourceTree = "<group>"; };
|
A9E78A2A2BE7799F00206866 /* JsonAny.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JsonAny.swift; sourceTree = "<group>"; };
|
||||||
A9FA2AB52B5079B200A43702 /* alarm_sound_0.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = alarm_sound_0.mp3; sourceTree = "<group>"; };
|
A9FA2AB52B5079B200A43702 /* alarm_sound_0.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = alarm_sound_0.mp3; sourceTree = "<group>"; };
|
||||||
|
A1B2C3D42F0A000100000001 /* AppearanceMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceMode.swift; sourceTree = "<group>"; };
|
||||||
D1A0CE002D0A000100000001 /* GroceryListMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroceryListMode.swift; sourceTree = "<group>"; };
|
D1A0CE002D0A000100000001 /* GroceryListMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroceryListMode.swift; sourceTree = "<group>"; };
|
||||||
D1A0CE022D0A000200000002 /* RemindersGroceryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemindersGroceryStore.swift; sourceTree = "<group>"; };
|
D1A0CE022D0A000200000002 /* RemindersGroceryStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemindersGroceryStore.swift; sourceTree = "<group>"; };
|
||||||
D1A0CE042D0A000300000003 /* GroceryListManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroceryListManager.swift; sourceTree = "<group>"; };
|
D1A0CE042D0A000300000003 /* GroceryListManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroceryListManager.swift; sourceTree = "<group>"; };
|
||||||
@@ -312,6 +314,7 @@
|
|||||||
A70171C52AB4C43A00064C43 /* DataModels.swift */,
|
A70171C52AB4C43A00064C43 /* DataModels.swift */,
|
||||||
A97B4D312B80B3E900EC1A88 /* RecipeModels.swift */,
|
A97B4D312B80B3E900EC1A88 /* RecipeModels.swift */,
|
||||||
A9BBB38F2B91BE31002DA7FF /* ObservableRecipeDetail.swift */,
|
A9BBB38F2B91BE31002DA7FF /* ObservableRecipeDetail.swift */,
|
||||||
|
A1B2C3D42F0A000100000001 /* AppearanceMode.swift */,
|
||||||
D1A0CE002D0A000100000001 /* GroceryListMode.swift */,
|
D1A0CE002D0A000100000001 /* GroceryListMode.swift */,
|
||||||
D1A0CE022D0A000200000002 /* RemindersGroceryStore.swift */,
|
D1A0CE022D0A000200000002 /* RemindersGroceryStore.swift */,
|
||||||
D1A0CE042D0A000300000003 /* GroceryListManager.swift */,
|
D1A0CE042D0A000300000003 /* GroceryListManager.swift */,
|
||||||
@@ -656,6 +659,7 @@
|
|||||||
A76B8A6F2ADFFA8800096CEC /* SupportedLanguage.swift in Sources */,
|
A76B8A6F2ADFFA8800096CEC /* SupportedLanguage.swift in Sources */,
|
||||||
A977D0E02B600318009783A9 /* RecipeTabView.swift in Sources */,
|
A977D0E02B600318009783A9 /* RecipeTabView.swift in Sources */,
|
||||||
A703226F2ABB1DD700D7C4ED /* ColorExtension.swift in Sources */,
|
A703226F2ABB1DD700D7C4ED /* ColorExtension.swift in Sources */,
|
||||||
|
A1B2C3D52F0A000100000001 /* AppearanceMode.swift in Sources */,
|
||||||
D1A0CE012D0A000100000001 /* GroceryListMode.swift in Sources */,
|
D1A0CE012D0A000100000001 /* GroceryListMode.swift in Sources */,
|
||||||
D1A0CE032D0A000200000002 /* RemindersGroceryStore.swift in Sources */,
|
D1A0CE032D0A000200000002 /* RemindersGroceryStore.swift in Sources */,
|
||||||
D1A0CE052D0A000300000003 /* GroceryListManager.swift in Sources */,
|
D1A0CE052D0A000300000003 /* GroceryListManager.swift in Sources */,
|
||||||
|
|||||||
22
Nextcloud Cookbook iOS Client/Data/AppearanceMode.swift
Normal file
22
Nextcloud Cookbook iOS Client/Data/AppearanceMode.swift
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// AppearanceMode.swift
|
||||||
|
// Nextcloud Cookbook iOS Client
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum AppearanceMode: String, CaseIterable {
|
||||||
|
case system = "system"
|
||||||
|
case light = "light"
|
||||||
|
case dark = "dark"
|
||||||
|
|
||||||
|
func descriptor() -> String {
|
||||||
|
switch self {
|
||||||
|
case .system: return String(localized: "System")
|
||||||
|
case .light: return String(localized: "Light")
|
||||||
|
case .dark: return String(localized: "Dark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static let allValues: [AppearanceMode] = AppearanceMode.allCases
|
||||||
|
}
|
||||||
@@ -144,7 +144,13 @@ class UserSettings: ObservableObject {
|
|||||||
UserDefaults.standard.set(mealPlanSyncEnabled, forKey: "mealPlanSyncEnabled")
|
UserDefaults.standard.set(mealPlanSyncEnabled, forKey: "mealPlanSyncEnabled")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Published var appearanceMode: String {
|
||||||
|
didSet {
|
||||||
|
UserDefaults.standard.set(appearanceMode, forKey: "appearanceMode")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
self.username = UserDefaults.standard.object(forKey: "username") as? String ?? ""
|
self.username = UserDefaults.standard.object(forKey: "username") as? String ?? ""
|
||||||
self.token = UserDefaults.standard.object(forKey: "token") as? String ?? ""
|
self.token = UserDefaults.standard.object(forKey: "token") as? String ?? ""
|
||||||
@@ -168,7 +174,8 @@ class UserSettings: ObservableObject {
|
|||||||
self.remindersListIdentifier = UserDefaults.standard.object(forKey: "remindersListIdentifier") as? String ?? ""
|
self.remindersListIdentifier = UserDefaults.standard.object(forKey: "remindersListIdentifier") as? String ?? ""
|
||||||
self.grocerySyncEnabled = UserDefaults.standard.object(forKey: "grocerySyncEnabled") as? Bool ?? true
|
self.grocerySyncEnabled = UserDefaults.standard.object(forKey: "grocerySyncEnabled") as? Bool ?? true
|
||||||
self.mealPlanSyncEnabled = UserDefaults.standard.object(forKey: "mealPlanSyncEnabled") as? Bool ?? true
|
self.mealPlanSyncEnabled = UserDefaults.standard.object(forKey: "mealPlanSyncEnabled") as? Bool ?? true
|
||||||
|
self.appearanceMode = UserDefaults.standard.object(forKey: "appearanceMode") as? String ?? AppearanceMode.system.rawValue
|
||||||
|
|
||||||
if authString == "" {
|
if authString == "" {
|
||||||
if token != "" && username != "" {
|
if token != "" && username != "" {
|
||||||
let loginString = "\(self.username):\(self.token)"
|
let loginString = "\(self.username):\(self.token)"
|
||||||
|
|||||||
@@ -694,6 +694,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"An HTML parsing and web scraping library for Swift. Used for importing schema.org recipes from websites." : {
|
"An HTML parsing and web scraping library for Swift. Used for importing schema.org recipes from websites." : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -782,6 +783,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Appearance" : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Erscheinungsbild"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Apariencia"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Apparence"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Apple Reminders" : {
|
"Apple Reminders" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
@@ -995,6 +1018,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Choose whether the app follows the system appearance or always uses light or dark mode." : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Wähle, ob die App dem Erscheinungsbild des Systems folgt oder immer den hellen oder dunklen Modus verwendet."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Elige si la app sigue la apariencia del sistema o siempre usa el modo claro u oscuro."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Choisissez si l'app suit l'apparence du système ou utilise toujours le mode clair ou sombre."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Clear" : {
|
"Clear" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
@@ -1293,6 +1338,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Dark" : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Dunkel"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Oscuro"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Sombre"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Decimal number format" : {
|
"Decimal number format" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
@@ -2536,6 +2603,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Light" : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Hell"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Claro"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Clair"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"List your tools here. 🍴" : {
|
"List your tools here. 🍴" : {
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -4460,6 +4549,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"SwiftSoup" : {
|
"SwiftSoup" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@@ -4483,6 +4573,28 @@
|
|||||||
},
|
},
|
||||||
"Sync grocery list across devices" : {
|
"Sync grocery list across devices" : {
|
||||||
|
|
||||||
|
},
|
||||||
|
"System" : {
|
||||||
|
"localizations" : {
|
||||||
|
"de" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "System"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"es" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Sistema"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fr" : {
|
||||||
|
"stringUnit" : {
|
||||||
|
"state" : "translated",
|
||||||
|
"value" : "Système"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"Thank you for downloading" : {
|
"Thank you for downloading" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@@ -5170,6 +5282,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Username: %@" : {
|
"Username: %@" : {
|
||||||
|
"extractionState" : "stale",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"de" : {
|
"de" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
|
|||||||
@@ -13,7 +13,16 @@ import SwiftUI
|
|||||||
struct Nextcloud_Cookbook_iOS_ClientApp: App {
|
struct Nextcloud_Cookbook_iOS_ClientApp: App {
|
||||||
@AppStorage("onboarding") var onboarding = true
|
@AppStorage("onboarding") var onboarding = true
|
||||||
@AppStorage("language") var language = Locale.current.language.languageCode?.identifier ?? "en"
|
@AppStorage("language") var language = Locale.current.language.languageCode?.identifier ?? "en"
|
||||||
|
@AppStorage("appearanceMode") var appearanceMode = AppearanceMode.system.rawValue
|
||||||
|
|
||||||
|
var colorScheme: ColorScheme? {
|
||||||
|
switch appearanceMode {
|
||||||
|
case AppearanceMode.light.rawValue: return .light
|
||||||
|
case AppearanceMode.dark.rawValue: return .dark
|
||||||
|
default: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
ZStack {
|
ZStack {
|
||||||
@@ -23,6 +32,7 @@ struct Nextcloud_Cookbook_iOS_ClientApp: App {
|
|||||||
MainView()
|
MainView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.preferredColorScheme(colorScheme)
|
||||||
.transition(.slide)
|
.transition(.slide)
|
||||||
.environment(
|
.environment(
|
||||||
\.locale,
|
\.locale,
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ struct AllRecipesListView: View {
|
|||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
.tint(.nextcloudBlue)
|
.tint(.primary)
|
||||||
}.padding()
|
}.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ struct RecipeListView: View {
|
|||||||
.bold()
|
.bold()
|
||||||
}
|
}
|
||||||
.buttonStyle(.bordered)
|
.buttonStyle(.bordered)
|
||||||
.tint(.nextcloudBlue)
|
.tint(.primary)
|
||||||
}.padding()
|
}.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,30 +22,6 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Form {
|
Form {
|
||||||
HStack(alignment: .center) {
|
|
||||||
if let avatarImage = viewModel.avatarImage {
|
|
||||||
Image(uiImage: avatarImage)
|
|
||||||
.resizable()
|
|
||||||
.clipShape(Circle())
|
|
||||||
.frame(width: 100, height: 100)
|
|
||||||
|
|
||||||
}
|
|
||||||
if let userData = viewModel.userData {
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(userData.userDisplayName)
|
|
||||||
.font(.title)
|
|
||||||
.padding(.leading)
|
|
||||||
Text("Username: \(userData.userId)")
|
|
||||||
.font(.subheadline)
|
|
||||||
.padding(.leading)
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Add actions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Picker("Select a default cookbook", selection: $userSettings.defaultCategory) {
|
Picker("Select a default cookbook", selection: $userSettings.defaultCategory) {
|
||||||
Text("None").tag("None")
|
Text("None").tag("None")
|
||||||
@@ -59,6 +35,16 @@ struct SettingsView: View {
|
|||||||
Text("The selected cookbook will open on app launch by default.")
|
Text("The selected cookbook will open on app launch by default.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
Picker("Appearance", selection: $userSettings.appearanceMode) {
|
||||||
|
ForEach(AppearanceMode.allValues, id: \.self) { mode in
|
||||||
|
Text(mode.descriptor()).tag(mode.rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} footer: {
|
||||||
|
Text("Choose whether the app follows the system appearance or always uses light or dark mode.")
|
||||||
|
}
|
||||||
|
|
||||||
Section {
|
Section {
|
||||||
Picker("Grocery list storage", selection: $userSettings.groceryListMode) {
|
Picker("Grocery list storage", selection: $userSettings.groceryListMode) {
|
||||||
ForEach(GroceryListMode.allValues, id: \.self) { mode in
|
ForEach(GroceryListMode.allValues, id: \.self) { mode in
|
||||||
@@ -211,13 +197,6 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Section(header: Text("Acknowledgements")) {
|
Section(header: Text("Acknowledgements")) {
|
||||||
VStack(alignment: .leading) {
|
|
||||||
if let url = URL(string: "https://github.com/scinfu/SwiftSoup") {
|
|
||||||
Link("SwiftSoup", destination: url)
|
|
||||||
.font(.headline)
|
|
||||||
Text("An HTML parsing and web scraping library for Swift. Used for importing schema.org recipes from websites.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
if let url = URL(string: "https://github.com/techprimate/TPPDF") {
|
if let url = URL(string: "https://github.com/techprimate/TPPDF") {
|
||||||
Link("TPPDF", destination: url)
|
Link("TPPDF", destination: url)
|
||||||
@@ -240,7 +219,6 @@ struct SettingsView: View {
|
|||||||
Text(viewModel.alertType.getMessage())
|
Text(viewModel.alertType.getMessage())
|
||||||
}
|
}
|
||||||
.task {
|
.task {
|
||||||
await viewModel.getUserData()
|
|
||||||
remindersPermission = groceryListManager.remindersPermissionStatus
|
remindersPermission = groceryListManager.remindersPermissionStatus
|
||||||
if remindersPermission == .fullAccess {
|
if remindersPermission == .fullAccess {
|
||||||
reminderLists = groceryListManager.availableReminderLists()
|
reminderLists = groceryListManager.availableReminderLists()
|
||||||
@@ -270,9 +248,6 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
extension SettingsView {
|
extension SettingsView {
|
||||||
class ViewModel: ObservableObject {
|
class ViewModel: ObservableObject {
|
||||||
@Published var avatarImage: UIImage? = nil
|
|
||||||
@Published var userData: UserData? = nil
|
|
||||||
|
|
||||||
@Published var showAlert: Bool = false
|
@Published var showAlert: Bool = false
|
||||||
fileprivate var alertType: SettingsAlert = .NONE
|
fileprivate var alertType: SettingsAlert = .NONE
|
||||||
|
|
||||||
@@ -297,16 +272,6 @@ extension SettingsView {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserData() async {
|
|
||||||
let (data, _) = await NextcloudApi.getAvatar()
|
|
||||||
let (userData, _) = await NextcloudApi.getHoverCard()
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.avatarImage = data
|
|
||||||
self.userData = userData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ struct GroceryListTabView: View {
|
|||||||
groceryList.deleteAll()
|
groceryList.deleteAll()
|
||||||
} label: {
|
} label: {
|
||||||
Text("Delete")
|
Text("Delete")
|
||||||
.foregroundStyle(Color.nextcloudBlue)
|
.foregroundStyle(.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ fileprivate struct MealPlanDayRow: View {
|
|||||||
.frame(maxWidth: .infinity, minHeight: 44)
|
.frame(maxWidth: .infinity, minHeight: 44)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 8)
|
RoundedRectangle(cornerRadius: 8)
|
||||||
.fill(Color.nextcloudBlue.opacity(0.1))
|
.fill(Color.nextcloudBlue.opacity(0.15))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ struct RecipeTabView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tint(.nextcloudBlue)
|
.tint(.primary)
|
||||||
.sheet(isPresented: $viewModel.showImportURLSheet) {
|
.sheet(isPresented: $viewModel.showImportURLSheet) {
|
||||||
ImportURLSheet { recipeDetail in
|
ImportURLSheet { recipeDetail in
|
||||||
viewModel.navigateToImportedRecipe(recipeDetail: recipeDetail)
|
viewModel.navigateToImportedRecipe(recipeDetail: recipeDetail)
|
||||||
|
|||||||
Reference in New Issue
Block a user