diff --git a/BeeKit/Managers/CurrentUserManager.swift b/BeeKit/Managers/CurrentUserManager.swift index 011721c58..251686bc9 100644 --- a/BeeKit/Managers/CurrentUserManager.swift +++ b/BeeKit/Managers/CurrentUserManager.swift @@ -128,8 +128,10 @@ public actor CurrentUserManager { return keychain.get(CurrentUserManager.accessTokenKey) } - public var username :String? { - return user(context: modelContext)?.username + public var username: String? { + get async { + user(context: modelContext)?.username + } } public nonisolated func signedIn(context: NSManagedObjectContext) -> Bool { diff --git a/BeeminderWidget/BeeminderGoalCountdownWidget.swift b/BeeminderWidget/BeeminderGoalCountdownWidget.swift index b7b875b1e..46c015bd8 100644 --- a/BeeminderWidget/BeeminderGoalCountdownWidget.swift +++ b/BeeminderWidget/BeeminderGoalCountdownWidget.swift @@ -11,13 +11,14 @@ import WidgetKit struct BeeminderGoalCountdownWidgetProvider: AppIntentTimelineProvider { func placeholder(in _: Context) -> BeeminderGoalCountdownWidgetEntry { - let goalDTO = usersGoals.randomElement() - - return .init(date: Date(), - configuration: GoalCountdownConfigurationAppIntent(), - updatedAt: Date().addingTimeInterval(-60 * 1000).timeIntervalSince1970, - username: username, - goalDTO: goalDTO) + .init(date: Date(), + configuration: GoalCountdownConfigurationAppIntent(), + updatedAt: Date().addingTimeInterval(-60 * 1000).timeIntervalSince1970, + username: "username", + goalDTO: BeeminderGoalCountdownGoalDTO(name: "goal1", + limSum: "+3 in 3 days", + countdownColor: Color.cyan, + lastTouch: "")) } func snapshot(for _: GoalCountdownConfigurationAppIntent, in context: Context) async -> BeeminderGoalCountdownWidgetEntry { @@ -25,7 +26,10 @@ struct BeeminderGoalCountdownWidgetProvider: AppIntentTimelineProvider { } func timeline(for configuration: GoalCountdownConfigurationAppIntent, in _: Context) async -> Timeline { - let goal = usersGoals.first { $0.name.caseInsensitiveCompare(configuration.goalName) == .orderedSame } + var goal: BeeminderGoalCountdownGoalDTO? { + guard let goalName = configuration.goalName else { return nil } + return usersGoals.first { $0.name.caseInsensitiveCompare(goalName) == .orderedSame } + } let updatedAt: TimeInterval? = { guard let lastTouch = goal?.lastTouch else { return nil } @@ -38,7 +42,7 @@ struct BeeminderGoalCountdownWidgetProvider: AppIntentTimelineProvider { return date?.timeIntervalSince1970 }() - let entries: [BeeminderGoalCountdownWidgetEntry] = [ + let entries: [BeeminderGoalCountdownWidgetEntry] = await [ BeeminderGoalCountdownWidgetEntry(date: Date(), configuration: configuration, updatedAt: updatedAt, @@ -52,10 +56,12 @@ struct BeeminderGoalCountdownWidgetProvider: AppIntentTimelineProvider { private extension BeeminderGoalCountdownWidgetProvider { private var username: String? { - ServiceLocator.currentUserManager.username + get async { + await ServiceLocator.currentUserManager.username + } } - private var usersGoals: [BeeminderGoalCountdownGoalDTO] { + var usersGoals: [BeeminderGoalCountdownGoalDTO] { ServiceLocator.goalManager .staleGoals(context: ServiceLocator.persistentContainer.viewContext)? .sorted(using: SortDescriptor(\.urgencyKey)) @@ -99,7 +105,7 @@ struct BeeminderGoalCountdownWidgetEntry: TimelineEntry { // let goal: Goal? let goalDTO: BeeminderGoalCountdownGoalDTO? - var userProvidedGoalName: String { + var userProvidedGoalName: String? { configuration.goalName } @@ -148,7 +154,7 @@ struct BeeminderGoalCountdownWidgetEntryView: View { Spacer() Spacer() - Text(entry.userProvidedGoalName) + Text(entry.userProvidedGoalName ?? "Edit Widget") .font(.title) .frame(width: .infinity) .minimumScaleFactor(0.2) @@ -177,7 +183,7 @@ struct BeeminderGoalCountdownWidgetEntryView: View { Spacer() - Text(entry.userProvidedGoalName) + Text(entry.userProvidedGoalName ?? "Edit Widget") .font(.title) .frame(width: .infinity) .minimumScaleFactor(0.2) diff --git a/BeeminderWidget/BeeminderGoalListWidget.swift b/BeeminderWidget/BeeminderGoalListWidget.swift index 50e39bfc6..68e27541c 100644 --- a/BeeminderWidget/BeeminderGoalListWidget.swift +++ b/BeeminderWidget/BeeminderGoalListWidget.swift @@ -19,7 +19,7 @@ struct BeeminderGoalListProvider: TimelineProvider { : BeeminderGoalListEntryGoalDTO.goalDTOs.shuffled() return .init(date: Date(), - username: username ?? "Player1", + username: "Player1", goals: goals.prefix(numGoals).map { $0 }) } @@ -28,21 +28,26 @@ struct BeeminderGoalListProvider: TimelineProvider { } func getTimeline(in _: Context, completion: @escaping @Sendable (Timeline) -> Void) { - let entries: [BeeminderGoalListEntry] = [ - .init(date: Date(), - username: username, - goals: usersGoals), - ] + Task { + let entries: [BeeminderGoalListEntry] = await [ + .init(date: Date(), + username: username, + goals: usersGoals), + ] - let timeline = Timeline(entries: entries, policy: .atEnd) + let timeline = Timeline(entries: entries, policy: .atEnd) - completion(timeline) + completion(timeline) + } + } } private extension BeeminderGoalListProvider { private var username: String? { - ServiceLocator.currentUserManager.username + get async { + await ServiceLocator.currentUserManager.username + } } private var usersGoals: [BeeminderGoalListEntryGoalDTO] { diff --git a/BeeminderWidget/BeeminderPledgedTodayWidget.swift b/BeeminderWidget/BeeminderPledgedTodayWidget.swift index 6c97b25f4..4d642092b 100644 --- a/BeeminderWidget/BeeminderPledgedTodayWidget.swift +++ b/BeeminderWidget/BeeminderPledgedTodayWidget.swift @@ -38,7 +38,9 @@ struct BeeminderPledgedTodayWidgetProvider: AppIntentTimelineProvider { private extension BeeminderPledgedTodayWidgetProvider { private var username: String? { - ServiceLocator.currentUserManager.username + get async { + await ServiceLocator.currentUserManager.username + } } private var usersGoals: [Goal] { diff --git a/BeeminderWidget/BeeminderWidgetConfigurationIntents.swift b/BeeminderWidget/BeeminderWidgetConfigurationIntents.swift index 353d2b65c..29e7d40ed 100644 --- a/BeeminderWidget/BeeminderWidgetConfigurationIntents.swift +++ b/BeeminderWidget/BeeminderWidgetConfigurationIntents.swift @@ -37,15 +37,27 @@ struct PledgedConfigurationAppIntent: WidgetConfigurationIntent { var denomination: BeeminderPledgeDenomination } -struct GoalCountdownConfigurationAppIntent: WidgetConfigurationIntent { +struct GoalCountdownConfigurationAppIntent: AppIntent, WidgetConfigurationIntent { static let title: LocalizedStringResource = "Colored Goal" static let description = IntentDescription("Shows a goal in its countdown color!") @Parameter(title: "Goal Name (aka slug)", - default: "dial", - inputOptions: String.IntentInputOptions(keyboardType: .default, capitalizationType: .none, autocorrect: false)) - var goalName: String + optionsProvider: BeeminderGoalCountdownWidgetProvider.GoalNameProvider()) + var goalName: String? @Parameter(title: "Show Summary of what you need to do to eke by, e.g., \"+2 within 1 day\".", default: true) var showLimSum: Bool } + +// namespace? +extension BeeminderGoalCountdownWidgetProvider { + struct GoalNameProvider: DynamicOptionsProvider { + func results() async throws -> [String] { + ServiceLocator.goalManager + .staleGoals(context: ServiceLocator.persistentContainer.viewContext)? + .sorted(using: SortDescriptor(\.slug)) + .map { $0.slug } + ?? [] + } + } +}