diff --git a/LeCountdown.xcodeproj/project.pbxproj b/LeCountdown.xcodeproj/project.pbxproj index c4a1665..211d254 100644 --- a/LeCountdown.xcodeproj/project.pbxproj +++ b/LeCountdown.xcodeproj/project.pbxproj @@ -128,6 +128,8 @@ C4BA2B6829A3C4AC00CB4FBA /* Context+Calculations.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6729A3C4AC00CB4FBA /* Context+Calculations.swift */; }; C4BA2B6A29A4BE1800CB4FBA /* Date+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B6929A4BE1800CB4FBA /* Date+Extensions.swift */; }; C4BA2B7129A51CA000CB4FBA /* SiriIntents.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B7029A51CA000CB4FBA /* SiriIntents.intentdefinition */; }; + C4BA2B7329A60CF000CB4FBA /* Shortcut.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B7229A60CF000CB4FBA /* Shortcut.swift */; }; + C4BA2B7929A65C1400CB4FBA /* UIDevice+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4BA2B7829A65C1400CB4FBA /* UIDevice+Extensions.swift */; }; C4F8B1532987FE6F005C86A5 /* LaunchWidgetLiveActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = C438C7D72981216200BF3EF9 /* LaunchWidgetLiveActivity.swift */; }; C4F8B15729891271005C86A5 /* Conductor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F8B15629891271005C86A5 /* Conductor.swift */; }; C4F8B15929891528005C86A5 /* forest_stream.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = C4F8B15829891528005C86A5 /* forest_stream.mp3 */; }; @@ -325,6 +327,8 @@ C4BA2B6929A4BE1800CB4FBA /* Date+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extensions.swift"; sourceTree = ""; }; C4BA2B6B29A4C47100CB4FBA /* LeCountdown.0.6.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = LeCountdown.0.6.2.xcdatamodel; sourceTree = ""; }; C4BA2B7029A51CA000CB4FBA /* SiriIntents.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = SiriIntents.intentdefinition; sourceTree = ""; }; + C4BA2B7229A60CF000CB4FBA /* Shortcut.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Shortcut.swift; sourceTree = ""; }; + C4BA2B7829A65C1400CB4FBA /* UIDevice+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extensions.swift"; sourceTree = ""; }; C4F8B15629891271005C86A5 /* Conductor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Conductor.swift; sourceTree = ""; }; C4F8B15829891528005C86A5 /* forest_stream.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = forest_stream.mp3; sourceTree = ""; }; C4F8B15E298961A7005C86A5 /* ReorderableForEach.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReorderableForEach.swift; sourceTree = ""; }; @@ -541,6 +545,8 @@ C438C81029829EAF00BF3EF9 /* PropertyWrappers.swift */, C40FDB612992985C0042A390 /* TextToSpeechRecorder.swift */, C4060DF4297AE9A7003FAB80 /* TimeInterval+Extensions.swift */, + C4BA2B7229A60CF000CB4FBA /* Shortcut.swift */, + C4BA2B7829A65C1400CB4FBA /* UIDevice+Extensions.swift */, ); path = Utils; sourceTree = ""; @@ -901,6 +907,7 @@ buildActionMask = 2147483647; files = ( C4F8B1D2298BF646005C86A5 /* PermissionAlertView.swift in Sources */, + C4BA2B7329A60CF000CB4FBA /* Shortcut.swift in Sources */, C4060DC9297AE73D003FAB80 /* Persistence.swift in Sources */, C4BA2B36299F82FB00CB4FBA /* Fakes.swift in Sources */, C498E5A1298D543900E90DE0 /* LiveTimer.swift in Sources */, @@ -915,6 +922,7 @@ C4BA2B57299FFA4F00CB4FBA /* AppGuard.swift in Sources */, C4F8B15729891271005C86A5 /* Conductor.swift in Sources */, C4BA2B5F299FFC8400CB4FBA /* StoreView.swift in Sources */, + C4BA2B7929A65C1400CB4FBA /* UIDevice+Extensions.swift in Sources */, C4F8B17E298AC234005C86A5 /* Alarm+CoreDataClass.swift in Sources */, C438C81129829EAF00BF3EF9 /* PropertyWrappers.swift in Sources */, C4F8B1BF298ACA0B005C86A5 /* StopwatchDialView.swift in Sources */, @@ -1433,6 +1441,7 @@ INFOPLIST_FILE = LaunchIntents/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = LaunchIntents; INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1459,6 +1468,7 @@ INFOPLIST_FILE = LaunchIntents/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = LaunchIntents; INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 16.2; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/LeCountdown.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/LeCountdown.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f9b0d7c --- /dev/null +++ b/LeCountdown.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/LeCountdown/Info.plist b/LeCountdown/Info.plist index ddc8c6b..2c97560 100644 --- a/LeCountdown/Info.plist +++ b/LeCountdown/Info.plist @@ -8,6 +8,7 @@ NSUserActivityTypes + app.kikai.NewCountdown LaunchTimerIntent SelectTimerIntent diff --git a/LeCountdown/LeCountdownApp.swift b/LeCountdown/LeCountdownApp.swift index 6ee4ce7..275cdf4 100644 --- a/LeCountdown/LeCountdownApp.swift +++ b/LeCountdown/LeCountdownApp.swift @@ -32,16 +32,13 @@ struct LeCountdownApp: App { @Environment(\.scenePhase) var scenePhase - #if os(iOS) - @Environment(\.horizontalSizeClass) private var horizontalSizeClass - #endif - var body: some Scene { WindowGroup { Group { #if os(iOS) - if horizontalSizeClass == .compact { + + if UIDevice.isPhoneIdiom { CompactHomeView() .environment(\.managedObjectContext, persistenceController.container.viewContext) } else { diff --git a/LeCountdown/Utils/Shortcut.swift b/LeCountdown/Utils/Shortcut.swift new file mode 100644 index 0000000..c127d49 --- /dev/null +++ b/LeCountdown/Utils/Shortcut.swift @@ -0,0 +1,56 @@ +// +// Shortcut.swift +// LeCountdown +// +// Created by Laurent Morvillier on 22/02/2023. +// + +import Foundation +import UIKit +import CoreServices +import CoreSpotlight +import UniformTypeIdentifiers + +enum Shortcut: String { + + case newCountdown = "app.kikai.NewCountdown" + + var userActivity: NSUserActivity { + let activity = NSUserActivity(activityType: self.rawValue) + activity.persistentIdentifier = NSUserActivityPersistentIdentifier(self.rawValue) + + activity.title = self.title + activity.suggestedInvocationPhrase = self.suggestedInvocationPhrase + activity.isEligibleForSearch = true + activity.isEligibleForPrediction = true + + let attributes = CSSearchableItemAttributeSet(contentType: UTType.item) + attributes.contentDescription = self.contentDescription + attributes.thumbnailData = self.image?.jpegData(compressionQuality: 1.0) + activity.contentAttributeSet = attributes + return activity + } + + var title: String { + switch self { + case .newCountdown: return NSLocalizedString("New countdown", comment: "") + } + } + + var suggestedInvocationPhrase: String { + switch self { + case .newCountdown: return NSLocalizedString("Create countdown", comment: "") + } + } + + var contentDescription: String { + switch self { + case .newCountdown: return NSLocalizedString("Create a new countdown", comment: "") + } + } + + var image: UIImage? { + return UIImage(named: "pic1") + } + +} diff --git a/LeCountdown/Utils/UIDevice+Extensions.swift b/LeCountdown/Utils/UIDevice+Extensions.swift new file mode 100644 index 0000000..022b121 --- /dev/null +++ b/LeCountdown/Utils/UIDevice+Extensions.swift @@ -0,0 +1,20 @@ +// +// UIDevice+Extensions.swift +// LeCountdown +// +// Created by Laurent Morvillier on 22/02/2023. +// + +import Foundation +import UIKit + +extension UIDevice { + + @objc static var isPadIdiom: Bool { + return UIDevice.current.userInterfaceIdiom == .pad + } + + @objc static var isPhoneIdiom: Bool { + return UIDevice.current.userInterfaceIdiom == .phone + } +} diff --git a/LeCountdown/Views/ContentView.swift b/LeCountdown/Views/ContentView.swift index 281eec3..b9a5305 100644 --- a/LeCountdown/Views/ContentView.swift +++ b/LeCountdown/Views/ContentView.swift @@ -55,10 +55,6 @@ struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext - #if os(iOS) - @Environment(\.horizontalSizeClass) private var horizontalSizeClass - #endif - @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \T.order, ascending: true)], animation: .default) @@ -168,7 +164,7 @@ struct ContentView: View { fileprivate func _columnCount() -> Int { #if os(iOS) - if horizontalSizeClass == .compact { + if UIDevice.isPhoneIdiom { return 2 } else { return 3 diff --git a/LeCountdown/Views/Countdown/NewCountdownView.swift b/LeCountdown/Views/Countdown/NewCountdownView.swift index 0d6ec7e..23fbdfb 100644 --- a/LeCountdown/Views/Countdown/NewCountdownView.swift +++ b/LeCountdown/Views/Countdown/NewCountdownView.swift @@ -15,16 +15,24 @@ struct NewCountdownView : View { @Binding var isPresented: Bool var tabSelection: Binding + var userActivity: NSUserActivity init(isPresented: Binding, tabSelection: Binding) { _isPresented = isPresented self.tabSelection = tabSelection + self.userActivity = Shortcut.newCountdown.userActivity } var body: some View { CountdownEditView(isPresented: $isPresented, tabSelection: self.tabSelection) .environment(\.managedObjectContext, viewContext) .navigationTitle("New countdown") + .onAppear { + self.userActivity.becomeCurrent() + } + .onDisappear { + self.userActivity.resignCurrent() + } } } diff --git a/LeCountdown/Views/HomeView.swift b/LeCountdown/Views/HomeView.swift index 635e320..4acfabc 100644 --- a/LeCountdown/Views/HomeView.swift +++ b/LeCountdown/Views/HomeView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import CoreSpotlight struct CompactHomeView: View { @@ -80,12 +81,16 @@ struct RegularHomeView: View { .tag(2) }.tabViewStyle(.page(indexDisplayMode: .never)) } - + .onContinueUserActivity(Shortcut.newCountdown.rawValue, perform: _handleNewCountdown) .onOpenURL { _ in self.tabSelection = 1 } } + fileprivate func _handleNewCountdown(_ userActivity: NSUserActivity) { + print(">>> new countdown") + } + } struct CompactHomeView_Previews: PreviewProvider { diff --git a/LeCountdown/Views/LiveTimerListView.swift b/LeCountdown/Views/LiveTimerListView.swift index 282d39a..630f308 100644 --- a/LeCountdown/Views/LiveTimerListView.swift +++ b/LeCountdown/Views/LiveTimerListView.swift @@ -197,10 +197,6 @@ struct LiveTimerListView: View { @Environment(\.managedObjectContext) private var viewContext @EnvironmentObject var conductor: Conductor - #if os(iOS) - @Environment(\.horizontalSizeClass) private var horizontalSizeClass - #endif - var body: some View { // ScrollView { @@ -234,7 +230,7 @@ struct LiveTimerListView: View { fileprivate func _columnCount() -> Int { #if os(iOS) - if horizontalSizeClass == .compact { + if UIDevice.isPhoneIdiom { return 2 } else { return 3 diff --git a/LeCountdown/Views/PresetsView.swift b/LeCountdown/Views/PresetsView.swift index 22814f2..6204804 100644 --- a/LeCountdown/Views/PresetsView.swift +++ b/LeCountdown/Views/PresetsView.swift @@ -145,10 +145,6 @@ struct PresetsView: View { @Environment(\.managedObjectContext) private var viewContext - #if os(iOS) - @Environment(\.horizontalSizeClass) private var horizontalSizeClass - #endif - @StateObject var model: PresetModel = PresetModel() @State var isPresented: Bool = false @@ -160,7 +156,7 @@ struct PresetsView: View { fileprivate func _columnCount() -> Int { #if os(iOS) - if horizontalSizeClass == .compact { + if UIDevice.isPhoneIdiom { return 2 } else { return 3 diff --git a/LeCountdown/Views/Stats/ActivityView.swift b/LeCountdown/Views/Stats/ActivityView.swift index 90f807d..e5371b0 100644 --- a/LeCountdown/Views/Stats/ActivityView.swift +++ b/LeCountdown/Views/Stats/ActivityView.swift @@ -12,7 +12,7 @@ struct ActivityView: View { @Environment(\.managedObjectContext) private var viewContext - var activity: Activity + let activity: Activity @State var selectedTimeFrame: TimeFrame = .all @@ -36,8 +36,10 @@ struct ActivityView: View { // } // Section { - RecordsView(activity: self.activity, - timeFrame: self.selectedTimeFrame) + + let filters = self.selectedTimeFrame.filters(context: viewContext) + + RecordsView(activity: self.activity, filters: filters) .environment(\.managedObjectContext, viewContext) // } // } @@ -106,6 +108,6 @@ enum TimeFrame: Int, Identifiable, CaseIterable { struct ActivityView_Previews: PreviewProvider { static var previews: some View { - ActivityView(activity: Activity.fake(context: PersistenceController.preview.container.viewContext)) + ActivityView(activity: Activity.fake(context: PersistenceController.preview.container.viewContext), selectedTimeFrame: .all) } } diff --git a/LeCountdown/Views/Stats/RecordsView.swift b/LeCountdown/Views/Stats/RecordsView.swift index eca616b..f0be1ed 100644 --- a/LeCountdown/Views/Stats/RecordsView.swift +++ b/LeCountdown/Views/Stats/RecordsView.swift @@ -22,11 +22,17 @@ struct RecordsView: View { @Environment(\.managedObjectContext) private var viewContext - var activity: Activity + let activity: Activity + + let filters: [Filter] - var timeFrame: TimeFrame +// let timeFrame: TimeFrame { +// didSet { +// self._loadFilters() +// } +// } - @StateObject var model: RecordsModel = RecordsModel() +// @StateObject var model: RecordsModel = RecordsModel() // var request: FetchRequest @@ -38,18 +44,16 @@ struct RecordsView: View { var body: some View { List { - ForEach(self.model.filters) { filter in + ForEach(self.filters) { filter in RecordsSectionView(activity: self.activity, filter: filter) .environment(\.managedObjectContext, viewContext) } - }.onChange(of: self.timeFrame) { newValue in - self._loadFilters() } } - fileprivate func _loadFilters() { - self.model.loadFilters(activity: self.activity, timeFrame: self.timeFrame, context: self.viewContext) - } +// fileprivate func _loadFilters() { +// self.model.loadFilters(activity: self.activity, timeFrame: self.timeFrame, context: self.viewContext) +// } } @@ -131,6 +135,6 @@ struct RecordsView_Previews: PreviewProvider { static var previews: some View { RecordsView(activity: Activity.fake(context: PersistenceController.preview.container.viewContext), - timeFrame: .all) + filters: []) } }