From 5f819bd0b4f307135bdd1bd5a21f6511f7e3f4c8 Mon Sep 17 00:00:00 2001 From: Hillel Coren Date: Thu, 22 Jun 2023 23:55:57 +0300 Subject: [PATCH] macOS widgets --- lib/ui/app/menu_drawer.dart | 34 +++-- lib/ui/app/window_manager.dart | 6 +- macos/CompanyIntent/IntentHandler.swift | 22 +++- .../DashboardWidget.intentdefinition | 122 +++++++++++++++++- macos/DashboardWidget/DashboardWidget.swift | 10 +- 5 files changed, 174 insertions(+), 20 deletions(-) diff --git a/lib/ui/app/menu_drawer.dart b/lib/ui/app/menu_drawer.dart index 2ea46e408..f3fdf5912 100644 --- a/lib/ui/app/menu_drawer.dart +++ b/lib/ui/app/menu_drawer.dart @@ -9,6 +9,7 @@ import 'package:flutter/services.dart'; // Package imports: import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_styled_toast/flutter_styled_toast.dart'; +import 'package:invoiceninja_flutter/data/models/dashboard_model.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_actions.dart'; import 'package:invoiceninja_flutter/redux/reports/reports_actions.dart'; import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart'; @@ -1405,20 +1406,27 @@ void _showAbout(BuildContext context) async { ), ]); } else { + final json = jsonEncode(WidgetData( + url: formatApiUrl(state.authState.url), + companyId: state.account.defaultCompanyId, + dateRanges: Map.fromIterable(DateRange.values, + key: (dynamic item) => toSnakeCase('$item'), + value: (dynamic item) => + localization.lookup('$item')), + companies: { + for (var userCompany in state.userCompanyStates + .where((state) => state.company.hasName)) + userCompany.company.id: + WidgetCompany.fromUserCompany( + userCompanyState: userCompany, + staticState: state.staticState, + ) + })); + + print('## Set Widget Data: $json'); + await UserDefaults.setString( - 'widget_data', - jsonEncode(WidgetData( - url: formatApiUrl(state.authState.url), - companyId: state.account.defaultCompanyId, - companies: { - for (var userCompany - in state.userCompanyStates.where((state) => state.company.hasName)) - userCompany.company.id: - WidgetCompany.fromUserCompany( - userCompanyState: userCompany, - staticState: state.staticState,) - })), - 'group.com.invoiceninja.app'); + 'widget_data', json, 'group.com.invoiceninja.app'); await WidgetKit.reloadAllTimelines(); /* diff --git a/lib/ui/app/window_manager.dart b/lib/ui/app/window_manager.dart index f8a508daa..ea06ad2b1 100644 --- a/lib/ui/app/window_manager.dart +++ b/lib/ui/app/window_manager.dart @@ -101,22 +101,26 @@ class WidgetData { this.url, this.companies, this.companyId, + this.dateRanges, }); WidgetData.fromJson(Map json) : url = json['url'], companyId = json['company_id'], - companies = json['companies']; + companies = json['companies'], + dateRanges = json['date_ranges']; Map toJson() => { 'companies': companies, 'company_id': companyId, 'url': url, + 'date_ranges': dateRanges, }; final String url; final String companyId; final Map companies; + final Map dateRanges; } class WidgetCompany { diff --git a/macos/CompanyIntent/IntentHandler.swift b/macos/CompanyIntent/IntentHandler.swift index cc61f7038..a11a23a74 100644 --- a/macos/CompanyIntent/IntentHandler.swift +++ b/macos/CompanyIntent/IntentHandler.swift @@ -1,10 +1,9 @@ import Intents class IntentHandler: INExtension, ConfigurationIntentHandling { - private func loadWidgetData() -> WidgetData { let sharedDefaults = UserDefaults(suiteName: "group.com.invoiceninja.app") - var widgetData: WidgetData = WidgetData(url: "", companyId: "", companies: [:]) + var widgetData: WidgetData = WidgetData(url: "", companyId: "", companies: [:], dateRanges: [:]) if let sharedDefaults = sharedDefaults { do { @@ -53,6 +52,25 @@ class IntentHandler: INExtension, ConfigurationIntentHandling { let currency = company?.currencies[company!.currencyId]; return Currency(identifier: currency!.id, display: currency!.name) } + + func provideDateRangeOptionsCollection(for intent: ConfigurationIntent) async throws -> INObjectCollection { + let widgetData = loadWidgetData() + + + let dateRanges = widgetData.dateRanges.keys.map { dateRange in + DateRange(identifier: dateRange, display: widgetData.dateRanges[dateRange]!) + } + + return INObjectCollection(items: dateRanges) + } + + func defaultDateRange(for intent: ConfigurationIntent) -> DateRange? { + let widgetData = loadWidgetData() + let defaultDateRange = "last30_days"; + let dateRamge = widgetData.dateRanges[defaultDateRange]!; + return DateRange(identifier: defaultDateRange, display: dateRamge) + } + override func handler(for intent: INIntent) -> Any { return self diff --git a/macos/DashboardWidget/DashboardWidget.intentdefinition b/macos/DashboardWidget/DashboardWidget.intentdefinition index bd59c8023..99eab223c 100644 --- a/macos/DashboardWidget/DashboardWidget.intentdefinition +++ b/macos/DashboardWidget/DashboardWidget.intentdefinition @@ -80,7 +80,7 @@ INIntentIneligibleForSuggestions INIntentLastParameterTag - 9 + 11 INIntentName Configuration INIntentParameters @@ -219,6 +219,63 @@ INIntentParameterType Integer + + INIntentParameterConfigurable + + INIntentParameterDisplayName + Date Range + INIntentParameterDisplayNameID + hGdezL + INIntentParameterDisplayPriority + 4 + INIntentParameterName + dateRange + INIntentParameterObjectType + DateRange + INIntentParameterObjectTypeNamespace + 88xZPY + INIntentParameterPromptDialogs + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Configuration + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogType + Primary + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + There are ${count} options matching ‘${dateRange}’. + INIntentParameterPromptDialogFormatStringID + M1c9EE + INIntentParameterPromptDialogType + DisambiguationIntroduction + + + INIntentParameterPromptDialogCustom + + INIntentParameterPromptDialogFormatString + Just to confirm, you wanted ‘${dateRange}’? + INIntentParameterPromptDialogFormatStringID + YL2DLe + INIntentParameterPromptDialogType + Confirmation + + + INIntentParameterSupportsDynamicEnumeration + + INIntentParameterTag + 11 + INIntentParameterType + Object + INIntentResponse @@ -376,6 +433,69 @@ + + INTypeDisplayName + Date Range + INTypeDisplayNameID + cJBTgC + INTypeLastPropertyTag + 99 + INTypeName + DateRange + INTypeProperties + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 1 + INTypePropertyName + identifier + INTypePropertyTag + 1 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 2 + INTypePropertyName + displayString + INTypePropertyTag + 2 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 3 + INTypePropertyName + pronunciationHint + INTypePropertyTag + 3 + INTypePropertyType + String + + + INTypePropertyDefault + + INTypePropertyDisplayPriority + 4 + INTypePropertyName + alternativeSpeakableMatches + INTypePropertySupportsMultipleValues + + INTypePropertyTag + 4 + INTypePropertyType + SpeakableString + + + diff --git a/macos/DashboardWidget/DashboardWidget.swift b/macos/DashboardWidget/DashboardWidget.swift index c2ada6c2a..132262d58 100644 --- a/macos/DashboardWidget/DashboardWidget.swift +++ b/macos/DashboardWidget/DashboardWidget.swift @@ -14,7 +14,7 @@ struct Provider: IntentTimelineProvider { SimpleEntry(date: Date(), configuration: ConfigurationIntent(), - widgetData: WidgetData(url: "url", companyId: "", companies: [:]), + widgetData: WidgetData(url: "url", companyId: "", companies: [:], dateRanges: [:]), field: "Active Invoices", value: "$100.00") } @@ -25,7 +25,7 @@ struct Provider: IntentTimelineProvider { let entry = SimpleEntry(date: Date(), configuration: configuration, - widgetData: WidgetData(url: "url", companyId: "", companies: [:]), + widgetData: WidgetData(url: "url", companyId: "", companies: [:], dateRanges: [:]), field: "Active Invoices", value: "$100.00") @@ -146,11 +146,13 @@ struct WidgetData: Decodable, Hashable { let url: String let companyId: String let companies: [String: WidgetCompany] + let dateRanges: [String: String] enum CodingKeys: String, CodingKey { case url case companyId = "company_id" case companies + case dateRanges = "date_ranges" } } @@ -158,6 +160,7 @@ struct WidgetCompany: Decodable, Hashable { let id: String let name: String let token: String + let firstMonthOfYear: Int let accentColor: String let currencyId: String let currencies: [String: WidgetCurrency] @@ -166,6 +169,7 @@ struct WidgetCompany: Decodable, Hashable { case id case name case token + case firstMonthOfYear = "first_month_of_year" case accentColor = "accent_color" case currencyId = "currency_id" case currencies @@ -257,7 +261,7 @@ struct DashboardWidget_Previews: PreviewProvider { static var previews: some View { let entry = SimpleEntry(date: Date(), configuration: ConfigurationIntent(), - widgetData: WidgetData(url: "url", companyId: "", companies: [:]), + widgetData: WidgetData(url: "url", companyId: "", companies: [:], dateRanges: [:]), field: "Active Invoices", value: "$100.00")