macOS widgets

This commit is contained in:
Hillel Coren 2023-06-29 13:35:02 +03:00
parent d8aefa096e
commit 8f6e27cb77
4 changed files with 121 additions and 119 deletions

View File

@ -16,7 +16,7 @@ class WidgetData {
this.companies,
this.companyId,
this.dateRanges,
this.fields,
this.dashboardFields,
});
WidgetData.fromState(AppState state, AppLocalization localization)
@ -30,7 +30,7 @@ class WidgetData {
staticState: state.staticState,
)
},
fields = Map.fromIterable(<String>[
dashboardFields = Map.fromIterable(<String>[
DashboardUISettings.FIELD_ACTIVE_INVOICES,
DashboardUISettings.FIELD_OUTSTANDING_INVOICES,
DashboardUISettings.FIELD_COMPLETED_PAYMENTS,
@ -47,21 +47,21 @@ class WidgetData {
companyId = json['company_id'],
companies = json['companies'],
dateRanges = json['date_ranges'],
fields = json['fields'];
dashboardFields = json['dashboard_fields'];
Map<String, dynamic> toJson() => <String, dynamic>{
'companies': companies,
'company_id': companyId,
'url': url,
'date_ranges': dateRanges,
'fields': fields,
'dashboard_Fields': dashboardFields,
};
final String url;
final String companyId;
final Map<String, WidgetCompany> companies;
final Map<String, String> dateRanges;
final Map<String, String> fields;
final Map<String, String> dashboardFields;
}
class WidgetCompany {

View File

@ -3,7 +3,11 @@ 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: [:], dateRanges: [:], fields: [:])
var widgetData: WidgetData = WidgetData(url: "",
companyId: "",
companies: [:],
dateRanges: [:],
dashboardFields: [:])
if let sharedDefaults = sharedDefaults {
do {
@ -71,7 +75,24 @@ class IntentHandler: INExtension, ConfigurationIntentHandling {
return DateRange(identifier: defaultDateRange, display: dateRamge)
}
func provideDashboardFieldOptionsCollection(for intent: ConfigurationIntent) async throws -> INObjectCollection<DashboardField> {
let widgetData = loadWidgetData()
let fields = widgetData.dashboardFields.keys.sorted().map { field in
DashboardField(identifier: field, display: widgetData.dashboardFields[field]!)
}
return INObjectCollection(items: fields)
}
func defaultDashboardField(for intent: ConfigurationIntent) -> DashboardField? {
let widgetData = loadWidgetData()
let defaultField = "total_active_invoices";
let field = widgetData.dashboardFields[defaultField]!;
return DashboardField(identifier: defaultField, display: field)
}
override func handler(for intent: INIntent) -> Any {
return self
}

View File

@ -3,67 +3,13 @@
<plist version="1.0">
<dict>
<key>INEnums</key>
<array>
<dict>
<key>INEnumDisplayName</key>
<string>Field</string>
<key>INEnumDisplayNameID</key>
<string>a7k58J</string>
<key>INEnumGeneratesHeader</key>
<true/>
<key>INEnumName</key>
<string>Field</string>
<key>INEnumType</key>
<string>Regular</string>
<key>INEnumValues</key>
<array>
<dict>
<key>INEnumValueDisplayName</key>
<string>unknown</string>
<key>INEnumValueDisplayNameID</key>
<string>lwYuqc</string>
<key>INEnumValueName</key>
<string>unknown</string>
</dict>
<dict>
<key>INEnumValueDisplayName</key>
<string>Active Invoices</string>
<key>INEnumValueDisplayNameID</key>
<string>z5NmCf</string>
<key>INEnumValueIndex</key>
<integer>1</integer>
<key>INEnumValueName</key>
<string>active_invoices</string>
</dict>
<dict>
<key>INEnumValueDisplayName</key>
<string>Outstanding Invoices</string>
<key>INEnumValueDisplayNameID</key>
<string>KMErB0</string>
<key>INEnumValueIndex</key>
<integer>2</integer>
<key>INEnumValueName</key>
<string>outstanding_invoices</string>
</dict>
<dict>
<key>INEnumValueDisplayName</key>
<string>Completed Payments</string>
<key>INEnumValueDisplayNameID</key>
<string>HEngYv</string>
<key>INEnumValueIndex</key>
<integer>3</integer>
<key>INEnumValueName</key>
<string>completed_payments</string>
</dict>
</array>
</dict>
</array>
<array/>
<key>INIntentDefinitionModelVersion</key>
<string>1.2</string>
<key>INIntentDefinitionNamespace</key>
<string>88xZPY</string>
<key>INIntentDefinitionSystemVersion</key>
<string>21G115</string>
<string>21G651</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>14C18</string>
<key>INIntentDefinitionToolsVersion</key>
@ -80,7 +26,7 @@
<key>INIntentIneligibleForSuggestions</key>
<true/>
<key>INIntentLastParameterTag</key>
<integer>11</integer>
<integer>12</integer>
<key>INIntentName</key>
<string>Configuration</string>
<key>INIntentParameters</key>
@ -168,17 +114,12 @@
<string>t2MezO</string>
<key>INIntentParameterDisplayPriority</key>
<integer>3</integer>
<key>INIntentParameterEnumType</key>
<string>Field</string>
<key>INIntentParameterEnumTypeNamespace</key>
<string>88xZPY</string>
<key>INIntentParameterMetadata</key>
<dict>
<key>INIntentParameterMetadataDefaultValue</key>
<string>active_invoices</string>
</dict>
<key>INIntentParameterName</key>
<string>field</string>
<string>dashboardField</string>
<key>INIntentParameterObjectType</key>
<string>DashboardField</string>
<key>INIntentParameterObjectTypeNamespace</key>
<string>88xZPY</string>
<key>INIntentParameterPromptDialogs</key>
<array>
<dict>
@ -197,7 +138,7 @@
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>There are ${count} options matching ${field}.</string>
<string>There are ${count} options matching ${dashboardField}.</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>3UOOUA</string>
<key>INIntentParameterPromptDialogType</key>
@ -207,17 +148,19 @@
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>Just to confirm, you wanted ${field}?</string>
<string>Just to confirm, you wanted ${dashboardField}?</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>4sG08t</string>
<key>INIntentParameterPromptDialogType</key>
<string>Confirmation</string>
</dict>
</array>
<key>INIntentParameterSupportsDynamicEnumeration</key>
<true/>
<key>INIntentParameterTag</key>
<integer>2</integer>
<integer>12</integer>
<key>INIntentParameterType</key>
<string>Integer</string>
<string>Object</string>
</dict>
<dict>
<key>INIntentParameterConfigurable</key>
@ -248,26 +191,6 @@
<key>INIntentParameterPromptDialogType</key>
<string>Primary</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>There are ${count} options matching ${dateRange}.</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>M1c9EE</string>
<key>INIntentParameterPromptDialogType</key>
<string>DisambiguationIntroduction</string>
</dict>
<dict>
<key>INIntentParameterPromptDialogCustom</key>
<true/>
<key>INIntentParameterPromptDialogFormatString</key>
<string>Just to confirm, you wanted ${dateRange}?</string>
<key>INIntentParameterPromptDialogFormatStringID</key>
<string>YL2DLe</string>
<key>INIntentParameterPromptDialogType</key>
<string>Confirmation</string>
</dict>
</array>
<key>INIntentParameterSupportsDynamicEnumeration</key>
<true/>
@ -496,6 +419,69 @@
</dict>
</array>
</dict>
<dict>
<key>INTypeDisplayName</key>
<string>Field</string>
<key>INTypeDisplayNameID</key>
<string>VFh9Fr</string>
<key>INTypeLastPropertyTag</key>
<integer>99</integer>
<key>INTypeName</key>
<string>DashboardField</string>
<key>INTypeProperties</key>
<array>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>1</integer>
<key>INTypePropertyName</key>
<string>identifier</string>
<key>INTypePropertyTag</key>
<integer>1</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>2</integer>
<key>INTypePropertyName</key>
<string>displayString</string>
<key>INTypePropertyTag</key>
<integer>2</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>3</integer>
<key>INTypePropertyName</key>
<string>pronunciationHint</string>
<key>INTypePropertyTag</key>
<integer>3</integer>
<key>INTypePropertyType</key>
<string>String</string>
</dict>
<dict>
<key>INTypePropertyDefault</key>
<true/>
<key>INTypePropertyDisplayPriority</key>
<integer>4</integer>
<key>INTypePropertyName</key>
<string>alternativeSpeakableMatches</string>
<key>INTypePropertySupportsMultipleValues</key>
<true/>
<key>INTypePropertyTag</key>
<integer>4</integer>
<key>INTypePropertyType</key>
<string>SpeakableString</string>
</dict>
</array>
</dict>
</array>
</dict>
</plist>

View File

@ -65,7 +65,7 @@ struct Provider: IntentTimelineProvider {
do {
return try getWidgetData()
} catch {
return WidgetData(url: "url", companyId: "", companies: [:], dateRanges: [:], fields: [:])
return WidgetData(url: "url", companyId: "", companies: [:], dateRanges: [:], dashboardFields: [:])
}
}()
@ -74,7 +74,6 @@ struct Provider: IntentTimelineProvider {
SimpleEntry(date: Date(),
configuration: ConfigurationIntent(),
widgetData: widgetData,
field: "Active Invoices",
value: "$100.00",
error: "")
}
@ -86,7 +85,6 @@ struct Provider: IntentTimelineProvider {
let entry = SimpleEntry(date: Date(),
configuration: configuration,
widgetData: widgetData,
field: "Active Invoices",
value: "$100.00",
error: "")
@ -109,7 +107,7 @@ struct Provider: IntentTimelineProvider {
do {
widgetData = try getWidgetData()
(label, value) = try await getTimelineData(for: configuration, widgetData: widgetData!)
value = try await getTimelineData(for: configuration, widgetData: widgetData!)
print("## VALUE: \(value)")
@ -123,7 +121,6 @@ struct Provider: IntentTimelineProvider {
let entry = SimpleEntry(date: Date(),
configuration: configuration,
widgetData: widgetData,
field: label,
value: value,
error: message)
@ -141,11 +138,10 @@ struct Provider: IntentTimelineProvider {
}
}
func getTimelineData(for configuration: ConfigurationIntent, widgetData:WidgetData) async throws -> (String, String) {
func getTimelineData(for configuration: ConfigurationIntent, widgetData:WidgetData) async throws -> (String) {
var rawValue = 0.0
var value = "Error"
var label = ""
let companyId = configuration.company?.identifier ?? ""
let company = widgetData.companies[companyId]
@ -188,22 +184,19 @@ struct Provider: IntentTimelineProvider {
throw "Data not found"
}
switch configuration.field {
case .active_invoices:
switch configuration.dashboardField?.identifier {
case "total_active_invoices":
if let invoicedAmount = data?.invoices?.invoicedAmount, let value = Double(invoicedAmount) {
rawValue = value
}
label = "Active Invoices"
case .outstanding_invoices:
case "total_outstanding_invoices":
if let amount = data?.outstanding?.amount, let value = Double(amount) {
rawValue = value
}
label = "Outstanding Invoices"
case .completed_payments:
case "total_completed_payments":
if let paidToDate = data?.revenue?.paidToDate, let value = Double(paidToDate) {
rawValue = value
}
label = "Completed Payments"
default:
break
}
@ -214,7 +207,7 @@ struct Provider: IntentTimelineProvider {
formatter.currencyCode = currency?.code ?? "USD"
value = formatter.string(from: NSNumber(value: rawValue))!
return (label, value)
return value
}
@ -278,14 +271,14 @@ struct WidgetData: Decodable, Hashable {
let companyId: String
let companies: [String: WidgetCompany]
let dateRanges: [String: String]
let fields: [String: String]
let dashboardFields: [String: String]
enum CodingKeys: String, CodingKey {
case url
case companyId = "company_id"
case companies
case dateRanges = "date_ranges"
case fields
case dashboardFields = "dashboard_fields"
}
}
@ -327,7 +320,6 @@ struct SimpleEntry: TimelineEntry {
let date: Date
let configuration: ConfigurationIntent
let widgetData: WidgetData?
let field: String
let value: String
let error: String
}
@ -358,7 +350,7 @@ struct DashboardWidgetEntryView : View {
HStack {
VStack {
Text(entry.field)
Text(entry.configuration.dashboardField?.displayString ?? "")
.font(.body)
.bold()
.lineLimit(2)
@ -413,7 +405,11 @@ struct DashboardWidget_Previews: PreviewProvider {
do {
return try getWidgetData()
} catch {
return WidgetData(url: "url", companyId: "", companies: [:], dateRanges: [:], fields: [:])
return WidgetData(url: "url",
companyId: "",
companies: [:],
dateRanges: [:],
dashboardFields: [:])
}
}()
@ -421,7 +417,6 @@ struct DashboardWidget_Previews: PreviewProvider {
let entry = SimpleEntry(date: Date(),
configuration: ConfigurationIntent(),
widgetData: widgetData,
field: "Active Invoices",
value: "$100.00",
error: "")