31 Commits

Author SHA1 Message Date
neon443
ca64157923 fix sendable warnings 2025-05-19 15:15:59 +01:00
neon443
db05da3043 add haptic feedback, bump version, update whats new 2025-05-19 15:13:38 +01:00
neon443
940a8f4ebd haptic on tab selection, remove divider 2025-05-19 15:08:34 +01:00
neon443
48071c0791 remove th goof button 2025-05-19 15:03:49 +01:00
neon443
3a94dc7675 app intents make me wanna kms 2025-05-16 09:49:14 +01:00
neon443
08bf56ecbd ajsdlkfjkl;asdjfkl 2025-05-15 18:52:23 +01:00
neon443
008eecb52c ddfsdfsd 2025-05-15 18:41:03 +01:00
neon443
c640c77600 dskljfasdlkfjkl;asjfklajsdkl;fjaksd;jfgkladfjhghadfiughaipehroigjuoeirjgfioejroifjaoigjoiaejoirg 2025-05-15 18:30:02 +01:00
neon443
e9d49cc137 pls pls pls b fixed 2025-05-15 18:00:22 +01:00
neon443
565341c3d0 s 2025-05-15 17:32:04 +01:00
neon443
f9c50df47f s 2025-05-15 17:27:27 +01:00
neon443
bb598250a6 modified: Config.xcconfig 2025-05-15 17:22:25 +01:00
neon443
c7fddda601 ssfdafasfasdfaf 2025-05-15 17:06:58 +01:00
neon443
033a01463f fix icon setting fr this time 2025-05-15 12:31:53 +01:00
neon443
87209f38f3 jfdsa 2025-05-15 07:46:37 +01:00
neon443
7c852fc8ec fix the whats new appearing each settings save 2025-05-14 21:42:22 +01:00
neon443
73b3d9b234 update widget
to imporve readablity and add red for past
bump version
add whats new
2025-05-14 21:20:55 +01:00
neon443
0a17ec6292 bump version adn update whats new 2025-05-14 21:16:35 +01:00
neon443
bb67b0ce98 YESSSSSSSSS
ok now jsut have ot make this scalabel 😭
😭😭😭
2025-05-14 21:04:00 +01:00
neon443
82341f40ef ajkl;dfs;asfjl;laskdjf;ajsf 2025-05-14 20:51:02 +01:00
neon443
f9f030c8b3 app icon yayyyyy 2025-05-14 19:25:22 +01:00
neon443
3ca5017b61 remove basic ui color 2025-05-13 19:36:34 +01:00
neon443
0efcc9e183 bump version to 4.2.1(1) 2025-05-13 11:07:23 +01:00
neon443
595c2b74c5 fixed whatsnewview's horizontal layout 2025-05-13 11:07:03 +01:00
neon443
f252359eb1 bump version, update whats new 2025-05-13 11:00:31 +01:00
neon443
2b266e03ca fix whats new showing every time
prevAppVersoin wasnt being set lol
remove .searchable()
rename WhatsNew -> WhatsNewChunk
2025-05-13 10:59:22 +01:00
neon443
c935d4b1f0 improve whats new
added a global getdevice icon thingy
2025-05-12 21:07:51 +01:00
neon443
c82ce5fa17 Whats New!
add AboutView
move getVersion() and geBuildID()
reorg of presentaion related modifiers
2025-05-12 20:49:28 +01:00
neon443
7bef3bc4e6 animations make me wanna kms
animations!!!!!!!!! on add, delete and ticking yayy
sorts on load
extract contentview to HomeView, its got too indented loll
2025-05-12 19:24:34 +01:00
neon443
025dbf22cd tring to ad animations 2025-05-12 17:26:37 +01:00
neon443
da65c3ac69 asdkjlf 2025-05-09 14:14:15 +01:00
74 changed files with 1080 additions and 513 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.0.0
VERSION = 4.4.0
NAME = Near Future
BUILD_NUMBER = 0
BUILD_NUMBER = 1

View File

@@ -7,6 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
A914FA4B2DD26C6800856265 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914FA4A2DD26C0F00856265 /* HomeView.swift */; };
A914FA4D2DD2768900856265 /* WhatsNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914FA4C2DD2768900856265 /* WhatsNewView.swift */; };
A914FA4F2DD276D200856265 /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A914FA4E2DD276D200856265 /* AboutView.swift */; };
A920C2882D24011400E4F9B1 /* NearFutureApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C2872D24011400E4F9B1 /* NearFutureApp.swift */; };
A920C28C2D24011400E4F9B1 /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = A920C28B2D24011400E4F9B1 /* Item.swift */; };
A920C28E2D24011A00E4F9B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A920C28D2D24011A00E4F9B1 /* Assets.xcassets */; };
@@ -70,6 +73,9 @@
/* Begin PBXFileReference section */
A90FDE222DC0D4310012790C /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
A914FA4A2DD26C0F00856265 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
A914FA4C2DD2768900856265 /* WhatsNewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WhatsNewView.swift; path = NearFuture/Views/Settings/WhatsNewView.swift; sourceTree = SOURCE_ROOT; };
A914FA4E2DD276D200856265 /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AboutView.swift; path = NearFuture/Views/Misc/AboutView.swift; sourceTree = SOURCE_ROOT; };
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 /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = "<group>"; };
@@ -220,6 +226,7 @@
A949F83F2DCAABE00064DCA0 /* ContentView.swift */,
A949F8402DCAABE00064DCA0 /* EventListView.swift */,
A949F8412DCAABE00064DCA0 /* HelpView.swift */,
A914FA4A2DD26C0F00856265 /* HomeView.swift */,
);
path = Home;
sourceTree = "<group>";
@@ -227,10 +234,11 @@
A949F8472DCAABE00064DCA0 /* Settings */ = {
isa = PBXGroup;
children = (
A949F8432DCAABE00064DCA0 /* ExportView.swift */,
A949F8442DCAABE00064DCA0 /* iCloudSettingsView.swift */,
A949F8452DCAABE00064DCA0 /* ImportView.swift */,
A949F8462DCAABE00064DCA0 /* SettingsView.swift */,
A949F8442DCAABE00064DCA0 /* iCloudSettingsView.swift */,
A914FA4C2DD2768900856265 /* WhatsNewView.swift */,
A949F8452DCAABE00064DCA0 /* ImportView.swift */,
A949F8432DCAABE00064DCA0 /* ExportView.swift */,
);
path = Settings;
sourceTree = "<group>";
@@ -246,12 +254,12 @@
A949F84A2DCAABE00064DCA0 /* Views */ = {
isa = PBXGroup;
children = (
A949F83B2DCAABE00064DCA0 /* Archive */,
A949F83E2DCAABE00064DCA0 /* Events */,
A949F8422DCAABE00064DCA0 /* Home */,
A949F8472DCAABE00064DCA0 /* Settings */,
A949F8492DCAABE00064DCA0 /* Stats */,
A949F83E2DCAABE00064DCA0 /* Events */,
A949F83B2DCAABE00064DCA0 /* Archive */,
A949F85E2DCABB420064DCA0 /* Misc */,
A949F8492DCAABE00064DCA0 /* Stats */,
A949F8472DCAABE00064DCA0 /* Settings */,
);
path = Views;
sourceTree = "<group>";
@@ -268,6 +276,7 @@
isa = PBXGroup;
children = (
A949F85D2DCABB420064DCA0 /* Buttons.swift */,
A914FA4E2DD276D200856265 /* AboutView.swift */,
);
path = Misc;
sourceTree = "<group>";
@@ -449,12 +458,15 @@
files = (
A920C28C2D24011400E4F9B1 /* Item.swift in Sources */,
A949F84B2DCAABE00064DCA0 /* ArchiveView.swift in Sources */,
A914FA4F2DD276D200856265 /* AboutView.swift in Sources */,
A949F84C2DCAABE00064DCA0 /* AddEventView.swift in Sources */,
A914FA4B2DD26C6800856265 /* HomeView.swift in Sources */,
A949F84D2DCAABE00064DCA0 /* EditEventView.swift in Sources */,
A949F84E2DCAABE00064DCA0 /* ContentView.swift in Sources */,
A949F84F2DCAABE00064DCA0 /* EventListView.swift in Sources */,
A949F8502DCAABE00064DCA0 /* HelpView.swift in Sources */,
A949F85F2DCABB420064DCA0 /* Buttons.swift in Sources */,
A914FA4D2DD2768900856265 /* WhatsNewView.swift in Sources */,
A949F8512DCAABE00064DCA0 /* ExportView.swift in Sources */,
A949F8522DCAABE00064DCA0 /* iCloudSettingsView.swift in Sources */,
A949F8532DCAABE00064DCA0 /* ImportView.swift in Sources */,
@@ -505,7 +517,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "Bloo Blue Green Pink Purple Red Yellow";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@@ -565,7 +580,7 @@
);
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 6.0;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
@@ -573,7 +588,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "Bloo Blue Green Pink Purple Red Yellow";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
@@ -621,7 +639,7 @@
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_VERSION = 6.0;
SWIFT_VERSION = 5.0;
};
name = Release;
};
@@ -629,8 +647,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = A90FDE222DC0D4310012790C /* Config.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "bloo blue green pink purple red yellow";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_USE_OPTIMIZATION_PROFILE = YES;
CODE_SIGN_ENTITLEMENTS = NearFuture/NearFuture.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
@@ -680,8 +700,10 @@
isa = XCBuildConfiguration;
baseConfigurationReference = A90FDE222DC0D4310012790C /* Config.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "bloo blue green pink purple red yellow";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_USE_OPTIMIZATION_PROFILE = YES;
CODE_SIGN_ENTITLEMENTS = NearFuture/NearFuture.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
@@ -730,6 +752,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_USE_OPTIMIZATION_PROFILE = YES;
CODE_SIGN_ENTITLEMENTS = NearFutureWidgets/NearFutureWidgetsExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";
@@ -770,6 +793,7 @@
buildSettings = {
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CLANG_USE_OPTIMIZATION_PROFILE = YES;
CODE_SIGN_ENTITLEMENTS = NearFutureWidgets/NearFutureWidgetsExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(BUILD_NUMBER)";

View File

@@ -1,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1630"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A920C2832D24011300E4F9B1"
BuildableName = "NearFuture.app"
BlueprintName = "NearFuture"
ReferencedContainer = "container:NearFuture.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A920C2832D24011300E4F9B1"
BuildableName = "NearFuture.app"
BlueprintName = "NearFuture"
ReferencedContainer = "container:NearFuture.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "A920C2832D24011300E4F9B1"
BuildableName = "NearFuture.app"
BlueprintName = "NearFuture"
ReferencedContainer = "container:NearFuture.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -4,11 +4,6 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>NearFuture nodebug.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>NearFuture.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>

View File

@@ -126,6 +126,7 @@ struct EventListView: View {
)
.fixedSize(horizontal: false, vertical: true)
}
.transition(.opacity)
.contextMenu() {
Button(role: .destructive) {
let eventToModify = viewModel.events.firstIndex() { currEvent in

View File

@@ -1,5 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
<dict>
<key>AppIntents</key>
<array>
<string>com.neon443.NearFuture.CompleteEvent</string>
</array>
</dict>
</plist>

View File

@@ -2,7 +2,7 @@
// Item.swift
// NearFuture
//
// Created by Nihaal Sharma on 24/12/2024.
// Created by neon443 on 24/12/2024.
//
import Foundation
@@ -10,6 +10,7 @@ import SwiftData
import SwiftUI
import WidgetKit
import UserNotifications
import AppIntents
//@Model
//final class Item {
@@ -20,7 +21,7 @@ import UserNotifications
// }
//}
struct Event: Identifiable, Codable {
struct Event: Identifiable, Codable, Equatable, Animatable {
var id = UUID()
var name: String
var complete: Bool
@@ -107,26 +108,51 @@ func daysUntilEvent(_ eventDate: Date) -> (long: String, short: String) {
struct Settings: Codable, Equatable {
var showCompletedInHome: Bool
var tint: ColorCodable
var showWhatsNew: Bool
var prevAppVersion: String
}
struct AccentIcon {
var icon: UIImage
var color: Color
var name: String
init(_ colorName: String) {
if colorName == "orange" {
self.icon = UIImage(named: "AppIcon")!
} else {
self.icon = UIImage(named: colorName)!
}
self.color = Color(uiColor: UIColor(named: "uiColors/\(colorName)")!)
self.name = colorName
}
}
class SettingsViewModel: ObservableObject {
@Published var settings: Settings = Settings(
showCompletedInHome: false,
tint: ColorCodable(uiColor: UIColor(named: "AccentColor")!)
tint: ColorCodable(uiColor: UIColor(named: "AccentColor")!),
showWhatsNew: true,
prevAppVersion: getVersion()+getBuildID()
)
@Published var notifsGranted: Bool = false
@Published var accentChoices: [Color] = [
Color(UIColor(named: "uiColors/red")!),
Color(UIColor(named: "uiColors/orange")!),
Color(UIColor(named: "uiColors/yellow")!),
Color(UIColor(named: "uiColors/green")!),
Color(UIColor(named: "uiColors/blue")!),
Color(UIColor(named: "uiColors/indigo")!),
Color(UIColor(named: "uiColors/basic")!)
@Published var colorChoices: [AccentIcon] = []
let accentChoices: [String] = [
"red",
"orange",
"yellow",
"green",
"blue",
"bloo",
"purple",
"pink"
]
@Published var device: (sf: String, label: String)
init(load: Bool = true) {
self.device = getDevice()
if load {
loadSettings()
Task {
@@ -138,6 +164,13 @@ class SettingsViewModel: ObservableObject {
}
}
func changeTint(to: String) {
if let uicolor = UIColor(named: "uiColors/\(to)") {
self.settings.tint = ColorCodable(uiColor: uicolor)
saveSettings()
}
}
let appGroupSettingsStore = UserDefaults(suiteName: "group.NearFuture") ?? UserDefaults.standard
let icSettStore = NSUbiquitousKeyValueStore.default
@@ -152,6 +185,9 @@ class SettingsViewModel: ObservableObject {
self.settings = decodedSetts
}
}
if self.settings.prevAppVersion != getVersion()+getBuildID() {
self.settings.showWhatsNew = true
}
}
func saveSettings() {
@@ -165,7 +201,7 @@ class SettingsViewModel: ObservableObject {
}
}
class EventViewModel: ObservableObject {
class EventViewModel: ObservableObject, @unchecked Sendable {
@Published var events: [Event] = []
@Published var icloudData: [Event] = []
@@ -227,6 +263,7 @@ class EventViewModel: ObservableObject {
}
}
updateSyncStatus()
self.events.sort() {$0.date < $1.date}
}
func getNotifs() async -> [UNNotificationRequest] {
@@ -414,10 +451,21 @@ class EventViewModel: ObservableObject {
}
}
class dummyEventViewModel: EventViewModel {
class dummyEventViewModel: EventViewModel, @unchecked Sendable {
var template2: Event
override init(load: Bool = false) {
self.template2 = Event(
name: "template2",
complete: false,
completeDesc: "",
symbol: "hammer",
color: ColorCodable(randomColor()),
notes: "notes",
date: Date(),
recurrence: .none
)
super.init(load: false)
self.events = [self.example, self.template, self.example, self.template]
self.events = [self.example, self.template, self.template2]
self.events[0].complete.toggle()
}
}
@@ -516,3 +564,84 @@ func cancelNotif(_ id: String) {
func cancelAllNotifs() {
UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
}
func getVersion() -> String {
guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] else {
fatalError("no bundle id wtf lol")
}
return "\(version)"
}
func getBuildID() -> String {
guard let build = Bundle.main.infoDictionary?["CFBundleVersion"] else {
fatalError("wtf did u do w the build number")
}
return "\(build)"
}
func getDevice() -> (sf: String, label: String) {
let asi = ProcessInfo().isiOSAppOnMac
let model = UIDevice().model
if asi {
return (sf: "laptopcomputer", label: "Computer")
} else if model == "iPhone" {
return (sf: model.lowercased(), label: model)
} else if model == "iPad" {
return (sf: model.lowercased(), label: model)
}
return (sf: "iphone", label: "iPhone")
}
extension Event: AppEntity {
static let defaultQuery = EventQuery()
static var typeDisplayRepresentation: TypeDisplayRepresentation {
TypeDisplayRepresentation("skdfj")
}
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation("eventsss")
}
}
struct EventQuery: EntityQuery, DynamicOptionsProvider {
typealias Entity = Event
@Dependency var vm: EventViewModel
func results() async throws -> some ResultsCollection {
return vm.events
}
// func defaultResult() async -> DefaultValue? {
// return vm.events[0]
// }
func entities(for identifiers: [Entity.ID]) async throws -> [Entity] {
return vm.events
}
func suggestedEntities() async throws -> some ResultsCollection {
return vm.events //lol cba
}
}
struct CompleteEvent: AppIntent {
static var title: LocalizedStringResource = "Complete An Event"
static var description = IntentDescription("Mark an Event as complete.")
@Parameter(title: "Event ID")
var eventID: String
func perform() async throws -> some IntentResult {
print("s")
let viewModel = EventViewModel()
print("hip")
guard let eventUUID = UUID(uuidString: eventID) else {
print(":sdklfajk")
return .result()
}
print("hii")
if let eventToModify = viewModel.events.firstIndex(where: { $0.id == eventUUID }) {
print("hiii")
viewModel.events[eventToModify].complete = true
viewModel.saveEvents()
}
return .result()
}
}

View File

@@ -2,7 +2,7 @@
// NearFutureApp.swift
// NearFuture
//
// Created by Nihaal Sharma on 24/12/2024.
// Created by neon443 on 24/12/2024.
//
import SwiftUI

View File

@@ -2,7 +2,7 @@
// AppIntent.swift
// NearFutureWidgets
//
// Created by Nihaal Sharma on 02/01/2025.
// Created by neon443 on 02/01/2025.
//
import WidgetKit

View File

@@ -2,7 +2,7 @@
// NearFutureWidgets.swift
// NearFutureWidgets
//
// Created by Nihaal Sharma on 02/01/2025.
// Created by neon443 on 02/01/2025.
//
import WidgetKit

View File

@@ -2,7 +2,7 @@
// NearFutureWidgetsBundle.swift
// NearFutureWidgets
//
// Created by Nihaal Sharma on 02/01/2025.
// Created by neon443 on 02/01/2025.
//
import WidgetKit

View File

@@ -2,7 +2,7 @@
// NearFutureWidgetsLiveActivity.swift
// NearFutureWidgets
//
// Created by Nihaal Sharma on 02/01/2025.
// Created by neon443 on 02/01/2025.
//
import ActivityKit

View File

@@ -10,7 +10,9 @@ import SwiftUI
struct ArchiveView: View {
@ObservedObject var viewModel: EventViewModel
@State var showAddEvent: Bool = false
@State var hey: UUID = UUID()
var filteredEvents: [Event] {
return viewModel.events.filter() {$0.complete}
}
var body: some View {
NavigationStack {
ZStack {
@@ -19,15 +21,14 @@ struct ArchiveView: View {
HelpView(showAddEvent: $showAddEvent)
} else {
ScrollView {
ForEach(viewModel.events.filter({$0.complete})) { event in
ForEach(filteredEvents) { event in
EventListView(viewModel: viewModel, event: event)
.transition(.moveAndFadeReversed)
.id(event.complete)
}
.padding(.horizontal)
.id(hey)
.onReceive(viewModel.objectWillChange) {
hey = UUID()
}
}
.animation(.default, value: filteredEvents)
}
}
.scrollContentBackground(.hidden)
@@ -58,12 +59,6 @@ struct ArchiveView: View {
eventRecurrence: $viewModel.editableTemplate.recurrence,
adding: true
)
.presentationDragIndicator(.visible)
.apply {
if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial)
}
}
}
}
}
@@ -71,5 +66,3 @@ struct ArchiveView: View {
#Preview {
ArchiveView(viewModel: dummyEventViewModel())
}

View File

@@ -2,7 +2,7 @@
// AddEventView.swift
// NearFuture
//
// Created by Nihaal Sharma on 25/12/2024.
// Created by neon443 on 25/12/2024.
//
import SwiftUI
@@ -24,6 +24,8 @@ struct AddEventView: View {
@State var showNeedsNameAlert: Bool = false
@State var isSymbolPickerPresented = false
@State private var bye: Bool = false
@FocusState private var focusedField: Field?
private enum Field {
case Name, Notes
@@ -32,172 +34,190 @@ struct AddEventView: View {
@Environment(\.dismiss) var dismiss
var body: some View {
NavigationStack {
Form {
Section(
header:
Text("Event Details")
.font(.headline)
.foregroundColor(.accentColor)
) {
// name & symbol
HStack(spacing: 5) {
Button() {
isSymbolPickerPresented.toggle()
} label: {
Image(systemName: eventSymbol)
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(eventColor)
}
.frame(width: 20)
.buttonStyle(.borderless)
.sheet(isPresented: $isSymbolPickerPresented) {
SymbolsPicker(
selection: $eventSymbol,
title: "Choose a Symbol",
searchLabel: "Search...",
autoDismiss: true)
.presentationDetents([.medium])
.apply {
if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial)
ZStack {
if !adding {
backgroundGradient
}
NavigationStack {
Form {
Section(
header:
Text("Event Details")
.font(.headline)
.foregroundColor(.accentColor)
) {
// name & symbol
HStack(spacing: 5) {
Button() {
isSymbolPickerPresented.toggle()
} label: {
Image(systemName: eventSymbol)
.resizable()
.scaledToFit()
.frame(width: 20, height: 20)
.foregroundStyle(eventColor)
}
.frame(width: 20)
.buttonStyle(.borderless)
.sheet(isPresented: $isSymbolPickerPresented) {
SymbolsPicker(
selection: $eventSymbol,
title: "Choose a Symbol",
searchLabel: "Search...",
autoDismiss: true)
.presentationDetents([.medium])
.apply {
if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial)
}
}
}
ColorPicker("", selection: $eventColor, supportsOpacity: false)
.fixedSize()
ZStack {
TextField("Event Name", text: $eventName)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.trailing, eventName.isEmpty ? 0 : 30)
.animation(.spring, value: eventName)
.focused($focusedField, equals: Field.Name)
.submitLabel(.next)
.onSubmit {
focusedField = .Notes
}
MagicClearButton(text: $eventName)
}
}
ColorPicker("", selection: $eventColor, supportsOpacity: false)
.fixedSize()
// dscription
ZStack {
TextField("Event Name", text: $eventName)
TextField("Event Notes", text: $eventNotes)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.trailing, eventName.isEmpty ? 0 : 30)
.animation(.spring, value: eventName)
.focused($focusedField, equals: Field.Name)
.submitLabel(.next)
.padding(.trailing, eventNotes.isEmpty ? 0 : 30)
.animation(.spring, value: eventNotes)
.focused($focusedField, equals: Field.Notes)
.submitLabel(.done)
.onSubmit {
focusedField = .Notes
focusedField = nil
}
MagicClearButton(text: $eventName)
MagicClearButton(text: $eventNotes)
}
}
// dscription
ZStack {
TextField("Event Notes", text: $eventNotes)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding(.trailing, eventNotes.isEmpty ? 0 : 30)
.animation(.spring, value: eventNotes)
.focused($focusedField, equals: Field.Notes)
.submitLabel(.done)
.onSubmit {
focusedField = nil
// date picker
HStack {
Spacer()
DatePicker("", selection: $eventDate, displayedComponents: .date)
.datePickerStyle(WheelDatePickerStyle())
Spacer()
Button() {
eventDate = Date()
} label: {
Image(systemName: "arrow.uturn.left")
.resizable()
.scaledToFit()
}
MagicClearButton(text: $eventNotes)
}
// date picker
HStack {
Spacer()
DatePicker("", selection: $eventDate, displayedComponents: .date)
.datePickerStyle(WheelDatePickerStyle())
Spacer()
Button() {
eventDate = Date()
} label: {
Image(systemName: "arrow.uturn.left")
.resizable()
.scaledToFit()
.buttonStyle(BorderlessButtonStyle())
.frame(width: 20)
}
.buttonStyle(BorderlessButtonStyle())
.frame(width: 20)
}
DatePicker(
"",
selection: $eventDate,
displayedComponents: .hourAndMinute
)
// re-ocurrence Picker
Picker("Recurrence", selection: $eventRecurrence) {
ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in
Text(recurrence.rawValue.capitalized)
// re-ocurrence Picker
Picker("Recurrence", selection: $eventRecurrence) {
ForEach(Event.RecurrenceType.allCases, id: \.self) { recurrence in
Text(recurrence.rawValue.capitalized)
}
}
.pickerStyle(SegmentedPickerStyle())
Text(
describeOccurrence(
date: eventDate,
recurrence: eventRecurrence
)
)
}
}
.scrollContentBackground(.hidden)
.navigationTitle("\(adding ? "Add Event" : "")")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
if adding {
Button() {
resetAddEventView()
dismiss()
} label: {
Image(systemName: "xmark.circle.fill")
.symbolRenderingMode(.hierarchical)
.resizable()
.scaledToFit()
.frame(width: 30)
}
}
}
ToolbarItem(placement: .topBarTrailing) {
if adding {
Button {
viewModel.addEvent(
newEvent: Event(
name: eventName,
complete: eventComplete,
completeDesc: eventCompleteDesc,
symbol: eventSymbol,
color: ColorCodable(eventColor),
notes: eventNotes,
date: eventDate,
recurrence: eventRecurrence
)
)
bye.toggle()
resetAddEventView()
} label: {
Text("Save")
.font(.headline)
.cornerRadius(10)
.buttonStyle(BorderedProminentButtonStyle())
}
.apply {
if #available(iOS 17, *) {
$0.sensoryFeedback(.success, trigger: bye)
}
}
.disabled(eventName.isEmpty)
.onTapGesture {
if eventName.isEmpty {
showNeedsNameAlert.toggle()
}
}
.alert("Missing Name", isPresented: $showNeedsNameAlert) {
Button("OK", role: .cancel) {
showNeedsNameAlert.toggle()
focusedField = .Name
}
} message: {
Text("Give your Event a name before saving.")
}
if eventName.isEmpty {
HStack {
Image(systemName: "exclamationmark")
.foregroundStyle(.red)
Text("Give your event a name.")
}
}
}
}
.pickerStyle(SegmentedPickerStyle())
Text(
describeOccurrence(
date: eventDate,
recurrence: eventRecurrence
)
)
}
}
.presentationDragIndicator(.visible)
.scrollContentBackground(.hidden)
.navigationTitle("\(adding ? "Add Event" : "")")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .topBarLeading) {
if adding {
Button() {
resetAddEventView()
dismiss()
} label: {
Image(systemName: "xmark.circle.fill")
.symbolRenderingMode(.hierarchical)
.resizable()
.scaledToFit()
.frame(width: 30)
}
}
}
ToolbarItem(placement: .topBarTrailing) {
if adding {
Button {
viewModel.addEvent(
newEvent: Event(
name: eventName,
complete: eventComplete,
completeDesc: eventCompleteDesc,
symbol: eventSymbol,
color: ColorCodable(eventColor),
notes: eventNotes,
date: eventDate,
recurrence: eventRecurrence
)
)
resetAddEventView()
} label: {
Text("Save")
.font(.headline)
.cornerRadius(10)
.buttonStyle(BorderedProminentButtonStyle())
}
.disabled(eventName.isEmpty)
.onTapGesture {
if eventName.isEmpty {
showNeedsNameAlert.toggle()
}
}
.alert("Missing Name", isPresented: $showNeedsNameAlert) {
Button("OK", role: .cancel) {
showNeedsNameAlert.toggle()
focusedField = .Name
}
} message: {
Text("Give your Event a name before saving.")
}
if eventName.isEmpty {
HStack {
Image(systemName: "exclamationmark")
.foregroundStyle(.red)
Text("Give your event a name.")
}
}
}
}
}
.apply {
if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial)
}
}
}
@@ -233,11 +253,5 @@ struct AddEventView: View {
eventRecurrence: .constant(vm.template.recurrence),
adding: true
)
.presentationDragIndicator(.visible)
.apply {
if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial)
}
}
}
}

View File

@@ -2,7 +2,7 @@
// EditEventView.swift
// NearFuture
//
// Created by Nihaal Sharma on 02/01/2025.
// Created by neon443 on 02/01/2025.
//
import SwiftUI

View File

@@ -2,7 +2,7 @@
// ContentView.swift
// NearFuture
//
// Created by Nihaal Sharma on 24/12/2024.
// Created by neon443 on 24/12/2024.
//
import SwiftUI
@@ -13,152 +13,47 @@ enum Field {
case Search
}
enum Tab {
case Home
case Archive
case Statistics
case Settings
case Gradient
case home
case archive
case stats
case settings
}
struct ContentView: View {
@StateObject var viewModel: EventViewModel
@StateObject var settingsModel: SettingsViewModel
@State private var eventName = ""
@State private var eventComplete = false
@State private var eventCompleteDesc = ""
@State private var eventSymbol = "star"
@State private var eventColor: Color = randomColor()
@State private var eventNotes = ""
@State private var eventDate = Date()
@State private var eventRecurrence: Event.RecurrenceType = .none
@State var hey: UUID = UUID()
@State private var showingAddEventView = false
@State private var searchInput: String = ""
var filteredEvents: [Event] {
if searchInput.isEmpty {
return viewModel.events.filter() {!$0.complete}
} else {
return viewModel.events.filter {
$0.name.localizedCaseInsensitiveContains(searchInput) ||
$0.notes.localizedCaseInsensitiveContains(searchInput)
}
}
}
@State var selection: Tab = .home
var noEvents: Bool {
if viewModel.events.count == 0 {
return true
} else {
return false
}
}
@FocusState private var focusedField: Field?
@FocusState private var focusedTab: Tab?
var body: some View {
TabView {
NavigationStack {
ZStack {
backgroundGradient
VStack {
ZStack {
TextField(
"\(Image(systemName: "magnifyingglass")) Search",
text: $searchInput
)
.padding(.trailing, searchInput.isEmpty ? 0 : 30)
.animation(.spring, value: searchInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
.submitLabel(.done)
.focused($focusedField, equals: Field.Search)
.onSubmit {
focusedField = nil
}
MagicClearButton(text: $searchInput)
.onTapGesture {
focusedField = nil
}
}
.padding(.horizontal)
if filteredEvents.isEmpty && !searchInput.isEmpty {
HelpView(searchInput: $searchInput, focusedField: focusedField)
} else {
ScrollView {
ForEach(filteredEvents) { event in
EventListView(viewModel: viewModel, event: event)
}
.onDelete(perform: viewModel.removeEvent)
.id(hey)
.onReceive(viewModel.objectWillChange) {
hey = UUID()
}
.padding(.horizontal)
if /*!searchInput.isEmpty && */filteredEvents.isEmpty {
HelpView(
searchInput: $searchInput,
focusedField: focusedField
)
}
Spacer()
}
}
}
.navigationTitle("Near Future")
.apply {
if #available(iOS 17, *) {
$0.toolbarTitleDisplayMode(.inlineLarge)
} else {
$0.navigationBarTitleDisplayMode(.inline)
}
}
.sheet(isPresented: $showingAddEventView) {
AddEventView(
viewModel: viewModel,
eventName: $eventName,
eventComplete: $eventComplete,
eventCompleteDesc: $eventCompleteDesc,
eventSymbol: $eventSymbol,
eventColor: $eventColor,
eventNotes: $eventNotes,
eventDate: $eventDate,
eventRecurrence: $eventRecurrence,
adding: true //adding event
)
.presentationDragIndicator(.visible)
.apply {
if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial)
}
}
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
AddEventButton(showingAddEventView: $showingAddEventView)
}
}
}
}
TabView(selection: $selection) {
HomeView(viewModel: viewModel, settingsModel: settingsModel)
.tabItem {
Label("Home", systemImage: "house")
}
.focused($focusedTab, equals: Tab.Home)
.tag(Tab.home)
ArchiveView(viewModel: viewModel)
.tabItem() {
Label("Archive", systemImage: "tray.full")
}
.focused($focusedTab, equals: Tab.Archive)
.tag(Tab.archive)
StatsView(viewModel: viewModel)
.tabItem {
Label("Statistics", systemImage: "chart.pie")
}
.focused($focusedTab, equals: Tab.Statistics)
.tag(Tab.stats)
SettingsView(viewModel: viewModel, settingsModel: settingsModel)
.tabItem {
Label("Settings", systemImage: "gear")
}
.focused($focusedTab, equals: Tab.Settings)
.tag(Tab.settings)
}
.apply {
if #available(iOS 17, *) {
$0.sensoryFeedback(.impact(weight: .heavy, intensity: 1), trigger: selection)
}
}
.sheet(isPresented: $settingsModel.settings.showWhatsNew) {
WhatsNewView(settingsModel: settingsModel)
}
}
}
@@ -171,9 +66,6 @@ struct ContentView: View {
}
extension View {
var appearance: ColorScheme {
return UITraitCollection.current.userInterfaceStyle == .dark ? .dark : .light
}
var backgroundGradient: some View {
return LinearGradient(
gradient: Gradient(colors: [.bgTop, .two]),
@@ -182,8 +74,23 @@ extension View {
)
.ignoresSafeArea(.all)
}
}
extension View {
func apply<V: View>(@ViewBuilder _ block: (Self) -> V) -> V { block(self) }
}
extension AnyTransition {
static var moveAndFade: AnyTransition {
.asymmetric(
insertion: .move(edge: .leading),
removal: .move(edge: .trailing)
)
.combined(with: .opacity)
}
static var moveAndFadeReversed: AnyTransition {
.asymmetric(
insertion: .move(edge: .trailing),
removal: .move(edge: .leading)
)
.combined(with: .opacity)
}
}

View File

@@ -75,7 +75,7 @@ struct EventListView: View {
VStack {
Text("\(daysUntilEvent(event.date).long)")
.font(.subheadline)
.foregroundStyle(.one)
.foregroundStyle(event.date.timeIntervalSinceNow < 0 ? .red : .one)
}
Button() {
withAnimation {
@@ -111,13 +111,19 @@ struct EventListView: View {
.frame(maxWidth: 25, maxHeight: 25)
.shadow(radius: 5)
.padding(.trailing, 5)
.apply {
if #available(iOS 17, *) {
$0.sensoryFeedback(.success, trigger: event.complete)
}
}
}
.transition(.opacity)
.padding(.vertical, 5)
.background(.ultraThinMaterial)
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(
.one.opacity(appearance == .dark ? 0.5 : 1),
.one.opacity(0.5),
lineWidth: 1
)
)

View File

@@ -0,0 +1,129 @@
//
// HomeView.swift
// NearFuture
//
// Created by neon443 on 12/05/2025.
//
import SwiftUI;import AppIntents
struct HomeView: View {
@ObservedObject var viewModel: EventViewModel
@ObservedObject var settingsModel: SettingsViewModel
@State private var eventName = ""
@State private var eventComplete = false
@State private var eventCompleteDesc = ""
@State private var eventSymbol = "star"
@State private var eventColor: Color = randomColor()
@State private var eventNotes = ""
@State private var eventDate = Date()
@State private var eventRecurrence: Event.RecurrenceType = .none
@State private var showingAddEventView = false
@State private var searchInput: String = ""
@Environment(\.colorScheme) var appearance
var darkMode: Bool {
return appearance == .dark
}
var filteredEvents: [Event] {
if searchInput.isEmpty {
if settingsModel.settings.showCompletedInHome {
return viewModel.events
} else {
return viewModel.events.filter() {!$0.complete}
}
} else {
return viewModel.events.filter {
$0.name.localizedCaseInsensitiveContains(searchInput) ||
$0.notes.localizedCaseInsensitiveContains(searchInput)
}
}
}
@State private var focusedTab: Tab = .home
@FocusState private var focusedField: Field?
var body: some View {
NavigationStack {
ZStack {
backgroundGradient
VStack {
ZStack {
TextField(
"\(Image(systemName: "magnifyingglass")) Search",
text: $searchInput
)
.padding(.trailing, searchInput.isEmpty ? 0 : 30)
.animation(.spring, value: searchInput)
.textFieldStyle(RoundedBorderTextFieldStyle())
.submitLabel(.done)
.focused($focusedField, equals: Field.Search)
.onSubmit {
focusedField = nil
}
MagicClearButton(text: $searchInput)
.onTapGesture {
focusedField = nil
}
}
.padding(.horizontal)
if filteredEvents.isEmpty && !searchInput.isEmpty {
HelpView(searchInput: $searchInput, focusedField: focusedField)
} else {
ScrollView {
ForEach(filteredEvents) { event in
EventListView(viewModel: viewModel, event: event)
.transition(.moveAndFade)
.id(event.complete)
}
.padding(.horizontal)
if filteredEvents.isEmpty {
HelpView(
searchInput: $searchInput,
focusedField: focusedField
)
}
Spacer()
}
.animation(.default, value: filteredEvents)
}
}
.navigationTitle("Near Future")
.apply {
if #available(iOS 17, *) {
$0.toolbarTitleDisplayMode(.inlineLarge)
} else {
$0.navigationBarTitleDisplayMode(.inline)
}
}
.sheet(isPresented: $showingAddEventView) {
AddEventView(
viewModel: viewModel,
eventName: $eventName,
eventComplete: $eventComplete,
eventCompleteDesc: $eventCompleteDesc,
eventSymbol: $eventSymbol,
eventColor: $eventColor,
eventNotes: $eventNotes,
eventDate: $eventDate,
eventRecurrence: $eventRecurrence,
adding: true //adding event
)
}
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
AddEventButton(showingAddEventView: $showingAddEventView)
}
}
}
}
}
}
#Preview {
HomeView(
viewModel: dummyEventViewModel(),
settingsModel: dummySettingsViewModel()
)
}

