Compare commits

...

3 Commits

Author SHA1 Message Date
Nikolas Rimikis ebc20a1a4c
refactor(neon,neon_files,neon_news,neon_notes,neon_notifications): deprecate AppImplementation.page 1 year ago
Nikolas Rimikis 23efce9d23
feat(neon): use go_router for apps 1 year ago
Nikolas Rimikis adab3c1722
feat(neon): give add relationship between AppsBloc and AppRouter 1 year ago
  1. 10
      packages/neon/neon/lib/src/app.dart
  2. 12
      packages/neon/neon/lib/src/blocs/accounts.dart
  3. 15
      packages/neon/neon/lib/src/blocs/apps.dart
  4. 19
      packages/neon/neon/lib/src/models/app_implementation.dart
  5. 21
      packages/neon/neon/lib/src/pages/home.dart
  6. 3
      packages/neon/neon/lib/src/pages/settings.dart
  7. 123
      packages/neon/neon/lib/src/router.dart
  8. 292
      packages/neon/neon/lib/src/router.g.dart
  9. 2
      packages/neon/neon/lib/src/widgets/account_switcher.dart
  10. 44
      packages/neon/neon/lib/src/widgets/app_bar.dart
  11. 3
      packages/neon/neon_files/lib/neon_files.dart
  12. 3
      packages/neon/neon_news/lib/neon_news.dart
  13. 3
      packages/neon/neon_notes/lib/neon_notes.dart
  14. 3
      packages/neon/neon_notifications/lib/neon_notifications.dart
  15. 44
      packages/neon/neon_notifications/lib/routes.dart

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

