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:
2026-02-15 06:31:14 +01:00
parent c38d4075be
commit 02118e3d7a
12 changed files with 177 additions and 53 deletions

View File

@@ -56,7 +56,7 @@ struct AllRecipesListView: View {
.bold()
}
.buttonStyle(.bordered)
.tint(.nextcloudBlue)
.tint(.primary)
}.padding()
}
}

View File

@@ -68,7 +68,7 @@ struct RecipeListView: View {
.bold()
}
.buttonStyle(.bordered)
.tint(.nextcloudBlue)
.tint(.primary)
}.padding()
}
}

View File

@@ -22,30 +22,6 @@ struct SettingsView: View {
var body: some View {
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 {
Picker("Select a default cookbook", selection: $userSettings.defaultCategory) {
Text("None").tag("None")
@@ -59,6 +35,16 @@ struct SettingsView: View {
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 {
Picker("Grocery list storage", selection: $userSettings.groceryListMode) {
ForEach(GroceryListMode.allValues, id: \.self) { mode in
@@ -211,13 +197,6 @@ struct SettingsView: View {
}
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) {
if let url = URL(string: "https://github.com/techprimate/TPPDF") {
Link("TPPDF", destination: url)
@@ -240,7 +219,6 @@ struct SettingsView: View {
Text(viewModel.alertType.getMessage())
}
.task {
await viewModel.getUserData()
remindersPermission = groceryListManager.remindersPermissionStatus
if remindersPermission == .fullAccess {
reminderLists = groceryListManager.availableReminderLists()
@@ -270,9 +248,6 @@ struct SettingsView: View {
extension SettingsView {
class ViewModel: ObservableObject {
@Published var avatarImage: UIImage? = nil
@Published var userData: UserData? = nil
@Published var showAlert: Bool = false
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
}
}
}
}

View File

@@ -55,7 +55,7 @@ struct GroceryListTabView: View {
groceryList.deleteAll()
} label: {
Text("Delete")
.foregroundStyle(Color.nextcloudBlue)
.foregroundStyle(.primary)
}
}
}

View File

@@ -191,7 +191,7 @@ fileprivate struct MealPlanDayRow: View {
.frame(maxWidth: .infinity, minHeight: 44)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(Color.nextcloudBlue.opacity(0.1))
.fill(Color.nextcloudBlue.opacity(0.15))
)
}
}

View File

@@ -163,7 +163,7 @@ struct RecipeTabView: View {
}
}
}
.tint(.nextcloudBlue)
.tint(.primary)
.sheet(isPresented: $viewModel.showImportURLSheet) {
ImportURLSheet { recipeDetail in
viewModel.navigateToImportedRecipe(recipeDetail: recipeDetail)