View File

@@ -0,0 +1,31 @@
//
// AboutView.swift
// NearFuture
//
// Created by neon443 on 12/05/2025.
//
import SwiftUI
struct AboutView: View {
var body: some View {
VStack {
Image(uiImage: #imageLiteral(resourceName: "NearFutureIcon.png"))
.resizable()
.scaledToFit()
.frame(width: 100)
.clipShape(RoundedRectangle(cornerRadius: 25))
Text("Near Future")
.bold()
.monospaced()
.font(.title)
.frame(maxWidth: .infinity)
Text("Version " + getVersion() + " (\(getBuildID()))")
.frame(maxWidth: .infinity)
}
}
}
#Preview {
AboutView()
}

View File

@@ -2,7 +2,7 @@
// SettingsView.swift
// NearFuture
//
// Created by Nihaal Sharma on 29/12/2024.
// Created by neon443 on 29/12/2024.
//
import SwiftUI
@@ -40,6 +40,22 @@ struct SettingsView: View {
}
}
func changeIcon(to: String) {
guard UIApplication.shared.supportsAlternateIcons else {
print("doesnt tsupport alternate icons")
return
}
guard to != "orange" else {
UIApplication.shared.setAlternateIconName(nil) { error in
print(error as Any)
}
return
}
UIApplication.shared.setAlternateIconName(to) { error in
print(error as Any)
}
}
var body: some View {
NavigationStack {
ZStack {
@@ -47,11 +63,12 @@ struct SettingsView: View {
List {
ScrollView(.horizontal) {
HStack {
ForEach(settingsModel.accentChoices, id: \.self) { color in
ForEach(settingsModel.accentChoices, id: \.self) { choice in
let color = Color(uiColor: UIColor(named: "uiColors/\(choice)")!)
ZStack {
Button() {
settingsModel.settings.tint.colorBind = color
settingsModel.saveSettings()
settingsModel.changeTint(to: choice)
changeIcon(to: choice)
} label: {
Circle()
.foregroundStyle(color)
@@ -67,6 +84,13 @@ struct SettingsView: View {
}
}
}
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 {
@@ -89,6 +113,7 @@ struct SettingsView: View {
NavigationLink() {
iCloudSettingsView(
viewModel: viewModel,
settingsModel: settingsModel,
hasUbiquitous: $hasUbiquitous,
lastSyncWasSuccessful: $lastSyncWasSuccessful,
lastSyncWasNormalAgo: $lastSyncWasNormalAgo,
@@ -148,20 +173,7 @@ struct SettingsView: View {
}
}
Section("About") {
VStack {
Image(uiImage: #imageLiteral(resourceName: "NearFutureIcon.png"))
.resizable()
.scaledToFit()
.frame(width: 100)
.clipShape(RoundedRectangle(cornerRadius: 25))
Text("Near Future")
.bold()
.monospaced()
.font(.title)
.frame(maxWidth: .infinity)
Text("Version " + getVersion() + " (\(getBuildID()))")
.frame(maxWidth: .infinity)
}
AboutView()
}
}
}
@@ -184,17 +196,3 @@ struct SettingsView: View {
settingsModel: dummySettingsViewModel()
)
}
func getVersion() -> String {
guard let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] else {
fatalError("no bundle id wtf lol")
}
return "\(version)"
}
func getBuildID() -> String {
guard let build = Bundle.main.infoDictionary?["CFBundleVersion"] else {
fatalError("wtf did u do w the build number")
}
return "\(build)"
}

View File

@@ -0,0 +1,138 @@
//
// WhatsNewView.swift
// NearFuture
//
// Created by neon443 on 12/05/2025.
//
import SwiftUI
struct WhatsNewView: View {
@ObservedObject var settingsModel: SettingsViewModel
@Environment(\.dismiss) var dismiss
struct WhatsNewChunk: Identifiable {
var id: UUID = UUID()
var symbol: String
var title: String
var subtitle: String
}
@State var bye = false
var whatsNewChunks: [WhatsNewChunk] {
return [
WhatsNewChunk(
symbol: "iphone.radiowaves.left.and.right",
title: "Haptic Feedback",
subtitle: "Lovely haptic feedback when completing and adding events, and selecting tabs"
),
WhatsNewChunk(
symbol: "app",
title: "App Icons",
subtitle: "You now get a special app icon that matches the color you choose in settings!"
),
WhatsNewChunk(
symbol: "apps.iphone",
title: "Widgets Day Count Fix",
subtitle: "The day count for widgets are now red for overdue events, just like the app, and are now more readable"
),
WhatsNewChunk(
symbol: settingsModel.device.sf,
title: "This Screen",
subtitle: "This update add a Whats New page that will tell you (suprise!) What's New"
),
WhatsNewChunk(
symbol: "bell.badge.fill",
title: "Notifications",
subtitle: "Events now have notifications, reminding you to complete them!"
),
WhatsNewChunk(
symbol: "list.bullet.indent",
title: "Animations!",
subtitle: "I added animations for adding, removing and ticking events - animations are definitely the most important change"
)
]
}
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
)
}
}
}
}
Button() {
bye.toggle()
dismiss()
} label: {
Text("Continue")
.font(.headline)
.frame(height: 40)
.bold()
.frame(maxWidth: .infinity)
}
.buttonStyle(BorderedProminentButtonStyle())
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding().padding()
.apply {
if #available(iOS 17, *) {
$0.sensoryFeedback(.impact(weight: .heavy, intensity: 1), trigger: bye)
}
}
}
.scrollContentBackground(.hidden)
.presentationDragIndicator(.visible)
.apply {
if #available(iOS 16.4, *) {
$0.presentationBackground(.ultraThinMaterial)
}
}
.onDisappear {
settingsModel.settings.prevAppVersion = getVersion()+getBuildID()
settingsModel.saveSettings()
}
}
}
#Preview {
Color.accent
.ignoresSafeArea(.all)
.sheet(isPresented: .constant(true)) {
WhatsNewView(settingsModel: dummySettingsViewModel())
}
}
struct WhatsNewChunkView: View {
@State var symbol: String
@State var title: String
@State var subtitle: String
var body: some View {
HStack {
Image(systemName: symbol)
.resizable()
.scaledToFit()
.frame(width: 30, height: 30)
.foregroundStyle(Color.accentColor)
.padding(.trailing, 15)
VStack(alignment: .leading) {
Text(title)
.font(.headline)
.bold()
Text(subtitle)
.foregroundStyle(.gray)
.font(.subheadline)
.fixedSize(horizontal: false, vertical: true)
}
}
}
}