@ -42,15 +42,12 @@ class NeonApp extends StatefulWidget {
// ignore: prefer_mixin
class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.TrayListener, WindowListener {
final _appRegex = RegExp(r'^app_([a-z]+)$', multiLine: true);
final _navigatorKey = GlobalKey<NavigatorState>();
late final GlobalKey<NavigatorState> _navigatorKey;
late final Iterable<AppImplementation> _appImplementations;
late final NeonPlatform _platform;
late final GlobalOptions _globalOptions;
late final AccountsBloc _accountsBloc;
late final _routerDelegate = AppRouter(
navigatorKey: _navigatorKey,
accountsBloc: _accountsBloc,
);
late final AppRouter _routerDelegate;
Rect? _lastBounds;
@ -63,6 +60,9 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
_globalOptions = Provider.of<GlobalOptions>(context, listen: false);
_accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
_routerDelegate = _accountsBloc.router;
_navigatorKey = _routerDelegate.navigatorKey;
WidgetsBinding.instance.addObserver(this);
if (_platform.canUseSystemTray) {
tray.trayManager.addListener(this);

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

@ -10,6 +10,7 @@ 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/router.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';
@ -63,6 +64,10 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
this._globalOptions,
this._allAppImplementations,
) {
router = AppRouter(
accountsBloc: this,
appImplementations: _allAppImplementations,
);
accounts
..add(loadAccounts(_storage))
..listen((final as) async {
@ -105,6 +110,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
final SharedPreferences _sharedPreferences;
final GlobalOptions _globalOptions;
final Iterable<AppImplementation> _allAppImplementations;
late final AppRouter router;
final _keyLastUsedAccount = 'last-used-account';
final _accountsOptions = <String, AccountSpecificOptions>{};
@ -130,8 +136,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
BehaviorSubject<List<Account>> accounts = BehaviorSubject<List<Account>>.seeded([]);
@override
BehaviorSubject<Account?> activeAccount = BehaviorSubject<Account?>()
..distinct((final current, final next) => current?.id != next?.id);
BehaviorSubject<Account?> activeAccount = BehaviorSubject<Account?>();
@override
void addAccount(final Account account) {
@ -167,8 +172,10 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
@override
void setActiveAccount(final Account account) {
if (activeAccount.valueOrNull != account) {
activeAccount.add(account);
}
}
@override
void updateAccount(final Account account) {
@ -239,6 +246,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState
this,
account,
_allAppImplementations,
router,
);
}

15
packages/neon/neon/lib/src/blocs/apps.dart

@ -10,6 +10,7 @@ import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/app_ids.dart';
import 'package:neon/src/models/app_implementation.dart';
import 'package:neon/src/models/notifications_interface.dart';
import 'package:neon/src/router.dart';
import 'package:neon/src/settings/models/nextcloud_app_options.dart';
import 'package:neon/src/utils/request_manager.dart';
import 'package:nextcloud/nextcloud.dart';
@ -22,6 +23,9 @@ abstract class AppsBlocEvents {
/// If the app is already the active app nothing will happen.
/// When using [skipAlreadySet] nothing will be done if there already is an active app.
void setActiveApp(final String appID, {final bool skipAlreadySet = false});
/// Navigates to the active app.
void navigateActiveApp();
}
abstract class AppsBlocStates {
@ -46,6 +50,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
this._accountsBloc,
this._account,
this._allAppImplementations,
this._router,
) {
apps.listen((final result) {
appImplementations
@ -76,6 +81,10 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
unawaited(_checkCompatibility());
});
activeApp.distinct().listen((final app) {
_router.go(app.initialPath);
});
unawaited(refresh());
}
@ -154,6 +163,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
final AccountsBloc _accountsBloc;
final Account _account;
final Iterable<AppImplementation> _allAppImplementations;
final AppRouter _router;
@override
void dispose() {
@ -221,6 +231,11 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
}
}
@override
void navigateActiveApp() {
_router.go(activeApp.value.initialPath);
}
T getAppBloc<T extends Bloc>(final AppImplementation<T, dynamic> appImplementation) =>
appImplementation.getBloc(_account);

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

@ -54,8 +54,6 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
BehaviorSubject<int>? getUnreadCounter(final T bloc) => null;
Widget get page;
NeonNavigationDestination destination(final BuildContext context) {
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
final account = accountsBloc.activeAccount.value!;
@ -68,29 +66,20 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
);
}
/// Main branch displayed in the home page.
///
/// There's usually no need to override this.
StatefulShellBranch get mainBranch => StatefulShellBranch(
routes: [
route,
],
);
/// Route for the app.
///
/// All pages of the app must be specified as subroutes.
/// If this is not [GoRoute] an inital route name must be specified by overriding [initialRouteName].
/// If this is not a [GoRoute] an inital path must be specified by overriding [initialPath].
RouteBase get route;
/// Name of the initial route for this app.
///
/// Subclasses that don't provide a [GoRoute] for [route] must override this.
String get initialRouteName {
String get initialPath {
final route = this.route;
if (route is GoRoute && route.name != null) {
return route.name!;
if (route is GoRoute) {
return route.path;
}
throw FlutterError('No name for the initial route provided.');

21
packages/neon/neon/lib/src/pages/home.dart

@ -20,9 +20,12 @@ const kQuickBarWidth = kAvatarSize + 20;
class HomePage extends StatefulWidget {
const HomePage({
this.appView,
super.key,
});
final Widget? appView;
@override
State<HomePage> createState() => _HomePageState();
}
@ -123,14 +126,11 @@ class _HomePageState extends State<HomePage> {
const drawer = NeonDrawer();
const appBar = NeonAppBar();
final appView = ResultBuilder<Iterable<AppImplementation>>.behaviorSubject(
final appView = widget.appView ??
ResultBuilder<Iterable<AppImplementation>>.behaviorSubject(
stream: _appsBloc.appImplementations,
builder: (final context, final appImplementations) {
if (!appImplementations.hasData) {
return const SizedBox();
}
if (appImplementations.requireData.isEmpty) {
if (appImplementations.hasData && appImplementations.requireData.isEmpty) {
return Center(
child: Text(
AppLocalizations.of(context).errorNoCompatibleNextcloudAppsFound,
@ -139,16 +139,7 @@ class _HomePageState extends State<HomePage> {
);
}
return StreamBuilder<AppImplementation>(
stream: _appsBloc.activeApp,
builder: (final context, final activeAppIDSnapshot) {
if (!activeAppIDSnapshot.hasData) {
return const SizedBox();
}
return activeAppIDSnapshot.requireData.page;
},
);
},
);

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

@ -59,6 +59,9 @@ class _SettingsPageState extends State<SettingsPage> {
final appImplementations = Provider.of<Iterable<AppImplementation>>(context);
final appBar = AppBar(
leading: BackButton(
onPressed: () => const HomeRoute().go(context),
),
title: Text(AppLocalizations.of(context).settings),
actions: [
IconButton(

123
packages/neon/neon/lib/src/router.dart

@ -2,6 +2,7 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:go_router/go_router.dart';
@ -9,6 +10,7 @@ import 'package:meta/meta.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/app_implementation.dart';
import 'package:neon/src/models/notifications_interface.dart';
import 'package:neon/src/pages/account_settings.dart';
import 'package:neon/src/pages/home.dart';
import 'package:neon/src/pages/login.dart';
@ -25,9 +27,21 @@ part 'router.g.dart';
@internal
class AppRouter extends GoRouter {
AppRouter({
required final GlobalKey<NavigatorState> navigatorKey,
factory AppRouter({
required final AccountsBloc accountsBloc,
required final Iterable<AppImplementation> appImplementations,
final GlobalKey<NavigatorState>? navigatorKey,
}) =>
AppRouter._(
navigatorKey: navigatorKey ?? GlobalKey<NavigatorState>(),
accountsBloc: accountsBloc,
appImplementations: appImplementations,
);
AppRouter._({
required this.navigatorKey,
required final AccountsBloc accountsBloc,
required final Iterable<AppImplementation> appImplementations,
}) : super(
debugLogDiagnostics: kDebugMode,
refreshListenable: StreamListenable(accountsBloc.activeAccount),
@ -50,7 +64,33 @@ class AppRouter extends GoRouter {
return null;
},
routes: $appRoutes,
routes: [
ShellRoute(
routes: appImplementations
.whereNot((final a) => a is NotificationsAppInterface)
.map((final a) => a.route)
.toList(),
builder: _mainViewBuilder,
),
...appImplementations.whereType<NotificationsAppInterface>().map((final a) => a.route),
...$appRoutes,
],
);
final GlobalKey<NavigatorState> navigatorKey;
}
Widget _mainViewBuilder(
final BuildContext context,
final GoRouterState state, [
final Widget? child,
]) {
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
final account = accountsBloc.activeAccount.valueOrNull!;
return HomePage(
appView: child,
key: Key(account.id),
);
}
@ -77,51 +117,20 @@ class AccountSettingsRoute extends GoRouteData {
@TypedGoRoute<HomeRoute>(
path: '/',
name: 'home',
routes: [
TypedGoRoute<SettingsRoute>(
path: 'settings',
name: 'Settings',
routes: [
TypedGoRoute<NextcloudAppSettingsRoute>(
path: 'apps/:appid',
name: 'NextcloudAppSettings',
),
TypedGoRoute<_AddAccountRoute>(
path: 'account/add',
name: 'addAccount',
routes: [
TypedGoRoute<_AddAccountFlowRoute>(
path: 'flow',
),
TypedGoRoute<_AddAccountQrcodeRoute>(
path: 'qrcode',
),
TypedGoRoute<_AddAccountCheckServerStatusRoute>(
path: 'check/server',
),
TypedGoRoute<_AddAccountCheckAccountRoute>(
path: 'check/account',
),
],
),
TypedGoRoute<AccountSettingsRoute>(
path: 'account/:accountid',
name: 'AccountSettings',
),
],
),
],
)
@immutable
class HomeRoute extends GoRouteData {
const HomeRoute();
@override
Widget build(final BuildContext context, final GoRouterState state) {
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
final account = accountsBloc.activeAccount.valueOrNull!;
Widget build(final BuildContext context, final GoRouterState state) => _mainViewBuilder(context, state);
@override
String? redirect(final BuildContext context, final GoRouterState state) {
final appsBloc = Provider.of<AccountsBloc>(context, listen: false).activeAppsBloc;
final activeApp = appsBloc.activeApp.valueOrNull;
return HomePage(key: Key(account.id));
return activeApp?.initialPath;
}
}
@ -367,6 +376,38 @@ class NextcloudAppSettingsRoute extends GoRouteData {
}
}
@TypedGoRoute<SettingsRoute>(
path: '/settings',
name: 'Settings',
routes: [
TypedGoRoute<NextcloudAppSettingsRoute>(
path: 'apps/:appid',
name: 'NextcloudAppSettings',
),
TypedGoRoute<_AddAccountRoute>(
path: 'account/add',
name: 'addAccount',
routes: [
TypedGoRoute<_AddAccountFlowRoute>(
path: 'flow',
),
TypedGoRoute<_AddAccountQrcodeRoute>(
path: 'qrcode',
),
TypedGoRoute<_AddAccountCheckServerStatusRoute>(
path: 'check/server',
),
TypedGoRoute<_AddAccountCheckAccountRoute>(
path: 'check/account',
),
],
),
TypedGoRoute<AccountSettingsRoute>(
path: 'account/:accountid',
name: 'AccountSettings',
),
],
)
@immutable
class SettingsRoute extends GoRouteData {
const SettingsRoute({this.initialCategory});

292
packages/neon/neon/lib/src/router.g.dart

@ -9,15 +9,160 @@ part of 'router.dart';
List<RouteBase> get $appRoutes => [
$homeRoute,
$loginRoute,
$settingsRoute,
];
RouteBase get $homeRoute => GoRouteData.$route(
path: '/',
name: 'home',
factory: $HomeRouteExtension._fromState,
);
extension $HomeRouteExtension on HomeRoute {
static HomeRoute _fromState(GoRouterState state) => const HomeRoute();
String get location => GoRouteData.$location(
'/',
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $loginRoute => GoRouteData.$route(
path: '/login',
name: 'login',
factory: $LoginRouteExtension._fromState,
routes: [
GoRouteData.$route(
path: 'settings',
path: 'flow',
factory: $LoginFlowRouteExtension._fromState,
),
GoRouteData.$route(
path: 'qrcode',
factory: $LoginQrcodeRouteExtension._fromState,
),
GoRouteData.$route(
path: 'check/server',
factory: $LoginCheckServerStatusRouteExtension._fromState,
),
GoRouteData.$route(
path: 'check/account',
factory: $LoginCheckAccountRouteExtension._fromState,
),
],
);
extension $LoginRouteExtension on LoginRoute {
static LoginRoute _fromState(GoRouterState state) => const LoginRoute();
String get location => GoRouteData.$location(
'/login',
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $LoginFlowRouteExtension on LoginFlowRoute {
static LoginFlowRoute _fromState(GoRouterState state) => LoginFlowRoute(
serverUrl: state.uri.queryParameters['server-url']!,
);
String get location => GoRouteData.$location(
'/login/flow',
queryParams: {
'server-url': serverUrl,
},
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $LoginQrcodeRouteExtension on LoginQrcodeRoute {
static LoginQrcodeRoute _fromState(GoRouterState state) => const LoginQrcodeRoute();
String get location => GoRouteData.$location(
'/login/qrcode',
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $LoginCheckServerStatusRouteExtension on LoginCheckServerStatusRoute {
static LoginCheckServerStatusRoute _fromState(GoRouterState state) => LoginCheckServerStatusRoute(
serverUrl: state.uri.queryParameters['server-url']!,
loginName: state.uri.queryParameters['login-name'],
password: state.uri.queryParameters['password'],
);
String get location => GoRouteData.$location(
'/login/check/server',
queryParams: {
'server-url': serverUrl,
if (loginName != null) 'login-name': loginName,
if (password != null) 'password': password,
},
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $LoginCheckAccountRouteExtension on LoginCheckAccountRoute {
static LoginCheckAccountRoute _fromState(GoRouterState state) => LoginCheckAccountRoute(
serverUrl: state.uri.queryParameters['server-url']!,
loginName: state.uri.queryParameters['login-name']!,
password: state.uri.queryParameters['password']!,
);
String get location => GoRouteData.$location(
'/login/check/account',
queryParams: {
'server-url': serverUrl,
'login-name': loginName,
'password': password,
},
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
RouteBase get $settingsRoute => GoRouteData.$route(
path: '/settings',
name: 'Settings',
factory: $SettingsRouteExtension._fromState,
routes: [
@ -55,26 +200,8 @@ RouteBase get $homeRoute => GoRouteData.$route(
factory: $AccountSettingsRouteExtension._fromState,
),
],
),
],
);
extension $HomeRouteExtension on HomeRoute {
static HomeRoute _fromState(GoRouterState state) => const HomeRoute();
String get location => GoRouteData.$location(
'/',
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $SettingsRouteExtension on SettingsRoute {
static SettingsRoute _fromState(GoRouterState state) => SettingsRoute(
initialCategory:
@ -255,130 +382,3 @@ T? _$convertMapValue<T>(
extension<T extends Enum> on Map<T, String> {
T _$fromName(String value) => entries.singleWhere((element) => element.value == value).key;
}
RouteBase get $loginRoute => GoRouteData.$route(
path: '/login',
name: 'login',
factory: $LoginRouteExtension._fromState,
routes: [
GoRouteData.$route(
path: 'flow',
factory: $LoginFlowRouteExtension._fromState,
),
GoRouteData.$route(
path: 'qrcode',
factory: $LoginQrcodeRouteExtension._fromState,
),
GoRouteData.$route(
path: 'check/server',
factory: $LoginCheckServerStatusRouteExtension._fromState,
),
GoRouteData.$route(
path: 'check/account',
factory: $LoginCheckAccountRouteExtension._fromState,
),
],
);
extension $LoginRouteExtension on LoginRoute {
static LoginRoute _fromState(GoRouterState state) => const LoginRoute();
String get location => GoRouteData.$location(
'/login',
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $LoginFlowRouteExtension on LoginFlowRoute {
static LoginFlowRoute _fromState(GoRouterState state) => LoginFlowRoute(
serverUrl: state.uri.queryParameters['server-url']!,
);
String get location => GoRouteData.$location(
'/login/flow',
queryParams: {
'server-url': serverUrl,
},
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $LoginQrcodeRouteExtension on LoginQrcodeRoute {
static LoginQrcodeRoute _fromState(GoRouterState state) => const LoginQrcodeRoute();
String get location => GoRouteData.$location(
'/login/qrcode',
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $LoginCheckServerStatusRouteExtension on LoginCheckServerStatusRoute {
static LoginCheckServerStatusRoute _fromState(GoRouterState state) => LoginCheckServerStatusRoute(
serverUrl: state.uri.queryParameters['server-url']!,
loginName: state.uri.queryParameters['login-name'],
password: state.uri.queryParameters['password'],
);
String get location => GoRouteData.$location(
'/login/check/server',
queryParams: {
'server-url': serverUrl,
if (loginName != null) 'login-name': loginName,
if (password != null) 'password': password,
},
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}
extension $LoginCheckAccountRouteExtension on LoginCheckAccountRoute {
static LoginCheckAccountRoute _fromState(GoRouterState state) => LoginCheckAccountRoute(
serverUrl: state.uri.queryParameters['server-url']!,
loginName: state.uri.queryParameters['login-name']!,
password: state.uri.queryParameters['password']!,
);
String get location => GoRouteData.$location(
'/login/check/account',
queryParams: {
'server-url': serverUrl,
'login-name': loginName,
'password': password,
},
);
void go(BuildContext context) => context.go(location);
Future<T?> push<T>(BuildContext context) => context.push<T>(location);
void pushReplacement(BuildContext context) => context.pushReplacement(location);
void replace(BuildContext context) => context.replace(location);
}

2
packages/neon/neon/lib/src/widgets/account_switcher.dart

@ -57,7 +57,7 @@ class AccountSwitcherButton extends StatelessWidget {
title: Text(AppLocalizations.of(context).settingsAccountManage),
onTap: () {
Navigator.of(context).pop();
const SettingsRoute(initialCategory: SettingsCageories.accounts).push(context);
const SettingsRoute(initialCategory: SettingsCageories.accounts).go(context);
},
),
],

44
packages/neon/neon/lib/src/widgets/app_bar.dart

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
@ -100,7 +101,6 @@ class NotificationIconButton extends StatefulWidget {
class _NotificationIconButtonState extends State<NotificationIconButton> {
late AccountsBloc _accountsBloc;
late AppsBloc _appsBloc;
late List<Account> _accounts;
late Account _account;
late StreamSubscription notificationSubscription;
@ -109,13 +109,12 @@ class _NotificationIconButtonState extends State<NotificationIconButton> {
super.initState();
_accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
_appsBloc = _accountsBloc.activeAppsBloc;
_accounts = _accountsBloc.accounts.value;
_account = _accountsBloc.activeAccount.value!;
notificationSubscription = _appsBloc.openNotifications.listen((final _) async {
notificationSubscription = _appsBloc.openNotifications.listen((final _) {
final notificationsAppImplementation = _appsBloc.notificationsAppImplementation.valueOrNull;
if (notificationsAppImplementation != null && notificationsAppImplementation.hasData) {
await _openNotifications(notificationsAppImplementation.data!);
_openNotifications(notificationsAppImplementation.data!);
}
});
}
@ -127,37 +126,8 @@ class _NotificationIconButtonState extends State<NotificationIconButton> {
super.dispose();
}
// TODO: migrate to go_router with a separate page
Future<void> _openNotifications(
final NotificationsAppInterface app,
) async {
final page = Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(app.name(context)),
if (_accounts.length > 1) ...[
Text(
_account.humanReadableID,
style: Theme.of(context).textTheme.bodySmall,
),
],
],
),
),
body: app.page,
);
await Navigator.of(context).push(
MaterialPageRoute(
builder: (final context) => Provider<NotificationsBlocInterface>(
create: (final context) => app.getBloc(_account),
child: page,
),
),
);
void _openNotifications(final NotificationsAppInterface app) {
GoRouter.of(context).go(app.initialPath);
}
@override
@ -177,8 +147,8 @@ class _NotificationIconButtonState extends State<NotificationIconButton> {
final unreadCount = unreadCounterSnapshot.data ?? 0;
return IconButton(
key: Key('app-${notificationsImplementationData.id}'),
onPressed: () async {
await _openNotifications(notificationsImplementationData);
onPressed: () {
_openNotifications(notificationsImplementationData);
},
tooltip: AppLocalizations.of(context).appImplementationName(notificationsImplementationData.id),
icon: NeonAppImplementationIcon(

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

@ -66,9 +66,6 @@ class FilesApp extends AppImplementation<FilesBloc, FilesAppSpecificOptions> {
platform,
);
@override
Widget get page => const FilesMainPage();
@override
RouteBase get route => $filesAppRoute;
}

3
packages/neon/neon_news/lib/neon_news.dart

@ -73,9 +73,6 @@ class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
account,
);
@override
Widget get page => const NewsMainPage();
@override
RouteBase get route => $newsAppRoute;

3
packages/neon/neon_notes/lib/neon_notes.dart

@ -63,9 +63,6 @@ class NotesApp extends AppImplementation<NotesBloc, NotesAppSpecificOptions> {
account,
);
@override
Widget get page => const NotesMainPage();
@override
RouteBase get route => $notesAppRoute;
}

3
packages/neon/neon_notifications/lib/neon_notifications.dart

@ -43,9 +43,6 @@ class NotificationsApp extends AppImplementation<NotificationsBloc, Notification
account,
);
@override
Widget get page => const NotificationsMainPage();
@override
RouteBase get route => $notificationsAppRoute;

44
packages/neon/neon_notifications/lib/routes.dart

@ -1,8 +1,11 @@
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:neon/blocs.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/models.dart';
import 'package:neon/utils.dart';
import 'package:neon_notifications/neon_notifications.dart';
import 'package:provider/provider.dart';
part 'routes.g.dart';
@ -15,5 +18,42 @@ class NotificationsAppRoute extends NeonAppRoute {
const NotificationsAppRoute();
@override
Widget build(final BuildContext context, final GoRouterState state) => const NotificationsMainPage();
Widget build(final BuildContext context, final GoRouterState state) {
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
final appsBloc = accountsBloc.activeAppsBloc;
final app = appsBloc.notificationsAppImplementation.value.data!;
final accounts = accountsBloc.accounts.value;
final account = accountsBloc.activeAccount.value!;
Widget title = Text(AppLocalizations.of(context).appImplementationName(AppIDs.notifications));
if (accounts.length > 1) {
title = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
title,
Text(
account.humanReadableID,
style: Theme.of(context).textTheme.bodySmall,
),
],
);
}
final page = Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
leading: BackButton(
onPressed: appsBloc.navigateActiveApp,
),
title: title,
),
body: const NotificationsMainPage(),
);
return Provider<NotificationsBlocInterface>(
create: (final context) => app.getBloc(account),
child: page,
);
}
}

Loading…
Cancel
Save