280 lines
12 KiB
Dart
280 lines
12 KiB
Dart
// Dart imports:
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
// Flutter imports:
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
// Package imports:
|
|
import 'package:redux/redux.dart';
|
|
import 'package:redux_logging/redux_logging.dart';
|
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:invoiceninja_flutter/utils/platforms.dart';
|
|
// import 'package:bitsdojo_window/bitsdojo_window.dart';
|
|
|
|
// Project imports:
|
|
import 'package:invoiceninja_flutter/.env.dart';
|
|
import 'package:invoiceninja_flutter/constants.dart';
|
|
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
|
import 'package:invoiceninja_flutter/data/models/serializers.dart';
|
|
import 'package:invoiceninja_flutter/main_app.dart';
|
|
import 'package:invoiceninja_flutter/redux/app/app_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/app/app_reducer.dart';
|
|
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
|
import 'package:invoiceninja_flutter/redux/auth/auth_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/client/client_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/credit/credit_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/design/design_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/document/document_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/expense/expense_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/expense_category/expense_category_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/group/group_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/invoice/invoice_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/payment/payment_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/payment_term/payment_term_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/product/product_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/project/project_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/quote/quote_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/recurring_expense/recurring_expense_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/reports/reports_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/settings/settings_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/subscription/subscription_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/task/task_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/task_status/task_status_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/token/token_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/ui/pref_state.dart';
|
|
import 'package:invoiceninja_flutter/redux/user/user_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/vendor/vendor_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/webhook/webhook_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/transaction/transaction_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/bank_account/bank_account_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_middleware.dart';
|
|
import 'package:invoiceninja_flutter/redux/transaction_rule/transaction_rule_middleware.dart';
|
|
import 'package:window_manager/window_manager.dart';
|
|
import 'package:invoiceninja_flutter/redux/schedule/schedule_middleware.dart';
|
|
// STARTER: import - do not remove comment
|
|
|
|
import 'package:invoiceninja_flutter/utils/web_stub.dart'
|
|
if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';
|
|
|
|
// https://github.com/dart-lang/io/issues/83#issuecomment-940617222
|
|
const isrgRootX1 = '''-----BEGIN CERTIFICATE-----
|
|
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
|
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
|
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
|
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
|
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
|
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
|
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
|
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
|
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
|
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
|
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
|
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
|
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
|
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
|
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
|
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
|
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
|
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
|
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
|
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
|
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
|
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
|
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
|
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
|
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
|
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
|
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
|
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
|
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
|
-----END CERTIFICATE-----
|
|
''';
|
|
|
|
void main({bool isTesting = false}) async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
try {
|
|
SecurityContext.defaultContext.setTrustedCertificatesBytes(
|
|
Uint8List.fromList(isrgRootX1.codeUnits),
|
|
);
|
|
} catch (e) {
|
|
// Ignore CERT_ALREADY_IN_HASH_TABLE
|
|
}
|
|
|
|
if (isDesktopOS()) {
|
|
await windowManager.ensureInitialized();
|
|
|
|
final prefs = await SharedPreferences.getInstance();
|
|
windowManager.waitUntilReadyToShow(
|
|
WindowOptions(
|
|
center: true,
|
|
size: Size(
|
|
prefs.getDouble(kSharedPrefWidth) ?? 800,
|
|
prefs.getDouble(kSharedPrefHeight) ?? 600,
|
|
),
|
|
), () async {
|
|
await windowManager.show();
|
|
await windowManager.focus();
|
|
});
|
|
}
|
|
|
|
final store = Store<AppState>(appReducer,
|
|
initialState: await _initialState(isTesting),
|
|
middleware: []
|
|
..addAll(createStoreAuthMiddleware())
|
|
..addAll(createStoreDocumentsMiddleware())
|
|
..addAll(createStoreDashboardMiddleware())
|
|
..addAll(createStoreProductsMiddleware())
|
|
..addAll(createStoreClientsMiddleware())
|
|
..addAll(createStoreInvoicesMiddleware())
|
|
..addAll(createStoreExpensesMiddleware())
|
|
..addAll(createStoreVendorsMiddleware())
|
|
..addAll(createStoreTasksMiddleware())
|
|
..addAll(createStoreProjectsMiddleware())
|
|
..addAll(createStorePaymentsMiddleware())
|
|
..addAll(createStoreQuotesMiddleware())
|
|
..addAll(createStoreSettingsMiddleware())
|
|
..addAll(createStoreReportsMiddleware())
|
|
// STARTER: middleware - do not remove comment
|
|
..addAll(createStoreSchedulesMiddleware())
|
|
..addAll(createStoreTransactionRulesMiddleware())
|
|
..addAll(createStoreTransactionsMiddleware())
|
|
..addAll(createStoreBankAccountsMiddleware())
|
|
..addAll(createStorePurchaseOrdersMiddleware())
|
|
..addAll(createStoreRecurringExpensesMiddleware())
|
|
..addAll(createStoreSubscriptionsMiddleware())
|
|
..addAll(createStoreTaskStatusesMiddleware())
|
|
..addAll(createStoreExpenseCategoriesMiddleware())
|
|
..addAll(createStoreRecurringInvoicesMiddleware())
|
|
..addAll(createStoreWebhooksMiddleware())
|
|
..addAll(createStoreTokensMiddleware())
|
|
..addAll(createStorePaymentTermsMiddleware())
|
|
..addAll(createStoreDesignsMiddleware())
|
|
..addAll(createStoreCreditsMiddleware())
|
|
..addAll(createStoreUsersMiddleware())
|
|
..addAll(createStoreTaxRatesMiddleware())
|
|
..addAll(createStoreCompanyGatewaysMiddleware())
|
|
..addAll(createStoreGroupsMiddleware())
|
|
..addAll(createStorePersistenceMiddleware())
|
|
..addAll(isTesting || kReleaseMode || !Config.DEBUG_EVENTS
|
|
? []
|
|
: [
|
|
LoggingMiddleware<dynamic>.printer(
|
|
formatter: LoggingMiddleware.multiLineFormatter,
|
|
),
|
|
]));
|
|
|
|
if (!kReleaseMode) {
|
|
runApp(InvoiceNinjaApp(store: store));
|
|
} else {
|
|
await SentryFlutter.init(
|
|
(options) {
|
|
options.dsn = Config.SENTRY_DNS;
|
|
options.release = const String.fromEnvironment('SENTRY_RELEASE',
|
|
defaultValue: kClientVersion);
|
|
options.dist = kClientVersion;
|
|
options.beforeSend = (SentryEvent event, {dynamic hint}) {
|
|
final state = store.state;
|
|
final account = state.account;
|
|
final reportErrors = account?.reportErrors ?? false;
|
|
|
|
if (!reportErrors) {
|
|
return null;
|
|
}
|
|
|
|
event = event.copyWith(
|
|
environment: '${store.state.environment}'.split('.').last,
|
|
extra: <String, dynamic>{
|
|
'server_version': account?.currentVersion ?? 'Unknown',
|
|
'route': state.uiState.currentRoute,
|
|
},
|
|
);
|
|
|
|
return event;
|
|
};
|
|
},
|
|
appRunner: () => runApp(InvoiceNinjaApp(store: store)),
|
|
);
|
|
}
|
|
|
|
/*
|
|
if (isWindows()) {
|
|
doWhenWindowReady(() {
|
|
final win = appWindow;
|
|
win.title = 'Invoice Ninja';
|
|
win.show();
|
|
});
|
|
}
|
|
*/
|
|
}
|
|
|
|
Future<AppState> _initialState(bool isTesting) async {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final prefString = prefs?.getString(kSharedPrefs);
|
|
|
|
final url = WebUtils.apiUrl ?? prefs.getString(kSharedPrefUrl) ?? '';
|
|
if (!kReleaseMode) {
|
|
//url = kAppStagingUrl;
|
|
//url = kAppProductionUrl;
|
|
//url = kAppDemoUrl;
|
|
}
|
|
|
|
var prefState = PrefState();
|
|
if (prefString != null) {
|
|
try {
|
|
prefState = serializers.deserializeWith(
|
|
PrefState.serializer, json.decode(prefString));
|
|
} catch (e) {
|
|
print('## Error: Failed to load prefs: $e');
|
|
}
|
|
}
|
|
prefState = prefState.rebuild((b) => b
|
|
..enableDarkModeSystem =
|
|
WidgetsBinding.instance.window.platformBrightness == Brightness.dark);
|
|
|
|
String browserRoute;
|
|
if (kIsWeb && prefState.isDesktop) {
|
|
browserRoute = WebUtils.browserRoute;
|
|
if (browserRoute.isNotEmpty && browserRoute.length > 4) {
|
|
if (browserRoute == '/kanban') {
|
|
browserRoute = '/task';
|
|
prefState = prefState.rebuild((b) => b
|
|
..showKanban = true
|
|
..useSidebarEditor[EntityType.task] = true);
|
|
}
|
|
} else {
|
|
browserRoute = null;
|
|
}
|
|
}
|
|
|
|
bool reportErrors = false;
|
|
bool whiteLabeled = false;
|
|
String referralCode = '';
|
|
|
|
if (kIsWeb) {
|
|
reportErrors = WebUtils.getHtmlValue('report-errors') == '1';
|
|
whiteLabeled = WebUtils.getHtmlValue('white-label') == '1';
|
|
referralCode = WebUtils.getHtmlValue('rc');
|
|
if (reportErrors) {
|
|
print('## Error reporting is enabled');
|
|
}
|
|
}
|
|
|
|
return AppState(
|
|
prefState: prefState,
|
|
url: Config.DEMO_MODE ? '' : url,
|
|
referralCode: referralCode,
|
|
reportErrors: reportErrors,
|
|
isWhiteLabeled: whiteLabeled,
|
|
currentRoute: browserRoute,
|
|
);
|
|
}
|