From 0663ba9e59a4efdbfd1022932d13d23909560bee Mon Sep 17 00:00:00 2001 From: neon443 <69979447+neon443@users.noreply.github.com> Date: Fri, 13 Jun 2025 21:28:37 +0100 Subject: [PATCH] add settings on mac add whats new on mac clean up settings on ios clean up whats new + liquid glass --- Config.xcconfig | 2 +- MacNearFuture/MacNearFutureApp.swift | 7 +- MacNearFuture/NFCommands.swift | 2 +- MacNearFuture/Views/ContentViewMac.swift | 10 +- MacNearFuture/Views/SettingsView.swift | 155 +++++++++++++++++++ NearFuture.xcodeproj/project.pbxproj | 8 +- NearFuture/Views/Settings/SettingsView.swift | 16 +- NearFuture/Views/Settings/WhatsNewView.swift | 43 ++--- Shared/ViewModifiers.swift | 11 ++ 9 files changed, 210 insertions(+), 44 deletions(-) create mode 100644 MacNearFuture/Views/SettingsView.swift diff --git a/Config.xcconfig b/Config.xcconfig index 7118d9a..b3278ca 100644 --- a/Config.xcconfig +++ b/Config.xcconfig @@ -12,6 +12,6 @@ TEAM_ID = 8JGND254B7 BUNDLE_ID = com.neon443.NearFuture BUNDLE_ID_WIDGETS = com.neon443.NearFuture.widgets GROUP_ID = group.NearFuture -VERSION = 4.4.0 +VERSION = 5 NAME = Near Future BUILD_NUMBER = 1 diff --git a/MacNearFuture/MacNearFutureApp.swift b/MacNearFuture/MacNearFutureApp.swift index 4dad8f4..97938ad 100644 --- a/MacNearFuture/MacNearFutureApp.swift +++ b/MacNearFuture/MacNearFutureApp.swift @@ -24,7 +24,7 @@ struct NearFutureApp: App { .defaultSize(width: 550, height: 650) .commands { CommandGroup(replacing: CommandGroupPlacement.appInfo) { - Button("about nf") { + Button("About Near Future") { openWindow(id: "about") } } @@ -60,7 +60,10 @@ struct NearFutureApp: App { .defaultPosition(UnitPoint.center) Settings { - Text("wip") + SettingsView( + viewModel: viewModel, + settingsModel: settingsModel + ) } } } diff --git a/MacNearFuture/NFCommands.swift b/MacNearFuture/NFCommands.swift index bc58a7e..29c6083 100644 --- a/MacNearFuture/NFCommands.swift +++ b/MacNearFuture/NFCommands.swift @@ -11,7 +11,7 @@ import SwiftUI struct NearFutureCommands: Commands { var body: some Commands { CommandGroup(after: CommandGroupPlacement.appInfo) { - Text("hi") +// Text("hi") } } } diff --git a/MacNearFuture/Views/ContentViewMac.swift b/MacNearFuture/Views/ContentViewMac.swift index 8ef8a8e..a745ed1 100644 --- a/MacNearFuture/Views/ContentViewMac.swift +++ b/MacNearFuture/Views/ContentViewMac.swift @@ -12,7 +12,7 @@ struct ContentView: View { @StateObject var settingsModel: SettingsViewModel var body: some View { - NavigationSplitView(preferredCompactColumn: .constant(.sidebar)) { + NavigationSplitView { List { NavigationLink { HomeView( @@ -43,11 +43,15 @@ struct ContentView: View { } } } detail: { - + Text("Welcome to Near Future") } .tint(settingsModel.settings.tint.color) .frame(minWidth: 450, minHeight: 550) - .containerBackground(.ultraThinMaterial, for: .window) + .containerBackground(.regularMaterial, for: .window) + .sheet(isPresented: $settingsModel.settings.showWhatsNew) { + WhatsNewView(settingsModel: settingsModel) + .presentationSizing(.form) + } } } diff --git a/MacNearFuture/Views/SettingsView.swift b/MacNearFuture/Views/SettingsView.swift new file mode 100644 index 0000000..34ad279 --- /dev/null +++ b/MacNearFuture/Views/SettingsView.swift @@ -0,0 +1,155 @@ +// +// SettingsView.swift +// NearFuture +// +// Created by neon443 on 13/06/2025. +// + +import SwiftUI + +struct SettingsView: View { + @ObservedObject var viewModel: EventViewModel + @ObservedObject var settingsModel: SettingsViewModel + + @State private var importStr: String = "" + + func changeIcon(to toIcon: String) { + if let nsimage = NSImage(named: toIcon) { + let nsImageView = NSImageView(image: nsimage) + nsImageView.frame = NSRect(x: 0, y: 0, width: 128, height: 128) + NSApplication.shared.dockTile.contentView = nsImageView + NSApplication.shared.dockTile.display() + } + } + + var body: some View { + NavigationStack { + List { + ScrollView(.horizontal) { + HStack { + ForEach(settingsModel.accentChoices, id: \.self) { choice in + let color = Color(nsColor: NSColor(named: "uiColors/\(choice)")!) + ZStack { + Button() { + settingsModel.changeTint(to: choice) + changeIcon(to: choice) + } label: { + Circle() + .foregroundStyle(color) + .frame(width: 30) + } + .buttonStyle(.plain) + if ColorCodable(color) == settingsModel.settings.tint { + let needContrast: Bool = ColorCodable(color) == settingsModel.settings.tint + Circle() + .foregroundStyle(needContrast ? .two : .one) + .frame(width: 10) + } + } + } + } + } + Button("Show What's New") { + settingsModel.settings.showWhatsNew = true + } + Toggle("Show completed Events in Home", isOn: $settingsModel.settings.showCompletedInHome) + .onChange(of: settingsModel.settings.showCompletedInHome) { _ in + settingsModel.saveSettings() + } + NavigationLink() { + List { + if !settingsModel.notifsGranted { + Text("\(Image(systemName: "xmark")) Notifications disabled for Near Future") + .foregroundStyle(.red) + Button("Request Notifications") { + Task.detached { + let requestNotifsResult = await requestNotifs() + await MainActor.run { + settingsModel.notifsGranted = requestNotifsResult + } + } + } + } else { + Text("\(Image(systemName: "checkmark")) Notifications enabled for Near Future") + .foregroundStyle(.green) + } + } + } label: { + Image(systemName: "bell.badge.fill") + Text("Notifications") + } + NavigationLink() { + iCloudSettingsView( + viewModel: viewModel, + settingsModel: settingsModel + ) + } label: { + HStack { + Image(systemName: "icloud.fill") + Text("iCloud") + Spacer() + Circle() + .frame(width: 20, height: 20) + .foregroundStyle(viewModel.iCloudStatusColor) + } + } + .onAppear { + viewModel.sync() + viewModel.updateiCStatus() + } + NavigationLink() { + ImportView(viewModel: viewModel, importStr: $importStr) + } label: { + Label("Import Events", systemImage: "tray.and.arrow.down.fill") + .foregroundStyle(.one) + } + NavigationLink() { + ExportView(viewModel: viewModel) + } label: { + Label("Export Events", systemImage: "square.and.arrow.up") + .foregroundStyle(.one) + } + + Text("Tip") + .font(.subheadline) + Text("Near Future has Widgets!") + + Text("Danger Zone") + .foregroundStyle(.red) + .font(.subheadline) + Button("Delete local data", role: .destructive) { + viewModel.dangerClearLocalData() + } + Button("Delete iCloud data", role: .destructive) { + viewModel.dangerCleariCloudData() + } + Button("Delete all data", role: .destructive) { + viewModel.dangerClearLocalData() + viewModel.dangerCleariCloudData() + } + + Text("Debug") + .foregroundStyle(.red) + .font(.subheadline) + Button("Reset UserDefaults", role: .destructive) { + viewModel.dangerResetLocalData() + } + Button("Reset iCloud", role: .destructive) { + viewModel.dangerResetiCloud() + } + + // AboutView() + + .modifier(navigationInlineLarge()) + .scrollContentBackground(.hidden) + } + } + } +} + +#Preview { + SettingsView( + viewModel: dummyEventViewModel(), + settingsModel: dummySettingsViewModel() + ) +} diff --git a/NearFuture.xcodeproj/project.pbxproj b/NearFuture.xcodeproj/project.pbxproj index 78145fe..32cce42 100644 --- a/NearFuture.xcodeproj/project.pbxproj +++ b/NearFuture.xcodeproj/project.pbxproj @@ -32,6 +32,8 @@ A91EF80B2DFC910000B8463D /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80A2DFC910000B8463D /* ViewModifiers.swift */; }; A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80A2DFC910000B8463D /* ViewModifiers.swift */; }; A91EF80D2DFC910000B8463D /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80A2DFC910000B8463D /* ViewModifiers.swift */; }; + A91EF80E2DFC9A0C00B8463D /* WhatsNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914FA4C2DD2768900856265 /* WhatsNewView.swift */; }; + A91EF8102DFCB66C00B8463D /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80F2DFCB66C00B8463D /* SettingsView.swift */; }; A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2872D24011400E4F9B1 /* NearFutureApp.swift */; }; A920C28C2D24011400E4F9B1 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Events.swift */; }; A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C28D2D24011A00E4F9B1 /* Assets.xcassets */; }; @@ -54,7 +56,6 @@ A95E9ED82DFC742B00ED655F /* AccentIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95E9ED72DFC742B00ED655F /* AccentIcon.swift */; }; A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95E9ED72DFC742B00ED655F /* AccentIcon.swift */; }; A95E9EDA2DFC742B00ED655F /* AccentIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95E9ED72DFC742B00ED655F /* AccentIcon.swift */; }; - A95E9EE32DFC775300ED655F /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8462DCAABE00064DCA0 /* SettingsView.swift */; }; A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8452DCAABE00064DCA0 /* ImportView.swift */; }; A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8432DCAABE00064DCA0 /* ExportView.swift */; }; A979F60A2D270AF00094C0B3 /* NearFutureWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979F6092D270AF00094C0B3 /* NearFutureWidgetsBundle.swift */; }; @@ -119,6 +120,7 @@ A914FA4E2DD276D200856265 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AboutView.swift; path = NearFuture/Views/Misc/AboutView.swift; sourceTree = SOURCE_ROOT; }; A91EF8062DFC8B8B00B8463D /* ColorCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCodable.swift; sourceTree = ""; }; A91EF80A2DFC910000B8463D /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = ""; }; + A91EF80F2DFCB66C00B8463D /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; A920C2842D24011400E4F9B1 /* NearFuture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NearFuture.app; sourceTree = BUILT_PRODUCTS_DIR; }; A920C2872D24011400E4F9B1 /* NearFutureApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureApp.swift; sourceTree = ""; }; A920C28B2D24011400E4F9B1 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; }; @@ -212,6 +214,7 @@ A98C20CA2DE730740008D61C /* EventListViewMac.swift */, A98C20CD2DE7308E0008D61C /* ArchiveView.swift */, A98C20C82DE730420008D61C /* Events */, + A91EF80F2DFCB66C00B8463D /* SettingsView.swift */, ); path = Views; sourceTree = ""; @@ -526,11 +529,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - A95E9EE32DFC775300ED655F /* SettingsView.swift in Sources */, + A91EF80E2DFC9A0C00B8463D /* WhatsNewView.swift in Sources */, A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */, A98C20CB2DE730740008D61C /* EventListViewMac.swift in Sources */, A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */, A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */, + A91EF8102DFCB66C00B8463D /* SettingsView.swift in Sources */, A98C20CC2DE730740008D61C /* EditEventView.swift in Sources */, A90D495E2DDE3C7400781124 /* NFCommands.swift in Sources */, A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */, diff --git a/NearFuture/Views/Settings/SettingsView.swift b/NearFuture/Views/Settings/SettingsView.swift index 0bd94e4..e6a5fba 100644 --- a/NearFuture/Views/Settings/SettingsView.swift +++ b/NearFuture/Views/Settings/SettingsView.swift @@ -14,7 +14,6 @@ struct SettingsView: View { @State private var importStr: String = "" func changeIcon(to toIcon: String) { - #if canImport(UIKit) guard UIApplication.shared.supportsAlternateIcons else { print("doesnt tsupport alternate icons") return @@ -28,31 +27,17 @@ struct SettingsView: View { UIApplication.shared.setAlternateIconName(toIcon) { error in print(error as Any) } - #else - if let nsimage = NSImage(named: toIcon) { - let nsImageView = NSImageView(image: nsimage) - nsImageView.frame = NSRect(x: 0, y: 0, width: 128, height: 128) - NSApplication.shared.dockTile.contentView = nsImageView - NSApplication.shared.dockTile.display() - } - #endif } var body: some View { NavigationStack { ZStack { - #if os(iOS) backgroundGradient - #endif List { ScrollView(.horizontal) { HStack { ForEach(settingsModel.accentChoices, id: \.self) { choice in - #if canImport(UIKit) let color = Color(uiColor: UIColor(named: "uiColors/\(choice)")!) - #else - let color = Color(nsColor: NSColor(named: "uiColors/\(choice)")!) - #endif ZStack { Button() { settingsModel.changeTint(to: choice) @@ -62,6 +47,7 @@ struct SettingsView: View { .foregroundStyle(color) .frame(width: 30) } + .buttonStyle(.plain) if ColorCodable(color) == settingsModel.settings.tint { let needContrast: Bool = ColorCodable(color) == settingsModel.settings.tint Circle() diff --git a/NearFuture/Views/Settings/WhatsNewView.swift b/NearFuture/Views/Settings/WhatsNewView.swift index 44f438a..ca709d1 100644 --- a/NearFuture/Views/Settings/WhatsNewView.swift +++ b/NearFuture/Views/Settings/WhatsNewView.swift @@ -19,6 +19,11 @@ struct WhatsNewView: View { @State var bye: Bool = false var whatsNewChunks: [WhatsNewChunk] { return [ + WhatsNewChunk( + symbol: "desktopcomputer", + title: "Mac Native App", + subtitle: "New Mac native app (Intel too!)" + ), WhatsNewChunk( symbol: "iphone.radiowaves.left.and.right", title: "Haptic Feedback", @@ -53,23 +58,22 @@ struct WhatsNewView: View { } var body: some View { NavigationStack { - List { - VStack { - Text("What's New") - .font(.largeTitle) - .bold() - AboutView() - Divider() - VStack(alignment: .leading) { - ForEach(whatsNewChunks) { new in - WhatsNewChunkView( - symbol: new.symbol, - title: new.title, - subtitle: new.subtitle - ) - } + ScrollView { + Text("What's New") + .font(.largeTitle) + .bold() + .padding(.vertical) + VStack(alignment: .leading) { + ForEach(whatsNewChunks) { new in + WhatsNewChunkView( + symbol: new.symbol, + title: new.title, + subtitle: new.subtitle + ) } + Spacer() } + .padding(.horizontal, 10) } Button() { bye.toggle() @@ -79,11 +83,10 @@ struct WhatsNewView: View { .font(.headline) .frame(height: 40) .bold() - .frame(maxWidth: .infinity) +// .frame(maxWidth: .infinity) } - .buttonStyle(BorderedProminentButtonStyle()) - .clipShape(RoundedRectangle(cornerRadius: 15)) - .padding().padding() + .foregroundStyle(.orange) + .modifier(glassButton()) .modifier(hapticHeavy(trigger: bye)) } .scrollContentBackground(.hidden) @@ -113,7 +116,7 @@ struct WhatsNewChunkView: View { .resizable() .scaledToFit() .frame(width: 30, height: 30) - .foregroundStyle(Color.accentColor) + .foregroundStyle(Color.orange) .padding(.trailing, 15) VStack(alignment: .leading) { Text(title) diff --git a/Shared/ViewModifiers.swift b/Shared/ViewModifiers.swift index 71d0f70..eaefab5 100644 --- a/Shared/ViewModifiers.swift +++ b/Shared/ViewModifiers.swift @@ -25,6 +25,17 @@ struct hapticHeavy: ViewModifier { } } +struct glassButton: ViewModifier { + func body(content: Content) -> some View { + if #available(iOS 19, macOS 16, *) { + content.buttonStyle(.glass) + } else { + content.buttonStyle(.borderedProminent) + .clipShape(RoundedRectangle(cornerRadius: 15)) + } + } +} + struct hapticSuccess: ViewModifier { var trigger: any Equatable