add settings on mac

add whats new on mac
clean up settings on ios
clean up whats new + liquid glass
This commit is contained in:
neon443
2025-06-13 21:28:37 +01:00
parent 6e7bc6c2d1
commit 0663ba9e59
9 changed files with 210 additions and 44 deletions

View File

@@ -12,6 +12,6 @@ TEAM_ID = 8JGND254B7
BUNDLE_ID = com.neon443.NearFuture BUNDLE_ID = com.neon443.NearFuture
BUNDLE_ID_WIDGETS = com.neon443.NearFuture.widgets BUNDLE_ID_WIDGETS = com.neon443.NearFuture.widgets
GROUP_ID = group.NearFuture GROUP_ID = group.NearFuture
VERSION = 4.4.0 VERSION = 5
NAME = Near Future NAME = Near Future
BUILD_NUMBER = 1 BUILD_NUMBER = 1

View File

@@ -24,7 +24,7 @@ struct NearFutureApp: App {
.defaultSize(width: 550, height: 650) .defaultSize(width: 550, height: 650)
.commands { .commands {
CommandGroup(replacing: CommandGroupPlacement.appInfo) { CommandGroup(replacing: CommandGroupPlacement.appInfo) {
Button("about nf") { Button("About Near Future") {
openWindow(id: "about") openWindow(id: "about")
} }
} }
@@ -60,7 +60,10 @@ struct NearFutureApp: App {
.defaultPosition(UnitPoint.center) .defaultPosition(UnitPoint.center)
Settings { Settings {
Text("wip") SettingsView(
viewModel: viewModel,
settingsModel: settingsModel
)
} }
} }
} }

View File

@@ -11,7 +11,7 @@ import SwiftUI
struct NearFutureCommands: Commands { struct NearFutureCommands: Commands {
var body: some Commands { var body: some Commands {
CommandGroup(after: CommandGroupPlacement.appInfo) { CommandGroup(after: CommandGroupPlacement.appInfo) {
Text("hi") // Text("hi")
} }
} }
} }

View File

@@ -12,7 +12,7 @@ struct ContentView: View {
@StateObject var settingsModel: SettingsViewModel @StateObject var settingsModel: SettingsViewModel
var body: some View { var body: some View {
NavigationSplitView(preferredCompactColumn: .constant(.sidebar)) { NavigationSplitView {
List { List {
NavigationLink { NavigationLink {
HomeView( HomeView(
@@ -43,11 +43,15 @@ struct ContentView: View {
} }
} }
} detail: { } detail: {
Text("Welcome to Near Future")
} }
.tint(settingsModel.settings.tint.color) .tint(settingsModel.settings.tint.color)
.frame(minWidth: 450, minHeight: 550) .frame(minWidth: 450, minHeight: 550)
.containerBackground(.ultraThinMaterial, for: .window) .containerBackground(.regularMaterial, for: .window)
.sheet(isPresented: $settingsModel.settings.showWhatsNew) {
WhatsNewView(settingsModel: settingsModel)
.presentationSizing(.form)
}
} }
} }

View File

@@ -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()
)
}

View File

@@ -32,6 +32,8 @@
A91EF80B2DFC910000B8463D /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80A2DFC910000B8463D /* ViewModifiers.swift */; }; A91EF80B2DFC910000B8463D /* ViewModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A91EF80A2DFC910000B8463D /* ViewModifiers.swift */; };
A91EF80C2DFC910000B8463D /* 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 */; }; 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 */; }; A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2872D24011400E4F9B1 /* NearFutureApp.swift */; };
A920C28C2D24011400E4F9B1 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Events.swift */; }; A920C28C2D24011400E4F9B1 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Events.swift */; };
A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C28D2D24011A00E4F9B1 /* Assets.xcassets */; }; 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 */; }; A95E9ED82DFC742B00ED655F /* AccentIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = A95E9ED72DFC742B00ED655F /* AccentIcon.swift */; };
A95E9ED92DFC742B00ED655F /* 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 */; }; 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 */; }; A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8452DCAABE00064DCA0 /* ImportView.swift */; };
A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8432DCAABE00064DCA0 /* ExportView.swift */; }; A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A949F8432DCAABE00064DCA0 /* ExportView.swift */; };
A979F60A2D270AF00094C0B3 /* NearFutureWidgetsBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A979F6092D270AF00094C0B3 /* NearFutureWidgetsBundle.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; }; 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 = "<group>"; }; A91EF8062DFC8B8B00B8463D /* ColorCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCodable.swift; sourceTree = "<group>"; };
A91EF80A2DFC910000B8463D /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; }; A91EF80A2DFC910000B8463D /* ViewModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifiers.swift; sourceTree = "<group>"; };
A91EF80F2DFCB66C00B8463D /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
A920C2842D24011400E4F9B1 /* NearFuture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NearFuture.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; A920C2872D24011400E4F9B1 /* NearFutureApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NearFutureApp.swift; sourceTree = "<group>"; };
A920C28B2D24011400E4F9B1 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; }; A920C28B2D24011400E4F9B1 /* Events.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = "<group>"; };
@@ -212,6 +214,7 @@
A98C20CA2DE730740008D61C /* EventListViewMac.swift */, A98C20CA2DE730740008D61C /* EventListViewMac.swift */,
A98C20CD2DE7308E0008D61C /* ArchiveView.swift */, A98C20CD2DE7308E0008D61C /* ArchiveView.swift */,
A98C20C82DE730420008D61C /* Events */, A98C20C82DE730420008D61C /* Events */,
A91EF80F2DFCB66C00B8463D /* SettingsView.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -526,11 +529,12 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A95E9EE32DFC775300ED655F /* SettingsView.swift in Sources */, A91EF80E2DFC9A0C00B8463D /* WhatsNewView.swift in Sources */,
A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */, A95E9EE42DFC77D400ED655F /* ImportView.swift in Sources */,
A98C20CB2DE730740008D61C /* EventListViewMac.swift in Sources */, A98C20CB2DE730740008D61C /* EventListViewMac.swift in Sources */,
A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */, A91EF80C2DFC910000B8463D /* ViewModifiers.swift in Sources */,
A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */, A95E9ED92DFC742B00ED655F /* AccentIcon.swift in Sources */,
A91EF8102DFCB66C00B8463D /* SettingsView.swift in Sources */,
A98C20CC2DE730740008D61C /* EditEventView.swift in Sources */, A98C20CC2DE730740008D61C /* EditEventView.swift in Sources */,
A90D495E2DDE3C7400781124 /* NFCommands.swift in Sources */, A90D495E2DDE3C7400781124 /* NFCommands.swift in Sources */,
A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */, A95E9EE52DFC77E200ED655F /* ExportView.swift in Sources */,