View File

@@ -8,7 +8,8 @@
import SwiftUI
struct iCloudSettingsView: View {
@State var viewModel: EventViewModel
@ObservedObject var viewModel: EventViewModel
@ObservedObject var settingsModel: SettingsViewModel
@State var showPushAlert: Bool = false
@State var showPullAlert: Bool = false
@@ -18,19 +19,6 @@ struct iCloudSettingsView: View {
@Binding var localCountEqualToiCloud: Bool
@Binding var icloudCountEqualToLocal: Bool
let asi = ProcessInfo().isiOSAppOnMac
let model = UIDevice().model
var device: (sf: String, label: String) {
if asi {
return (sf: "laptopcomputer", label: "Computer")
} else if model == "iPhone" {
return (sf: model.lowercased(), label: model)
} else if model == "iPad" {
return (sf: model.lowercased(), label: model)
}
return (sf: "iphone", label: "iPhone")
}
var updateStatus: () -> Void
var body: some View {
@@ -107,7 +95,7 @@ struct iCloudSettingsView: View {
}
}
ZStack {
Image(systemName: device.sf)
Image(systemName: settingsModel.device.sf)
.resizable()
.scaledToFit()
.frame(width: 75, height: 75)
@@ -117,7 +105,7 @@ struct iCloudSettingsView: View {
.monospaced()
.bold()
}
Text(device.label)
Text(settingsModel.device.label)
}
Spacer()
}
@@ -196,6 +184,7 @@ struct iCloudSettingsView: View {
#Preview("iCloudSettingsView") {
iCloudSettingsView(
viewModel: dummyEventViewModel(),
settingsModel: dummySettingsViewModel(),
hasUbiquitous: .constant(true),
lastSyncWasSuccessful: .constant(true),
lastSyncWasNormalAgo: .constant(true),

View File

@@ -2,7 +2,7 @@
// StatsView.swift
// NearFuture
//
// Created by Nihaal Sharma on 05/01/2025.
// Created by neon443 on 05/01/2025.
//
import SwiftUI

View File

@@ -2,11 +2,12 @@
// NearFutureWidgets.swift
// NearFutureWidgets
//
// Created by Nihaal Sharma on 02/01/2025.
// Created by neon443 on 02/01/2025.
//
import WidgetKit
import SwiftUI
import AppIntents
// Timeline Entry for Widget
struct EventWidgetEntry: TimelineEntry {
@@ -46,6 +47,24 @@ struct EventWidgetProvider: TimelineProvider {
}
}
// Event Widget View
struct EventWidgetView: View {
var entry: EventWidgetEntry
@@ -128,17 +147,36 @@ struct EventWidgetView: View {
Spacer()
//short days till if not large widget
if isLarge {
Text(daysUntilEvent(event.date).long)
.font(.caption)
.multilineTextAlignment(.trailing)
.foregroundColor(event.color.color)
.padding(.trailing, -12)
} else {
if !isLarge {
Text(daysUntilEvent(event.date).short)
.font(.caption)
.multilineTextAlignment(.trailing)
.foregroundColor(event.color.color)
.foregroundColor(event.date < Date() ? .red : .primary)
.padding(.trailing, -12)
} else {
Button(
intent: CompleteEvent(
eventID: IntentParameter(
title: LocalizedStringResource(
stringLiteral: event.id.uuidString
)
)
)
) {
if event.complete {
Circle()
.frame(width: 10)
.foregroundStyle(.green)
} else {
Circle()
.frame(width: 10)
.foregroundStyle(.gray)
}
}
Text(daysUntilEvent(event.date).long)
.font(.caption)
.multilineTextAlignment(.trailing)
.foregroundColor(event.date < Date() ? .red : .primary)
.padding(.trailing, -12)
}
}

View File

@@ -2,7 +2,7 @@
// NearFutureWidgetsBundle.swift
// NearFutureWidgets
//
// Created by Nihaal Sharma on 02/01/2025.
// Created by neon443 on 02/01/2025.
//
import WidgetKit

View File

@@ -2,7 +2,7 @@
// NearFutureWidgetsLiveActivity.swift
// NearFutureWidgets
//
// Created by Nihaal Sharma on 02/01/2025.
// Created by neon443 on 02/01/2025.
//
//import ActivityKit

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "bloo.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "blooDark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "NearFutureIconTint.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 KiB

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "blue.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "blueDark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "NearFutureIconTint.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 KiB

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "green.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "greenDark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "NearFutureIconTint.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 734 KiB

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "pink.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "pinkDark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "NearFutureIconTint.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 KiB

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "purple.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "purpleDark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "NearFutureIconTint.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 KiB

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "red.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "redDark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "NearFutureIconTint.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 751 KiB

View File

@@ -1,38 +0,0 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.000",
"green" : "0.000",
"red" : "0.000"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "1.000",
"green" : "1.000",
"red" : "1.000"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,20 @@
{
"colors" : [
{
"color" : {
"color-space" : "display-p3",
"components" : {
"alpha" : "1.000",
"blue" : "0.341",
"green" : "0.286",
"red" : "0.114"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "yellow.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "yellowDark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "NearFutureIconTint.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 KiB

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

BIN
Resources/Grue.pxd Normal file
View File

Binary file not shown.

BIN
Resources/GrueDark.pxd Normal file
View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.

View File

Binary file not shown.