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_WIDGETS = com.neon443.NearFuture.widgets
GROUP_ID = group.NearFuture
VERSION = 4.4.0
VERSION = 5
NAME = Near Future
BUILD_NUMBER = 1

View File

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

View File

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

View File

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

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 */; };
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 = "<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; };
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>"; };
@@ -212,6 +214,7 @@
A98C20CA2DE730740008D61C /* EventListViewMac.swift */,
A98C20CD2DE7308E0008D61C /* ArchiveView.swift */,
A98C20C82DE730420008D61C /* Events */,
A91EF80F2DFCB66C00B8463D /* SettingsView.swift */,
);
path = Views;
sourceTree = "<group>";
@@ -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 */,

View File

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

View File

@@ -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,13 +58,11 @@ struct WhatsNewView: View {
}
var body: some View {
NavigationStack {
List {
VStack {
ScrollView {
Text("What's New")
.font(.largeTitle)
.bold()
AboutView()
Divider()
.padding(.vertical)
VStack(alignment: .leading) {
ForEach(whatsNewChunks) { new in
WhatsNewChunkView(
@@ -68,8 +71,9 @@ struct WhatsNewView: View {
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)

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 {
var trigger: any Equatable