View File

@@ -14,7 +14,6 @@ struct SettingsView: View {
@State private var importStr: String = "" @State private var importStr: String = ""
func changeIcon(to toIcon: String) { func changeIcon(to toIcon: String) {
#if canImport(UIKit)
guard UIApplication.shared.supportsAlternateIcons else { guard UIApplication.shared.supportsAlternateIcons else {
print("doesnt tsupport alternate icons") print("doesnt tsupport alternate icons")
return return
@@ -28,31 +27,17 @@ struct SettingsView: View {
UIApplication.shared.setAlternateIconName(toIcon) { error in UIApplication.shared.setAlternateIconName(toIcon) { error in
print(error as Any) 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 { var body: some View {
NavigationStack { NavigationStack {
ZStack { ZStack {
#if os(iOS)
backgroundGradient backgroundGradient
#endif
List { List {
ScrollView(.horizontal) { ScrollView(.horizontal) {
HStack { HStack {
ForEach(settingsModel.accentChoices, id: \.self) { choice in ForEach(settingsModel.accentChoices, id: \.self) { choice in
#if canImport(UIKit)
let color = Color(uiColor: UIColor(named: "uiColors/\(choice)")!) let color = Color(uiColor: UIColor(named: "uiColors/\(choice)")!)
#else
let color = Color(nsColor: NSColor(named: "uiColors/\(choice)")!)
#endif
ZStack { ZStack {
Button() { Button() {
settingsModel.changeTint(to: choice) settingsModel.changeTint(to: choice)
@@ -62,6 +47,7 @@ struct SettingsView: View {
.foregroundStyle(color) .foregroundStyle(color)
.frame(width: 30) .frame(width: 30)
} }
.buttonStyle(.plain)
if ColorCodable(color) == settingsModel.settings.tint { if ColorCodable(color) == settingsModel.settings.tint {
let needContrast: Bool = ColorCodable(color) == settingsModel.settings.tint let needContrast: Bool = ColorCodable(color) == settingsModel.settings.tint
Circle() Circle()

View File

@@ -19,6 +19,11 @@ struct WhatsNewView: View {
@State var bye: Bool = false @State var bye: Bool = false
var whatsNewChunks: [WhatsNewChunk] { var whatsNewChunks: [WhatsNewChunk] {
return [ return [
WhatsNewChunk(
symbol: "desktopcomputer",
title: "Mac Native App",
subtitle: "New Mac native app (Intel too!)"
),
WhatsNewChunk( WhatsNewChunk(
symbol: "iphone.radiowaves.left.and.right", symbol: "iphone.radiowaves.left.and.right",
title: "Haptic Feedback", title: "Haptic Feedback",
@@ -53,13 +58,11 @@ struct WhatsNewView: View {
} }
var body: some View { var body: some View {
NavigationStack { NavigationStack {
List { ScrollView {
VStack {
Text("What's New") Text("What's New")
.font(.largeTitle) .font(.largeTitle)
.bold() .bold()
AboutView() .padding(.vertical)
Divider()
VStack(alignment: .leading) { VStack(alignment: .leading) {
ForEach(whatsNewChunks) { new in ForEach(whatsNewChunks) { new in
WhatsNewChunkView( WhatsNewChunkView(
@@ -68,8 +71,9 @@ struct WhatsNewView: View {
subtitle: new.subtitle subtitle: new.subtitle
) )
} }
Spacer()
} }
} .padding(.horizontal, 10)
} }
Button() { Button() {
bye.toggle() bye.toggle()
@@ -79,11 +83,10 @@ struct WhatsNewView: View {
.font(.headline) .font(.headline)
.frame(height: 40) .frame(height: 40)
.bold() .bold()
.frame(maxWidth: .infinity) // .frame(maxWidth: .infinity)
} }
.buttonStyle(BorderedProminentButtonStyle()) .foregroundStyle(.orange)
.clipShape(RoundedRectangle(cornerRadius: 15)) .modifier(glassButton())
.padding().padding()
.modifier(hapticHeavy(trigger: bye)) .modifier(hapticHeavy(trigger: bye))
} }
.scrollContentBackground(.hidden) .scrollContentBackground(.hidden)
@@ -113,7 +116,7 @@ struct WhatsNewChunkView: View {
.resizable() .resizable()
.scaledToFit() .scaledToFit()
.frame(width: 30, height: 30) .frame(width: 30, height: 30)
.foregroundStyle(Color.accentColor) .foregroundStyle(Color.orange)
.padding(.trailing, 15) .padding(.trailing, 15)
VStack(alignment: .leading) { VStack(alignment: .leading) {
Text(title) Text(title)

View File

@@ -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 { struct hapticSuccess: ViewModifier {
var trigger: any Equatable var trigger: any Equatable