macOS widgets
This commit is contained in:
parent
7b2fb20407
commit
7e1ae1f0d2
|
|
@ -1406,11 +1406,17 @@ void _showAbout(BuildContext context) async {
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
await UserDefaults.setString(
|
await UserDefaults.setString(
|
||||||
'widgetData',
|
'widget_data',
|
||||||
jsonEncode(WidgetData(
|
jsonEncode(WidgetData(
|
||||||
url: formatApiUrl(state.authState.url),
|
url: formatApiUrl(state.authState.url),
|
||||||
tokens: state.apiTokens,
|
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');
|
'group.com.invoiceninja.app');
|
||||||
await WidgetKit.reloadAllTimelines();
|
await WidgetKit.reloadAllTimelines();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,13 @@ import 'dart:convert';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:invoiceninja_flutter/constants.dart';
|
import 'package:invoiceninja_flutter/constants.dart';
|
||||||
|
import 'package:invoiceninja_flutter/data/models/company_model.dart';
|
||||||
import 'package:invoiceninja_flutter/main_app.dart';
|
import 'package:invoiceninja_flutter/main_app.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||||
|
import 'package:invoiceninja_flutter/redux/company/company_selectors.dart';
|
||||||
|
import 'package:invoiceninja_flutter/redux/company/company_state.dart';
|
||||||
|
import 'package:invoiceninja_flutter/redux/static/static_state.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/platforms.dart';
|
import 'package:invoiceninja_flutter/utils/platforms.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:widget_kit_plugin/user_defaults/user_defaults.dart';
|
import 'package:widget_kit_plugin/user_defaults/user_defaults.dart';
|
||||||
|
|
@ -91,17 +95,90 @@ class _WindowManagerState extends State<WindowManager> with WindowListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
class WidgetData {
|
class WidgetData {
|
||||||
WidgetData({this.url, this.tokens});
|
WidgetData({this.url, this.companies});
|
||||||
|
|
||||||
WidgetData.fromJson(Map<String, dynamic> json)
|
WidgetData.fromJson(Map<String, dynamic> json)
|
||||||
: url = json['url'],
|
: url = json['url'],
|
||||||
tokens = json['tokens'];
|
companies = json['companies'];
|
||||||
|
|
||||||
final String url;
|
|
||||||
final Map<String, String> tokens;
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() => <String, dynamic>{
|
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||||
'tokens': tokens,
|
'companies': companies,
|
||||||
'url': url,
|
'url': url,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
final String url;
|
||||||
|
final Map<String, WidgetCompany> companies;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WidgetCompany {
|
||||||
|
WidgetCompany(
|
||||||
|
{this.id,
|
||||||
|
this.name,
|
||||||
|
this.token,
|
||||||
|
this.accentColor,
|
||||||
|
this.currencyId,
|
||||||
|
this.currencies});
|
||||||
|
|
||||||
|
WidgetCompany.fromUserCompany(
|
||||||
|
{UserCompanyState userCompanyState, StaticState staticState})
|
||||||
|
: id = userCompanyState.userCompany.company.id,
|
||||||
|
name = userCompanyState.userCompany.company.displayName,
|
||||||
|
token = userCompanyState.userCompany.token.token,
|
||||||
|
accentColor = userCompanyState.userCompany.settings.accentColor,
|
||||||
|
currencyId = userCompanyState.userCompany.company.currencyId,
|
||||||
|
currencies = {
|
||||||
|
for (var currencyId in getCurrencyIds(
|
||||||
|
userCompanyState.userCompany.company,
|
||||||
|
userCompanyState.clientState.map,
|
||||||
|
userCompanyState.groupState.map,
|
||||||
|
).where((currencyId) => currencyId != kCurrencyAll))
|
||||||
|
currencyId: WidgetCurrency(
|
||||||
|
id: currencyId,
|
||||||
|
name: staticState.currencyMap[currencyId].name,
|
||||||
|
exchangeRate: staticState.currencyMap[currencyId].exchangeRate,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
WidgetCompany.fromJson(Map<String, dynamic> json)
|
||||||
|
: id = json['id'],
|
||||||
|
name = json['name'],
|
||||||
|
token = json['token'],
|
||||||
|
accentColor = json['accent_color'],
|
||||||
|
currencies = json['currencies'],
|
||||||
|
currencyId = json['currency_id'];
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||||
|
'id': id,
|
||||||
|
'name': name,
|
||||||
|
'token': token,
|
||||||
|
'accent_color': accentColor,
|
||||||
|
'currencies': currencies,
|
||||||
|
'currency_id': currencyId,
|
||||||
|
};
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
final String name;
|
||||||
|
final String token;
|
||||||
|
final String accentColor;
|
||||||
|
final String currencyId;
|
||||||
|
final Map<String, WidgetCurrency> currencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
class WidgetCurrency {
|
||||||
|
WidgetCurrency({this.id, this.name, this.exchangeRate});
|
||||||
|
|
||||||
|
WidgetCurrency.fromJson(Map<String, dynamic> json)
|
||||||
|
: id = json['id'],
|
||||||
|
name = json['name'],
|
||||||
|
exchangeRate = json['exchange_rate'];
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => <String, dynamic>{
|
||||||
|
'id': id,
|
||||||
|
'name': name,
|
||||||
|
'exchange_rate': exchangeRate,
|
||||||
|
};
|
||||||
|
|
||||||
|
final String id;
|
||||||
|
final String name;
|
||||||
|
final double exchangeRate;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,14 @@ class IntentHandler: INExtension, ConfigurationIntentHandling {
|
||||||
func provideCompanyOptionsCollection(for intent: ConfigurationIntent) async throws -> INObjectCollection<Company> {
|
func provideCompanyOptionsCollection(for intent: ConfigurationIntent) async throws -> INObjectCollection<Company> {
|
||||||
|
|
||||||
let sharedDefaults = UserDefaults.init(suiteName: "group.com.invoiceninja.app")
|
let sharedDefaults = UserDefaults.init(suiteName: "group.com.invoiceninja.app")
|
||||||
var exampleData: WidgetData = WidgetData(url: "", tokens:[:])
|
var exampleData: WidgetData = WidgetData(url: "", companies: [:])
|
||||||
|
|
||||||
if sharedDefaults != nil {
|
if sharedDefaults != nil {
|
||||||
do {
|
do {
|
||||||
let shared = sharedDefaults!.string(forKey: "widgetData")
|
let shared = sharedDefaults!.string(forKey: "widget_data")
|
||||||
|
|
||||||
|
print("## Shared: \(shared!)")
|
||||||
|
|
||||||
if shared != nil {
|
if shared != nil {
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
exampleData = try decoder.decode(WidgetData.self, from: shared!.data(using: .utf8)!)
|
exampleData = try decoder.decode(WidgetData.self, from: shared!.data(using: .utf8)!)
|
||||||
|
|
@ -26,11 +29,11 @@ class IntentHandler: INExtension, ConfigurationIntentHandling {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let companies = exampleData.tokens.keys.map { token in
|
let companies = exampleData.companies.values.map { company in
|
||||||
|
|
||||||
let company = Company(
|
let company = Company(
|
||||||
identifier: token,
|
identifier: company.token,
|
||||||
display: exampleData.tokens[token] ?? ""
|
display: company.name
|
||||||
)
|
)
|
||||||
//company.symbol = asset.symbol
|
//company.symbol = asset.symbol
|
||||||
//company.name = asset.name
|
//company.name = asset.name
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,11 @@ import Intents
|
||||||
|
|
||||||
struct Provider: IntentTimelineProvider {
|
struct Provider: IntentTimelineProvider {
|
||||||
func placeholder(in context: Context) -> SimpleEntry {
|
func placeholder(in context: Context) -> SimpleEntry {
|
||||||
SimpleEntry(date: Date(), configuration: ConfigurationIntent(), widgetData: WidgetData(url: "url", tokens: ["plk": "ply"]), field: "Invoices", value: 0)
|
SimpleEntry(date: Date(), configuration: ConfigurationIntent(), widgetData: WidgetData(url: "url", companies: [:]), field: "Invoices", value: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||||
let entry = SimpleEntry(date: Date(), configuration: configuration, widgetData: WidgetData(url: "url", tokens: ["sk": "sy"]), field: "Invoices", value: 0)
|
let entry = SimpleEntry(date: Date(), configuration: configuration, widgetData: WidgetData(url: "url", companies: [:]), field: "Invoices", value: 0)
|
||||||
completion(entry)
|
completion(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,10 +35,10 @@ struct Provider: IntentTimelineProvider {
|
||||||
|
|
||||||
if sharedDefaults != nil {
|
if sharedDefaults != nil {
|
||||||
do {
|
do {
|
||||||
let shared = sharedDefaults!.string(forKey: "widgetData")
|
let shared = sharedDefaults!.string(forKey: "widget_data")
|
||||||
if shared != nil {
|
if shared != nil {
|
||||||
|
|
||||||
//print("## Shared: \(shared!)")
|
print("## Shared: \(shared!)")
|
||||||
|
|
||||||
let decoder = JSONDecoder()
|
let decoder = JSONDecoder()
|
||||||
exampleData = try decoder.decode(WidgetData.self, from: shared!.data(using: .utf8)!)
|
exampleData = try decoder.decode(WidgetData.self, from: shared!.data(using: .utf8)!)
|
||||||
|
|
@ -48,8 +48,9 @@ struct Provider: IntentTimelineProvider {
|
||||||
let url = (exampleData?.url ?? "") + "/charts/totals_v2";
|
let url = (exampleData?.url ?? "") + "/charts/totals_v2";
|
||||||
var token = configuration.company?.identifier ?? ""
|
var token = configuration.company?.identifier ?? ""
|
||||||
|
|
||||||
if (token == "") {
|
if (token == "" && !(exampleData?.companies.isEmpty)!) {
|
||||||
token = exampleData?.tokens.keys.first ?? "";
|
let company = exampleData?.companies.values.first;
|
||||||
|
token = company?.token ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
print("## company.name: \(configuration.company?.displayString ?? "")")
|
print("## company.name: \(configuration.company?.displayString ?? "")")
|
||||||
|
|
@ -95,7 +96,37 @@ struct Provider: IntentTimelineProvider {
|
||||||
|
|
||||||
struct WidgetData: Decodable, Hashable {
|
struct WidgetData: Decodable, Hashable {
|
||||||
let url: String
|
let url: String
|
||||||
let tokens: [String: String]
|
let companies: [String: WidgetCompany]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WidgetCompany: Decodable, Hashable {
|
||||||
|
let id: String
|
||||||
|
let name: String
|
||||||
|
let token: String
|
||||||
|
let accentColor: String
|
||||||
|
let currencyId: String
|
||||||
|
let currencies: [String: WidgetCurrency]
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case name
|
||||||
|
case token
|
||||||
|
case accentColor = "accent_color"
|
||||||
|
case currencyId = "currency_id"
|
||||||
|
case currencies
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct WidgetCurrency: Decodable, Hashable {
|
||||||
|
let id: String
|
||||||
|
let name: String
|
||||||
|
let exchangeRate: Double
|
||||||
|
|
||||||
|
enum CodingKeys: String, CodingKey {
|
||||||
|
case id
|
||||||
|
case name
|
||||||
|
case exchangeRate = "exchange_rate"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SimpleEntry: TimelineEntry {
|
struct SimpleEntry: TimelineEntry {
|
||||||
|
|
@ -154,7 +185,7 @@ struct DashboardWidget: Widget {
|
||||||
|
|
||||||
struct DashboardWidget_Previews: PreviewProvider {
|
struct DashboardWidget_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
DashboardWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent(), widgetData: WidgetData(url: "url", tokens: ["pk": "py"]), field: "Invoices", value: 0))
|
DashboardWidgetEntryView(entry: SimpleEntry(date: Date(), configuration: ConfigurationIntent(), widgetData: WidgetData(url: "url", companies: [:]), field: "Invoices", value: 0))
|
||||||
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
.previewContext(WidgetPreviewContext(family: .systemSmall))
|
||||||
//.environment(\.sizeCategory, .extraLarge)
|
//.environment(\.sizeCategory, .extraLarge)
|
||||||
//.environment(\.colorScheme, .dark)
|
//.environment(\.colorScheme, .dark)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue