Browse Source

refactor(neon,neon_files,neon_news,neon_notes,neon_notifications,app): Use global instance of NeonPlatform

Signed-off-by: jld3103 <jld3103yt@gmail.com>
pull/495/head
jld3103 1 year ago
parent
commit
7253352a9f
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 10
      packages/app/lib/apps.dart
  2. 13
      packages/neon/neon/lib/neon.dart
  3. 22
      packages/neon/neon/lib/src/app.dart
  4. 4
      packages/neon/neon/lib/src/blocs/accounts.dart
  5. 4
      packages/neon/neon/lib/src/blocs/push_notifications.dart
  6. 12
      packages/neon/neon/lib/src/blocs/user_statuses.dart
  7. 3
      packages/neon/neon/lib/src/models/app_implementation.dart
  8. 5
      packages/neon/neon/lib/src/models/notifications_interface.dart
  9. 4
      packages/neon/neon/lib/src/pages/login.dart
  10. 392
      packages/neon/neon/lib/src/pages/settings.dart
  11. 46
      packages/neon/neon/lib/src/platform/platform.dart
  12. 3
      packages/neon/neon/lib/src/utils/global_popups.dart
  13. 9
      packages/neon/neon/lib/src/utils/request_manager.dart
  14. 6
      packages/neon/neon_files/lib/blocs/files.dart
  15. 2
      packages/neon/neon_files/lib/dialogs/choose_create.dart
  16. 3
      packages/neon/neon_files/lib/neon_files.dart
  17. 4
      packages/neon/neon_news/lib/neon_news.dart
  18. 6
      packages/neon/neon_news/lib/options.dart
  19. 2
      packages/neon/neon_news/lib/widgets/articles_view.dart
  20. 2
      packages/neon/neon_notes/lib/neon_notes.dart
  21. 2
      packages/neon/neon_notifications/lib/neon_notifications.dart

10
packages/app/lib/apps.dart

@ -1,5 +1,4 @@
import 'package:neon/models.dart';
import 'package:neon/platform.dart';
import 'package:neon_files/neon_files.dart';
import 'package:neon_news/neon_news.dart';
import 'package:neon_notes/neon_notes.dart';
@ -8,11 +7,10 @@ import 'package:shared_preferences/shared_preferences.dart';
List<AppImplementation> getAppImplementations(
final SharedPreferences sharedPreferences,
final NeonPlatform platform,
) =>
[
FilesApp(sharedPreferences, platform),
NewsApp(sharedPreferences, platform),
NotesApp(sharedPreferences, platform),
NotificationsApp(sharedPreferences, platform),
FilesApp(sharedPreferences),
NewsApp(sharedPreferences),
NotesApp(sharedPreferences),
NotificationsApp(sharedPreferences),
];

13
packages/neon/neon/lib/neon.dart

@ -9,7 +9,6 @@ import 'package:neon/src/blocs/next_push.dart';
import 'package:neon/src/blocs/push_notifications.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/app_implementation.dart';
import 'package:neon/src/platform/platform.dart';
import 'package:neon/src/theme/neon.dart';
import 'package:neon/src/utils/global_options.dart';
import 'package:neon/src/utils/request_manager.dart';
@ -19,7 +18,7 @@ import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future runNeon({
required final Iterable<AppImplementation> Function(SharedPreferences, NeonPlatform) getAppImplementations,
required final Iterable<AppImplementation> Function(SharedPreferences) getAppImplementations,
required final NeonTheme theme,
@visibleForTesting final WidgetsBinding? bindingOverride,
@visibleForTesting final Account? account,
@ -31,9 +30,8 @@ Future runNeon({
final sharedPreferences = await SharedPreferences.getInstance();
final platform = await getNeonPlatform();
await RequestManager.instance.initCache(platform);
final allAppImplementations = getAppImplementations(sharedPreferences, platform);
await RequestManager.instance.initCache();
final allAppImplementations = getAppImplementations(sharedPreferences);
final packageInfo = await PackageInfo.fromPlatform();
buildUserAgent(packageInfo);
@ -44,7 +42,6 @@ Future runNeon({
);
final accountsBloc = AccountsBloc(
platform,
sharedPreferences,
globalOptions,
allAppImplementations,
@ -58,7 +55,6 @@ Future runNeon({
accountsBloc,
sharedPreferences,
globalOptions,
platform,
);
final firstLaunchBloc = FirstLaunchBloc(
sharedPreferences,
@ -76,9 +72,6 @@ Future runNeon({
Provider<SharedPreferences>(
create: (final _) => sharedPreferences,
),
Provider<NeonPlatform>(
create: (final _) => platform,
),
Provider<GlobalOptions>(
create: (final _) => globalOptions,
),

22
packages/neon/neon/lib/src/app.dart

@ -44,7 +44,6 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
final _appRegex = RegExp(r'^app_([a-z]+)$', multiLine: true);
final _navigatorKey = GlobalKey<NavigatorState>();
late final Iterable<AppImplementation> _appImplementations;
late final NeonPlatform _platform;
late final GlobalOptions _globalOptions;
late final AccountsBloc _accountsBloc;
late final _routerDelegate = AppRouter(
@ -59,15 +58,14 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
super.initState();
_appImplementations = Provider.of<Iterable<AppImplementation>>(context, listen: false);
_platform = Provider.of<NeonPlatform>(context, listen: false);
_globalOptions = Provider.of<GlobalOptions>(context, listen: false);
_accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
WidgetsBinding.instance.addObserver(this);
if (_platform.canUseSystemTray) {
if (NeonPlatform.instance.canUseSystemTray) {
tray.trayManager.addListener(this);
}
if (_platform.canUseWindowManager) {
if (NeonPlatform.instance.canUseWindowManager) {
windowManager.addListener(this);
}
@ -77,7 +75,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
if (!mounted) {
return;
}
if (_platform.canUseQuickActions) {
if (NeonPlatform.instance.canUseQuickActions) {
const quickActions = QuickActions();
await quickActions.setShortcutItems(
_appImplementations
@ -93,7 +91,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
await quickActions.initialize(_handleShortcut);
}
if (_platform.canUseWindowManager) {
if (NeonPlatform.instance.canUseWindowManager) {
await windowManager.setPreventClose(true);
if (_globalOptions.startupMinimized.value) {
@ -101,7 +99,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
}
}
if (_platform.canUseSystemTray) {
if (NeonPlatform.instance.canUseSystemTray) {
_globalOptions.systemTrayEnabled.addListener(() async {
if (_globalOptions.systemTrayEnabled.value) {
// TODO: This works on Linux, but maybe not on macOS or Windows
@ -136,7 +134,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
});
}
if (_platform.canUsePushNotifications) {
if (NeonPlatform.instance.canUsePushNotifications) {
final localNotificationsPlugin = await PushUtils.initLocalNotifications();
Global.onPushNotificationReceived = (final accountID) async {
final account = _accountsBloc.accounts.value.tryFind(accountID);
@ -213,7 +211,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
Future _handleShortcut(final String shortcutType) async {
if (shortcutType == 'show_hide') {
if (_platform.canUseWindowManager) {
if (NeonPlatform.instance.canUseWindowManager) {
if (await windowManager.isVisible()) {
await _saveAndMinimizeWindow();
} else {
@ -249,7 +247,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
}
Future _showAndRestoreWindow() async {
if (!_platform.canUseWindowManager) {
if (!NeonPlatform.instance.canUseWindowManager) {
return;
}
@ -264,10 +262,10 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
if (_platform.canUseSystemTray) {
if (NeonPlatform.instance.canUseSystemTray) {
tray.trayManager.removeListener(this);
}
if (_platform.canUseWindowManager) {
if (NeonPlatform.instance.canUseWindowManager) {
windowManager.removeListener(this);
}

4
packages/neon/neon/lib/src/blocs/accounts.dart

@ -10,7 +10,6 @@ import 'package:neon/src/blocs/user_details.dart';
import 'package:neon/src/blocs/user_statuses.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/app_implementation.dart';
import 'package:neon/src/platform/platform.dart';
import 'package:neon/src/settings/models/storage.dart';
import 'package:neon/src/utils/account_options.dart';
import 'package:neon/src/utils/global_options.dart';
@ -57,7 +56,6 @@ abstract interface class AccountsBlocStates {
class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocStates {
AccountsBloc(
this._platform,
this._sharedPreferences,
this._globalOptions,
this._allAppImplementations,
@ -98,7 +96,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
}
}
final NeonPlatform _platform;
late final AppStorage _storage = AppStorage('accounts', _sharedPreferences);
final SharedPreferences _sharedPreferences;
final GlobalOptions _globalOptions;
@ -287,7 +284,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
}
return _userStatusesBlocs[account.id] = UserStatusesBloc(
_platform,
account,
);
}

4
packages/neon/neon/lib/src/blocs/push_notifications.dart

@ -28,9 +28,8 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents,
this._accountsBloc,
this._sharedPreferences,
this._globalOptions,
this._platform,
) {
if (_platform.canUsePushNotifications) {
if (NeonPlatform.instance.canUsePushNotifications) {
unawaited(UnifiedPush.getDistributors().then(_globalOptions.updateDistributors));
_globalOptions.pushNotificationsEnabled.addListener(_pushNotificationsEnabledListener);
@ -40,7 +39,6 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents,
}
final AccountsBloc _accountsBloc;
final NeonPlatform _platform;
final SharedPreferences _sharedPreferences;
late final _storage = AppStorage(AppIDs.notifications, _sharedPreferences);
final GlobalOptions _globalOptions;

12
packages/neon/neon/lib/src/blocs/user_statuses.dart

@ -2,11 +2,11 @@ import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import 'package:neon/platform.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/blocs/timer.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/platform/platform.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
import 'package:window_manager/window_manager.dart';
@ -22,14 +22,12 @@ abstract class UserStatusesBlocStates {
@internal
class UserStatusesBloc extends InteractiveBloc implements UserStatusesBlocEvents, UserStatusesBlocStates {
UserStatusesBloc(
this._platform,
this._account,
) {
unawaited(refresh());
_timer = TimerBloc().registerTimer(const Duration(minutes: 5), refresh);
}
final NeonPlatform _platform;
final Account _account;
late final NeonTimer _timer;
@ -63,8 +61,12 @@ class UserStatusesBloc extends InteractiveBloc implements UserStatusesBlocEvents
UserStatusPublic? data;
if (_account.username == username) {
final isAway =
_platform.canUseWindowManager && (!(await windowManager.isFocused()) || !(await windowManager.isVisible()));
var isAway = false;
if (NeonPlatform.instance.canUseWindowManager) {
final focused = await windowManager.isFocused();
final visible = await windowManager.isFocused();
isAway = !focused || !visible;
}
try {
final response = await _account.client.userStatus.heartbeat.heartbeat(
status: isAway ? 'away' : 'online',

3
packages/neon/neon/lib/src/models/app_implementation.dart

@ -6,7 +6,6 @@ import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/platform/platform.dart';
import 'package:neon/src/settings/models/nextcloud_app_options.dart';
import 'package:neon/src/settings/models/storage.dart';
import 'package:neon/src/widgets/drawer_destination.dart';
@ -17,7 +16,6 @@ import 'package:shared_preferences/shared_preferences.dart';
abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions> {
AppImplementation(
final SharedPreferences sharedPreferences,
this.platform,
) {
final storage = AppStorage('app-$id', sharedPreferences);
options = buildOptions(storage);
@ -26,7 +24,6 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
String get id;
LocalizationsDelegate get localizationsDelegate;
List<Locale> get supportedLocales;
final NeonPlatform platform;
String nameFromLocalization(final AppLocalizations localizations) => localizations.appImplementationName(id);
String name(final BuildContext context) => nameFromLocalization(AppLocalizations.of(context));

5
packages/neon/neon/lib/src/models/notifications_interface.dart

@ -4,10 +4,7 @@ import 'package:neon/src/settings/models/nextcloud_app_options.dart';
abstract interface class NotificationsAppInterface<T extends NotificationsBlocInterface,
R extends NotificationsOptionsInterface> extends AppImplementation<T, R> {
NotificationsAppInterface(
super.sharedPreferences,
super.platform,
);
NotificationsAppInterface(super.sharedPreferences);
}
abstract interface class NotificationsBlocInterface extends InteractiveBloc {

4
packages/neon/neon/lib/src/pages/login.dart

@ -6,7 +6,6 @@ import 'package:neon/src/theme/branding.dart';
import 'package:neon/src/theme/dialog.dart';
import 'package:neon/src/utils/validators.dart';
import 'package:neon/src/widgets/nextcloud_logo.dart';
import 'package:provider/provider.dart';
class LoginPage extends StatefulWidget {
const LoginPage({
@ -40,7 +39,6 @@ class _LoginPageState extends State<LoginPage> {
@override
Widget build(final BuildContext context) {
final branding = Branding.of(context);
final platform = Provider.of<NeonPlatform>(context, listen: false);
return Scaffold(
resizeToAvoidBottomInset: true,
@ -101,7 +99,7 @@ class _LoginPageState extends State<LoginPage> {
onFieldSubmitted: login,
),
),
if (platform.canUseCamera) ...[
if (NeonPlatform.instance.canUseCamera) ...[
const SizedBox(
height: 50,
),

392
packages/neon/neon/lib/src/pages/settings.dart

@ -86,236 +86,234 @@ class _SettingsPageState extends State<SettingsPage> {
builder: (
final context,
final accountsSnapshot,
) {
final platform = Provider.of<NeonPlatform>(context, listen: false);
return ValueListenableBuilder<bool>(
valueListenable: globalOptions.pushNotificationsEnabled,
builder: (
final context,
final _,
final __,
) =>
SettingsList(
initialCategory: widget.initialCategory?.name,
categories: [
) =>
ValueListenableBuilder<bool>(
valueListenable: globalOptions.pushNotificationsEnabled,
builder: (
final context,
final _,
final __,
) =>
SettingsList(
initialCategory: widget.initialCategory?.name,
categories: [
SettingsCategory(
title: Text(AppLocalizations.of(context).settingsApps),
key: ValueKey(SettingsCageories.apps.name),
tiles: <SettingsTile>[
for (final appImplementation in appImplementations) ...[
if (appImplementation.options.options.isNotEmpty) ...[
CustomSettingsTile(
leading: appImplementation.buildIcon(),
title: Text(appImplementation.name(context)),
onTap: () {
NextcloudAppSettingsRoute(appid: appImplementation.id).go(context);
},
),
],
],
],
),
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryTheme),
key: ValueKey(SettingsCageories.theme.name),
tiles: [
DropdownButtonSettingsTile(
option: globalOptions.themeMode,
),
CheckBoxSettingsTile(
option: globalOptions.themeOLEDAsDark,
),
CheckBoxSettingsTile(
option: globalOptions.themeKeepOriginalAccentColor,
),
],
),
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryNavigation),
key: ValueKey(SettingsCageories.navigation.name),
tiles: [
DropdownButtonSettingsTile(
option: globalOptions.navigationMode,
),
],
),
if (NeonPlatform.instance.canUsePushNotifications) ...[
SettingsCategory(
title: Text(AppLocalizations.of(context).settingsApps),
key: ValueKey(SettingsCageories.apps.name),
tiles: <SettingsTile>[
for (final appImplementation in appImplementations) ...[
if (appImplementation.options.options.isNotEmpty) ...[
CustomSettingsTile(
leading: appImplementation.buildIcon(),
title: Text(appImplementation.name(context)),
onTap: () {
NextcloudAppSettingsRoute(appid: appImplementation.id).go(context);
},
title: Text(AppLocalizations.of(context).optionsCategoryPushNotifications),
key: ValueKey(SettingsCageories.pushNotifications.name),
tiles: [
if (!globalOptions.pushNotificationsEnabled.enabled) ...[
TextSettingsTile(
text: AppLocalizations.of(context).globalOptionsPushNotificationsEnabledDisabledNotice,
style: TextStyle(
fontWeight: FontWeight.w600,
fontStyle: FontStyle.italic,
color: Theme.of(context).colorScheme.error,
),
],
),
],
CheckBoxSettingsTile(
option: globalOptions.pushNotificationsEnabled,
),
DropdownButtonSettingsTile(
option: globalOptions.pushNotificationsDistributor,
),
],
),
],
if (NeonPlatform.instance.canUseWindowManager) ...[
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryTheme),
key: ValueKey(SettingsCageories.theme.name),
title: Text(AppLocalizations.of(context).optionsCategoryStartup),
key: ValueKey(SettingsCageories.startup.name),
tiles: [
DropdownButtonSettingsTile(
option: globalOptions.themeMode,
),
CheckBoxSettingsTile(
option: globalOptions.themeOLEDAsDark,
option: globalOptions.startupMinimized,
),
CheckBoxSettingsTile(
option: globalOptions.themeKeepOriginalAccentColor,
option: globalOptions.startupMinimizeInsteadOfExit,
),
],
),
],
if (NeonPlatform.instance.canUseWindowManager && NeonPlatform.instance.canUseSystemTray) ...[
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryNavigation),
key: ValueKey(SettingsCageories.navigation.name),
title: Text(AppLocalizations.of(context).optionsCategorySystemTray),
key: ValueKey(SettingsCageories.systemTray.name),
tiles: [
DropdownButtonSettingsTile(
option: globalOptions.navigationMode,
CheckBoxSettingsTile(
option: globalOptions.systemTrayEnabled,
),
CheckBoxSettingsTile(
option: globalOptions.systemTrayHideToTrayWhenMinimized,
),
],
),
if (platform.canUsePushNotifications) ...[
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryPushNotifications),
key: ValueKey(SettingsCageories.pushNotifications.name),
tiles: [
if (!globalOptions.pushNotificationsEnabled.enabled) ...[
TextSettingsTile(
text: AppLocalizations.of(context).globalOptionsPushNotificationsEnabledDisabledNotice,
style: TextStyle(
fontWeight: FontWeight.w600,
fontStyle: FontStyle.italic,
color: Theme.of(context).colorScheme.error,
),
),
],
CheckBoxSettingsTile(
option: globalOptions.pushNotificationsEnabled,
),
DropdownButtonSettingsTile(
option: globalOptions.pushNotificationsDistributor,
),
],
),
],
if (platform.canUseWindowManager) ...[
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryStartup),
key: ValueKey(SettingsCageories.startup.name),
tiles: [
CheckBoxSettingsTile(
option: globalOptions.startupMinimized,
),
CheckBoxSettingsTile(
option: globalOptions.startupMinimizeInsteadOfExit,
),
],
),
],
if (platform.canUseWindowManager && platform.canUseSystemTray) ...[
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategorySystemTray),
key: ValueKey(SettingsCageories.systemTray.name),
tiles: [
CheckBoxSettingsTile(
option: globalOptions.systemTrayEnabled,
),
CheckBoxSettingsTile(
option: globalOptions.systemTrayHideToTrayWhenMinimized,
),
],
),
],
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryAccounts),
key: ValueKey(SettingsCageories.accounts.name),
tiles: [
if (accountsSnapshot.requireData.length > 1) ...[
CheckBoxSettingsTile(
option: globalOptions.rememberLastUsedAccount,
),
DropdownButtonSettingsTile(
option: globalOptions.initialAccount,
),
],
for (final account in accountsSnapshot.requireData) ...[
AccountSettingsTile(
account: account,
onTap: () {
AccountSettingsRoute(accountid: account.id).go(context);
},
),
],
CustomSettingsTile(
title: ElevatedButton.icon(
onPressed: () async => const LoginRoute().push(context),
icon: Icon(MdiIcons.accountPlus),
label: Text(AppLocalizations.of(context).globalOptionsAccountsAdd),
),
],
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryAccounts),
key: ValueKey(SettingsCageories.accounts.name),
tiles: [
if (accountsSnapshot.requireData.length > 1) ...[
CheckBoxSettingsTile(
option: globalOptions.rememberLastUsedAccount,
),
DropdownButtonSettingsTile(
option: globalOptions.initialAccount,
),
],
),
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryOther),
key: ValueKey(SettingsCageories.other.name),
tiles: <SettingsTile>[
CustomSettingsTile(
leading: Icon(
MdiIcons.scriptText,
color: Theme.of(context).colorScheme.primary,
),
title: Text(AppLocalizations.of(context).licenses),
onTap: () async {
final branding = Branding.of(context);
showLicensePage(
context: context,
applicationName: branding.name,
applicationIcon: branding.logo,
applicationLegalese: branding.legalese,
applicationVersion: Provider.of<PackageInfo>(context, listen: false).version,
);
for (final account in accountsSnapshot.requireData) ...[
AccountSettingsTile(
account: account,
onTap: () {
AccountSettingsRoute(accountid: account.id).go(context);
},
),
CustomSettingsTile(
leading: Icon(
MdiIcons.export,
color: Theme.of(context).colorScheme.primary,
),
title: Text(AppLocalizations.of(context).settingsExport),
onTap: () async {
final settingsExportHelper = _buildSettingsExportHelper(context);
],
CustomSettingsTile(
title: ElevatedButton.icon(
onPressed: () async => const LoginRoute().push(context),
icon: Icon(MdiIcons.accountPlus),
label: Text(AppLocalizations.of(context).globalOptionsAccountsAdd),
),
),
],
),
SettingsCategory(
title: Text(AppLocalizations.of(context).optionsCategoryOther),
key: ValueKey(SettingsCageories.other.name),
tiles: <SettingsTile>[
CustomSettingsTile(
leading: Icon(
MdiIcons.scriptText,
color: Theme.of(context).colorScheme.primary,
),
title: Text(AppLocalizations.of(context).licenses),
onTap: () async {
final branding = Branding.of(context);
showLicensePage(
context: context,
applicationName: branding.name,
applicationIcon: branding.logo,
applicationLegalese: branding.legalese,
applicationVersion: Provider.of<PackageInfo>(context, listen: false).version,
);
},
),
CustomSettingsTile(
leading: Icon(
MdiIcons.export,
color: Theme.of(context).colorScheme.primary,
),
title: Text(AppLocalizations.of(context).settingsExport),
onTap: () async {
final settingsExportHelper = _buildSettingsExportHelper(context);
try {
final fileName =
'nextcloud-neon-settings-${DateTime.now().millisecondsSinceEpoch ~/ 1000}.json.base64';
final data = base64.encode(
utf8.encode(
json.encode(
settingsExportHelper.toJsonExport(),
),
try {
final fileName =
'nextcloud-neon-settings-${DateTime.now().millisecondsSinceEpoch ~/ 1000}.json.base64';
final data = base64.encode(
utf8.encode(
json.encode(
settingsExportHelper.toJsonExport(),
),
);
await saveFileWithPickDialog(fileName, utf8.encode(data) as Uint8List);
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
if (mounted) {
NeonException.showSnackbar(context, e);
}
),
);
await saveFileWithPickDialog(fileName, utf8.encode(data) as Uint8List);
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
if (mounted) {
NeonException.showSnackbar(context, e);
}
},
}
},
),
CustomSettingsTile(
leading: Icon(
MdiIcons.import,
color: Theme.of(context).colorScheme.primary,
),
CustomSettingsTile(
leading: Icon(
MdiIcons.import,
color: Theme.of(context).colorScheme.primary,
),
title: Text(AppLocalizations.of(context).settingsImport),
onTap: () async {
final settingsExportHelper = _buildSettingsExportHelper(context);
title: Text(AppLocalizations.of(context).settingsImport),
onTap: () async {
final settingsExportHelper = _buildSettingsExportHelper(context);
try {
final result = await FilePicker.platform.pickFiles(
withData: true,
);
try {
final result = await FilePicker.platform.pickFiles(
withData: true,
);
if (result == null) {
return;
}
if (result == null) {
return;
}
if (!result.files.single.path!.endsWith('.json.base64')) {
if (mounted) {
NeonException.showSnackbar(
context,
AppLocalizations.of(context).settingsImportWrongFileExtension,
);
}
return;
if (!result.files.single.path!.endsWith('.json.base64')) {
if (mounted) {
NeonException.showSnackbar(
context,
AppLocalizations.of(context).settingsImportWrongFileExtension,
);
}
return;
}
final data = json.decode(utf8.decode(base64.decode(utf8.decode(result.files.single.bytes!))));
final data = json.decode(utf8.decode(base64.decode(utf8.decode(result.files.single.bytes!))));
await settingsExportHelper.applyFromJson(data as Map<String, dynamic>);
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
if (mounted) {
NeonException.showSnackbar(context, e);
}
await settingsExportHelper.applyFromJson(data as Map<String, dynamic>);
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
if (mounted) {
NeonException.showSnackbar(context, e);
}
},
),
],
),
],
),
);
},
}
},
),
],
),
],
),
),
);
return Scaffold(

46
packages/neon/neon/lib/src/platform/platform.dart

@ -5,22 +5,42 @@ import 'package:meta/meta.dart';
import 'package:neon/src/platform/android.dart';
import 'package:neon/src/platform/linux.dart';
Future<NeonPlatform> getNeonPlatform() async {
final NeonPlatform platform;
if (Platform.isAndroid) {
platform = const AndroidNeonPlatform();
} else if (Platform.isLinux) {
platform = const LinuxNeonPlatform();
} else {
throw UnimplementedError('No implementation for platform ${Platform.operatingSystem} found');
/// Implements platform specific functionality and exposes the availability of certain features.
@immutable
abstract interface class NeonPlatform {
@visibleForTesting
factory NeonPlatform.mocked(final NeonPlatform platform) => _platform = platform;
static NeonPlatform? _platform;
/// Infers and configures the platform automatically.
///
/// Required to be called before accessing [NeonPlatform.instance].
static Future setup() async {
if (Platform.isAndroid) {
_platform = const AndroidNeonPlatform();
} else if (Platform.isLinux) {
_platform = const LinuxNeonPlatform();
} else {
throw UnimplementedError('No implementation for platform ${Platform.operatingSystem} found');
}
await _platform!.init();
}
await platform.init.call();
return platform;
}
/// Gets the current instance of [NeonPlatform].
///
/// Make sure [NeonPlatform.setup] has been called before accessing the instance.
static NeonPlatform get instance {
if (_platform == null) {
throw StateError(
'NeonPlatform has not been set up yet. Please make sure NeonPlatform.setup() has been called before and completed.',
);
}
return _platform!;
}
@immutable
abstract interface class NeonPlatform {
abstract final bool canUseWebView;
abstract final bool canUseQuickActions;

3
packages/neon/neon/lib/src/utils/global_popups.dart

@ -45,10 +45,9 @@ class GlobalPopups {
final globalOptions = Provider.of<GlobalOptions>(context, listen: false);
final firstLaunchBloc = Provider.of<FirstLaunchBloc>(context, listen: false);
final nextPushBloc = Provider.of<NextPushBloc>(context, listen: false);
final platform = Provider.of<NeonPlatform>(context, listen: false);
_subscriptions.addAll([
if (platform.canUsePushNotifications) ...[
if (NeonPlatform.instance.canUsePushNotifications) ...[
firstLaunchBloc.onFirstLaunch.listen((final _) {
assert(context.mounted, 'Context should be mounted');
if (!globalOptions.pushNotificationsEnabled.enabled) {

9
packages/neon/neon/lib/src/utils/request_manager.dart

@ -22,8 +22,8 @@ class RequestManager {
// ignore: prefer_constructors_over_static_methods
static RequestManager get instance => _requestManager ??= RequestManager();
Future initCache(final NeonPlatform platform) async {
_cache = Cache(platform);
Future initCache() async {
_cache = Cache();
await _cache!.init();
}
@ -187,9 +187,6 @@ class RequestManager {
@internal
class Cache {
Cache(this._platform);
final NeonPlatform _platform;
Database? _database;
Future init() async {
@ -199,7 +196,7 @@ class Cache {
_database = await openDatabase(
p.join(
await _platform.getApplicationCachePath(),
await NeonPlatform.instance.getApplicationCachePath(),
'cache.db',
),
version: 1,

6
packages/neon/neon_files/lib/blocs/files.dart

@ -28,7 +28,6 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
FilesBloc(
this.options,
this.account,
this._platform,
) {
options.uploadQueueParallelism.addListener(_uploadParalelismListener);
options.downloadQueueParallelism.addListener(_downloadParalelismListener);
@ -36,7 +35,6 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
final FilesAppSpecificOptions options;
final Account account;
final NeonPlatform _platform;
late final browser = getNewFilesBrowserBloc();
final _uploadQueue = Queue();
@ -86,7 +84,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
() async {
final file = File(
p.join(
await _platform.getApplicationCachePath(),
await NeonPlatform.instance.getApplicationCachePath(),
'files',
etag.replaceAll('"', ''),
path.last,
@ -139,7 +137,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
() async {
final file = File(
p.join(
await _platform.getUserAccessibleAppDataPath(),
await NeonPlatform.instance.getUserAccessibleAppDataPath(),
account.humanReadableID,
'files',
path.join(Platform.pathSeparator),

2
packages/neon/neon_files/lib/dialogs/choose_create.dart

@ -77,7 +77,7 @@ class _FilesChooseCreateDialogState extends State<FilesChooseCreateDialog> {
}
},
),
if (Provider.of<NeonPlatform>(context, listen: false).canUseCamera) ...[
if (NeonPlatform.instance.canUseCamera) ...[
ListTile(
leading: Icon(
MdiIcons.cameraPlus,

3
packages/neon/neon_files/lib/neon_files.dart

@ -44,7 +44,7 @@ part 'widgets/browser_view.dart';
part 'widgets/file_preview.dart';
class FilesApp extends AppImplementation<FilesBloc, FilesAppSpecificOptions> {
FilesApp(super.sharedPreferences, super.platform);
FilesApp(super.sharedPreferences);
@override
String id = AppIDs.files;
@ -62,7 +62,6 @@ class FilesApp extends AppImplementation<FilesBloc, FilesAppSpecificOptions> {
FilesBloc buildBloc(final Account account) => FilesBloc(
options,
account,
platform,
);
@override

4
packages/neon/neon_news/lib/neon_news.dart

@ -52,7 +52,7 @@ part 'widgets/folder_view.dart';
part 'widgets/folders_view.dart';
class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
NewsApp(super.sharedPreferences, super.platform);
NewsApp(super.sharedPreferences);
@override
String id = AppIDs.news;
@ -64,7 +64,7 @@ class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
List<Locale> supportedLocales = AppLocalizations.supportedLocales;
@override
NewsAppSpecificOptions buildOptions(final AppStorage storage) => NewsAppSpecificOptions(storage, platform);
NewsAppSpecificOptions buildOptions(final AppStorage storage) => NewsAppSpecificOptions(storage);
@override
NewsBloc buildBloc(final Account account) => NewsBloc(

6
packages/neon/neon_news/lib/options.dart

@ -1,7 +1,7 @@
part of 'neon_news.dart';
class NewsAppSpecificOptions extends NextcloudAppOptions {
NewsAppSpecificOptions(super.storage, this._platform) {
NewsAppSpecificOptions(super.storage) {
super.categories = [
generalCategory,
articlesCategory,
@ -23,8 +23,6 @@ class NewsAppSpecificOptions extends NextcloudAppOptions {
];
}
final NeonPlatform _platform;
final generalCategory = OptionsCategory(
name: (final context) => AppLocalizations.of(context).general,
);
@ -62,7 +60,7 @@ class NewsAppSpecificOptions extends NextcloudAppOptions {
defaultValue: ArticleViewType.direct,
values: {
ArticleViewType.direct: (final context) => AppLocalizations.of(context).optionsArticleViewTypeDirect,
if (_platform.canUseWebView)
if (NeonPlatform.instance.canUseWebView)
ArticleViewType.internalBrowser: (final context) =>
AppLocalizations.of(context).optionsArticleViewTypeInternalBrowser,
ArticleViewType.externalBrowser: (final context) =>

2
packages/neon/neon_news/lib/widgets/articles_view.dart

@ -203,7 +203,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
);
} else if (viewType == ArticleViewType.internalBrowser &&
article.url != null &&
Provider.of<NeonPlatform>(context, listen: false).canUseWebView) {
NeonPlatform.instance.canUseWebView) {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (final context) => NewsArticlePage(

2
packages/neon/neon_notes/lib/neon_notes.dart

@ -42,7 +42,7 @@ part 'widgets/notes_floating_action_button.dart';
part 'widgets/notes_view.dart';
class NotesApp extends AppImplementation<NotesBloc, NotesAppSpecificOptions> {
NotesApp(super.sharedPreferences, super.platform);
NotesApp(super.sharedPreferences);
@override
String id = AppIDs.notes;

2
packages/neon/neon_notifications/lib/neon_notifications.dart

@ -22,7 +22,7 @@ part 'pages/main.dart';
class NotificationsApp extends AppImplementation<NotificationsBloc, NotificationsAppSpecificOptions>
implements NotificationsAppInterface<NotificationsBloc, NotificationsAppSpecificOptions> {
NotificationsApp(super.sharedPreferences, super.platform);
NotificationsApp(super.sharedPreferences);
@override
String id = AppIDs.notifications;

Loading…
Cancel
Save