Browse Source

neon: Open notifications page on unknown push notification

pull/162/head
jld3103 2 years ago
parent
commit
e2ac5e3e26
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 8
      packages/neon/lib/src/blocs/apps.dart
  2. 807
      packages/neon/lib/src/pages/home.dart

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

@ -14,6 +14,8 @@ abstract class AppsBlocStates {
BehaviorSubject<Result<NotificationsApp?>> get notificationsAppImplementation; BehaviorSubject<Result<NotificationsApp?>> get notificationsAppImplementation;
BehaviorSubject<String?> get activeAppID; BehaviorSubject<String?> get activeAppID;
BehaviorSubject get openNotifications;
} }
class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates { class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates {
@ -88,6 +90,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
unawaited(appImplementations.close()); unawaited(appImplementations.close());
unawaited(notificationsAppImplementation.close()); unawaited(notificationsAppImplementation.close());
unawaited(activeAppID.close()); unawaited(activeAppID.close());
unawaited(openNotifications.close());
for (final key in _blocs.keys) { for (final key in _blocs.keys) {
_blocs[key]!.dispose(); _blocs[key]!.dispose();
} }
@ -107,6 +110,9 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
BehaviorSubject<Result<NotificationsApp?>> notificationsAppImplementation = BehaviorSubject<Result<NotificationsApp?>> notificationsAppImplementation =
BehaviorSubject<Result<NotificationsApp?>>(); BehaviorSubject<Result<NotificationsApp?>>();
@override
BehaviorSubject openNotifications = BehaviorSubject();
@override @override
Future refresh() async { Future refresh() async {
await _requestManager.wrapNextcloud<List<NextcloudApp>, NextcloudCoreNavigationApps>( await _requestManager.wrapNextcloud<List<NextcloudApp>, NextcloudCoreNavigationApps>(
@ -128,7 +134,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
activeAppID.add(appID); activeAppID.add(appID);
} }
} else if (appID == 'notifications') { } else if (appID == 'notifications') {
// TODO: Open notifications page openNotifications.add(null);
} else { } else {
throw Exception('App $appID not found'); throw Exception('App $appID not found');
} }

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

@ -21,8 +21,9 @@ class _HomePageState extends State<HomePage> {
final _scaffoldKey = GlobalKey<ScaffoldState>(); final _scaffoldKey = GlobalKey<ScaffoldState>();
late GlobalOptions _globalOptions; late GlobalOptions _globalOptions;
late CapabilitiesBloc _capabilitiesBloc; late AccountsBloc _accountsBloc;
late AppsBloc _appsBloc; late AppsBloc _appsBloc;
late CapabilitiesBloc _capabilitiesBloc;
late FirstLaunchBloc _firstLaunchBloc; late FirstLaunchBloc _firstLaunchBloc;
@override @override
@ -30,10 +31,22 @@ class _HomePageState extends State<HomePage> {
super.initState(); super.initState();
_globalOptions = Provider.of<GlobalOptions>(context, listen: false); _globalOptions = Provider.of<GlobalOptions>(context, listen: false);
_appsBloc = Provider.of<AccountsBloc>(context, listen: false).getAppsBloc(widget.account); _accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
_capabilitiesBloc = Provider.of<AccountsBloc>(context, listen: false).getCapabilitiesBloc(widget.account); _appsBloc = _accountsBloc.getAppsBloc(widget.account);
_capabilitiesBloc = _accountsBloc.getCapabilitiesBloc(widget.account);
_firstLaunchBloc = Provider.of<FirstLaunchBloc>(context, listen: false); _firstLaunchBloc = Provider.of<FirstLaunchBloc>(context, listen: false);
_appsBloc.openNotifications.listen((final _) async {
final notificationsAppImplementation = _appsBloc.notificationsAppImplementation.valueOrNull;
if (notificationsAppImplementation != null) {
await _openNotifications(
notificationsAppImplementation.data!,
_accountsBloc.accounts.value,
_accountsBloc.activeAccount.value!,
);
}
});
_capabilitiesBloc.capabilities.listen((final result) async { _capabilitiesBloc.capabilities.listen((final result) async {
if (result.data != null) { if (result.data != null) {
widget.onThemeChanged(result.data!.capabilities.theming); widget.onThemeChanged(result.data!.capabilities.theming);
@ -153,6 +166,34 @@ class _HomePageState extends State<HomePage> {
); );
} }
Future _openNotifications(
final NotificationsApp app,
final List<Account> accounts,
final Account account,
) async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (final context) => Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(app.name(context)),
if (accounts.length > 1) ...[
Text(
account.client.humanReadableID,
style: Theme.of(context).textTheme.bodySmall,
),
],
],
),
),
body: app.buildPage(context, _appsBloc),
),
),
);
}
@override @override
void dispose() { void dispose() {
_capabilitiesBloc.dispose(); _capabilitiesBloc.dispose();
@ -160,450 +201,432 @@ class _HomePageState extends State<HomePage> {
} }
@override @override
Widget build(final BuildContext context) { Widget build(final BuildContext context) => ResultBuilder<CapabilitiesBloc, Capabilities>(
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false); stream: _capabilitiesBloc.capabilities,
return ResultBuilder<CapabilitiesBloc, Capabilities>( builder: (final context, final capabilities) => ResultBuilder<AppsBloc, List<AppImplementation>>(
stream: _capabilitiesBloc.capabilities, stream: _appsBloc.appImplementations,
builder: (final context, final capabilities) => ResultBuilder<AppsBloc, List<AppImplementation>>( builder: (final context, final appImplementations) => ResultBuilder<AppsBloc, NotificationsApp?>(
stream: _appsBloc.appImplementations, stream: _appsBloc.notificationsAppImplementation,
builder: (final context, final appImplementations) => ResultBuilder<AppsBloc, NotificationsApp?>( builder: (final context, final notificationsAppImplementation) => StreamBuilder<String?>(
stream: _appsBloc.notificationsAppImplementation, stream: _appsBloc.activeAppID,
builder: (final context, final notificationsAppImplementation) => StreamBuilder<String?>(
stream: _appsBloc.activeAppID,
builder: (
final context,
final activeAppIDSnapshot,
) =>
StreamBuilder<List<Account>>(
stream: accountsBloc.accounts,
builder: ( builder: (
final context, final context,
final accountsSnapshot, final activeAppIDSnapshot,
) => ) =>
OptionBuilder<NavigationMode>( StreamBuilder<List<Account>>(
option: _globalOptions.navigationMode, stream: _accountsBloc.accounts,
builder: (final context, final navigationMode) => WillPopScope( builder: (
onWillPop: () async { final context,
if (_scaffoldKey.currentState!.isDrawerOpen) { final accountsSnapshot,
Navigator.pop(context); ) =>
return true; OptionBuilder<NavigationMode>(
} option: _globalOptions.navigationMode,
builder: (final context, final navigationMode) => WillPopScope(
onWillPop: () async {
if (_scaffoldKey.currentState!.isDrawerOpen) {
Navigator.pop(context);
return true;
}
_scaffoldKey.currentState!.openDrawer(); _scaffoldKey.currentState!.openDrawer();
return false; return false;
}, },
child: Builder( child: Builder(
builder: (final context) { builder: (final context) {
if (accountsSnapshot.hasData) { if (accountsSnapshot.hasData) {
final accounts = accountsSnapshot.data!; final accounts = accountsSnapshot.data!;
final account = accounts.singleWhere((final account) => account.id == widget.account.id); final account = accounts.singleWhere((final account) => account.id == widget.account.id);
final isQuickBar = navigationMode == NavigationMode.quickBar; final isQuickBar = navigationMode == NavigationMode.quickBar;
final drawer = Drawer( final drawer = Drawer(
width: isQuickBar ? kQuickBarWidth : null, width: isQuickBar ? kQuickBarWidth : null,
child: Container( child: Container(
padding: isQuickBar ? const EdgeInsets.all(5) : null, padding: isQuickBar ? const EdgeInsets.all(5) : null,
child: Column( child: Column(
children: [ children: [
Expanded( Expanded(
child: Scrollbar( child: Scrollbar(
child: ListView( child: ListView(
// Needed for the drawer header to also render in the statusbar // Needed for the drawer header to also render in the statusbar
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
children: [ children: [
Builder( Builder(
builder: (final context) { builder: (final context) {
if (accountsSnapshot.hasData) { if (accountsSnapshot.hasData) {
if (isQuickBar) { if (isQuickBar) {
return Column( return Column(
children: [ children: [
if (accounts.length != 1) ...[ if (accounts.length != 1) ...[
for (final account in accounts) ...[ for (final account in accounts) ...[
Container( Container(
margin: const EdgeInsets.symmetric( margin: const EdgeInsets.symmetric(
vertical: 5, vertical: 5,
), ),
child: Tooltip( child: Tooltip(
message: account.client.humanReadableID, message: account.client.humanReadableID,
child: IconButton( child: IconButton(
onPressed: () { onPressed: () {
accountsBloc.setActiveAccount(account); _accountsBloc.setActiveAccount(account);
}, },
icon: IntrinsicHeight( icon: IntrinsicHeight(
child: AccountAvatar( child: AccountAvatar(
account: account, account: account,
),
), ),
), ),
), ),
), ),
), ],
], Container(
Container( margin: const EdgeInsets.only(
margin: const EdgeInsets.only( top: 10,
top: 10, ),
), child: Divider(
child: Divider( height: 5,
height: 5, color: Theme.of(context).appBarTheme.foregroundColor,
color: Theme.of(context).appBarTheme.foregroundColor,
),
),
],
],
);
}
return DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (capabilities.data != null) ...[
if (capabilities.data!.capabilities.theming?.name != null) ...[
Text(
capabilities.data!.capabilities.theming!.name!,
style: DefaultTextStyle.of(context).style.copyWith(
color: Theme.of(context).appBarTheme.foregroundColor,
),
),
],
if (capabilities.data!.capabilities.theming?.logo != null) ...[
Flexible(
child: CachedURLImage(
url: capabilities.data!.capabilities.theming!.logo!,
), ),
), ),
], ],
] else ...[
ExceptionWidget(
capabilities.error,
onRetry: _capabilitiesBloc.refresh,
),
CustomLinearProgressIndicator(
visible: capabilities.loading,
),
], ],
if (accounts.length != 1) ...[
DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
dropdownColor: Theme.of(context).colorScheme.primary,
iconEnabledColor: Theme.of(context).colorScheme.onBackground,
value: widget.account.id,
items: accounts
.map<DropdownMenuItem<String>>(
(final account) => DropdownMenuItem<String>(
value: account.id,
child: AccountTile(
account: account,
dense: true,
textColor:
Theme.of(context).appBarTheme.foregroundColor,
),
),
)
.toList(),
onChanged: (final id) {
for (final account in accounts) {
if (account.id == id) {
accountsBloc.setActiveAccount(account);
break;
}
}
},
),
),
],
],
),
);
}
return Container();
},
),
ExceptionWidget(
appImplementations.error,
onlyIcon: isQuickBar,
onRetry: _appsBloc.refresh,
),
CustomLinearProgressIndicator(
visible: appImplementations.loading,
),
if (appImplementations.data != null) ...[
for (final appImplementation in appImplementations.data!) ...[
StreamBuilder<int>(
stream: appImplementation.getUnreadCounter(_appsBloc) ??
BehaviorSubject<int>.seeded(0),
builder: (final context, final unreadCounterSnapshot) {
final unreadCount = unreadCounterSnapshot.data ?? 0;
if (isQuickBar) {
return Tooltip(
message: appImplementation.name(context),
child: IconButton(
onPressed: () async {
await _appsBloc.setActiveApp(appImplementation.id);
},
icon: AppImplementationIcon(
appImplementation: appImplementation,
unreadCount: unreadCount,
color: Theme.of(context).colorScheme.primary,
),
),
); );
} }
return ListTile( return DrawerHeader(
key: Key('app-${appImplementation.id}'), decoration: BoxDecoration(
title: Row( color: Theme.of(context).colorScheme.primary,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text(appImplementation.name(context)), if (capabilities.data != null) ...[
if (unreadCount > 0) ...[ if (capabilities.data!.capabilities.theming?.name != null) ...[
Text( Text(
unreadCount.toString(), capabilities.data!.capabilities.theming!.name!,
style: TextStyle( style: DefaultTextStyle.of(context).style.copyWith(
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).appBarTheme.foregroundColor,
fontWeight: FontWeight.bold, ),
fontSize: 14, ),
],
if (capabilities.data!.capabilities.theming?.logo != null) ...[
Flexible(
child: CachedURLImage(
url: capabilities.data!.capabilities.theming!.logo!,
),
),
],
] else ...[
ExceptionWidget(
capabilities.error,
onRetry: _capabilitiesBloc.refresh,
),
CustomLinearProgressIndicator(
visible: capabilities.loading,
),
],
if (accounts.length != 1) ...[
DropdownButtonHideUnderline(
child: DropdownButton<String>(
isExpanded: true,
dropdownColor: Theme.of(context).colorScheme.primary,
iconEnabledColor:
Theme.of(context).colorScheme.onBackground,
value: widget.account.id,
items: accounts
.map<DropdownMenuItem<String>>(
(final account) => DropdownMenuItem<String>(
value: account.id,
child: AccountTile(
account: account,
dense: true,
textColor:
Theme.of(context).appBarTheme.foregroundColor,
),
),
)
.toList(),
onChanged: (final id) {
for (final account in accounts) {
if (account.id == id) {
_accountsBloc.setActiveAccount(account);
break;
}
}
},
), ),
), ),
], ],
], ],
), ),
leading: appImplementation.buildIcon(context),
minLeadingWidth: 0,
onTap: () async {
await _appsBloc.setActiveApp(appImplementation.id);
if (navigationMode == NavigationMode.drawer) {
// Don't pop when the drawer is always shown
if (!mounted) {
return;
}
Navigator.of(context).pop();
}
},
); );
}, }
), return Container();
},
),
ExceptionWidget(
appImplementations.error,
onlyIcon: isQuickBar,
onRetry: _appsBloc.refresh,
),
CustomLinearProgressIndicator(
visible: appImplementations.loading,
),
if (appImplementations.data != null) ...[
for (final appImplementation in appImplementations.data!) ...[
StreamBuilder<int>(
stream: appImplementation.getUnreadCounter(_appsBloc) ??
BehaviorSubject<int>.seeded(0),
builder: (final context, final unreadCounterSnapshot) {
final unreadCount = unreadCounterSnapshot.data ?? 0;
if (isQuickBar) {
return Tooltip(
message: appImplementation.name(context),
child: IconButton(
onPressed: () async {
await _appsBloc.setActiveApp(appImplementation.id);
},
icon: AppImplementationIcon(
appImplementation: appImplementation,
unreadCount: unreadCount,
color: Theme.of(context).colorScheme.primary,
),
),
);
}
return ListTile(
key: Key('app-${appImplementation.id}'),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(appImplementation.name(context)),
if (unreadCount > 0) ...[
Text(
unreadCount.toString(),
style: TextStyle(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold,
fontSize: 14,
),
),
],
],
),
leading: appImplementation.buildIcon(context),
minLeadingWidth: 0,
onTap: () async {
await _appsBloc.setActiveApp(appImplementation.id);
if (navigationMode == NavigationMode.drawer) {
// Don't pop when the drawer is always shown
if (!mounted) {
return;
}
Navigator.of(context).pop();
}
},
);
},
),
],
], ],
], ],
], ),
), ),
), ),
), if (isQuickBar) ...[
if (isQuickBar) ...[ IconButton(
IconButton( icon: Icon(
icon: Icon( Icons.settings,
Icons.settings, color: Theme.of(context).appBarTheme.foregroundColor,
color: Theme.of(context).appBarTheme.foregroundColor, ),
onPressed: _openSettings,
), ),
onPressed: _openSettings, ] else ...[
), ListTile(
] else ...[ key: const Key('settings'),
ListTile( title: Text(AppLocalizations.of(context).settings),
key: const Key('settings'), leading: const Icon(Icons.settings),
title: Text(AppLocalizations.of(context).settings), minLeadingWidth: 0,
leading: const Icon(Icons.settings), onTap: () async {
minLeadingWidth: 0, if (navigationMode == NavigationMode.drawer) {
onTap: () async { Navigator.of(context).pop();
if (navigationMode == NavigationMode.drawer) { }
Navigator.of(context).pop(); await _openSettings();
} },
await _openSettings(); ),
}, ],
),
], ],
], ),
), ),
), );
);
return Scaffold( return Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
body: Row( body: Row(
children: [ children: [
if (navigationMode == NavigationMode.drawerAlwaysVisible) ...[ if (navigationMode == NavigationMode.drawerAlwaysVisible) ...[
drawer, drawer,
], ],
Expanded( Expanded(
child: Scaffold( child: Scaffold(
key: _scaffoldKey, key: _scaffoldKey,
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
drawer: navigationMode == NavigationMode.drawer ? drawer : null, drawer: navigationMode == NavigationMode.drawer ? drawer : null,
appBar: AppBar( appBar: AppBar(
scrolledUnderElevation: navigationMode != NavigationMode.drawer ? 0 : null, scrolledUnderElevation: navigationMode != NavigationMode.drawer ? 0 : null,
automaticallyImplyLeading: navigationMode == NavigationMode.drawer, automaticallyImplyLeading: navigationMode == NavigationMode.drawer,
leadingWidth: isQuickBar ? kQuickBarWidth : null, leadingWidth: isQuickBar ? kQuickBarWidth : null,
leading: isQuickBar leading: isQuickBar
? Container( ? Container(
padding: const EdgeInsets.all(5), padding: const EdgeInsets.all(5),
child: capabilities.data?.capabilities.theming?.logo != null child: capabilities.data?.capabilities.theming?.logo != null
? CachedURLImage( ? CachedURLImage(
url: capabilities.data!.capabilities.theming!.logo!, url: capabilities.data!.capabilities.theming!.logo!,
) )
: null, : null,
) )
: null, : null,
title: Column( title: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( Row(
children: [ children: [
if (appImplementations.data != null && activeAppIDSnapshot.hasData) ...[ if (appImplementations.data != null && activeAppIDSnapshot.hasData) ...[
Flexible( Flexible(
child: Text( child: Text(
appImplementations.data! appImplementations.data!
.singleWhere((final a) => a.id == activeAppIDSnapshot.data!) .singleWhere((final a) => a.id == activeAppIDSnapshot.data!)
.name(context), .name(context),
),
), ),
), ],
], if (appImplementations.error != null) ...[
if (appImplementations.error != null) ...[ const SizedBox(
const SizedBox( width: 8,
width: 8,
),
Icon(
Icons.error_outline,
size: 30,
color: Theme.of(context).colorScheme.onPrimary,
),
],
if (appImplementations.loading) ...[
const SizedBox(
width: 8,
),
Expanded(
child: CustomLinearProgressIndicator(
color: Theme.of(context).appBarTheme.foregroundColor,
), ),
), Icon(
Icons.error_outline,
size: 30,
color: Theme.of(context).colorScheme.onPrimary,
),
],
if (appImplementations.loading) ...[
const SizedBox(
width: 8,
),
Expanded(
child: CustomLinearProgressIndicator(
color: Theme.of(context).appBarTheme.foregroundColor,
),
),
],
], ],
),
if (accounts.length > 1) ...[
Text(
account.client.humanReadableID,
style: Theme.of(context).textTheme.bodySmall,
),
], ],
), ],
if (accounts.length > 1) ...[ ),
Text( actions: [
account.client.humanReadableID, if (notificationsAppImplementation.data != null) ...[
style: Theme.of(context).textTheme.bodySmall, StreamBuilder<int>(
stream: notificationsAppImplementation.data!.getUnreadCounter(_appsBloc),
builder: (final context, final unreadCounterSnapshot) {
final unreadCount = unreadCounterSnapshot.data ?? 0;
return IconButton(
key: Key('app-${notificationsAppImplementation.data!.id}'),
icon: AppImplementationIcon(
appImplementation: notificationsAppImplementation.data!,
unreadCount: unreadCount,
color: unreadCount > 0
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onBackground,
width: kAvatarSize * 2 / 3,
height: kAvatarSize * 2 / 3,
),
onPressed: () async {
await _openNotifications(
notificationsAppImplementation.data!,
accounts,
account,
);
},
);
},
), ),
], ],
], IconButton(
), icon: IntrinsicWidth(
actions: [ child: AccountAvatar(
if (notificationsAppImplementation.data != null) ...[ account: account,
StreamBuilder<int>( ),
stream: notificationsAppImplementation.data!.getUnreadCounter(_appsBloc), ),
builder: (final context, final unreadCounterSnapshot) { onPressed: () async {
final unreadCount = unreadCounterSnapshot.data ?? 0; await Navigator.of(context).push(
return IconButton( MaterialPageRoute(
key: Key('app-${notificationsAppImplementation.data!.id}'), builder: (final context) => AccountSettingsPage(
icon: AppImplementationIcon( bloc: _accountsBloc,
appImplementation: notificationsAppImplementation.data!, account: account,
unreadCount: unreadCount, ),
color: unreadCount > 0
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onBackground,
width: kAvatarSize * 2 / 3,
height: kAvatarSize * 2 / 3,
), ),
onPressed: () async {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (final context) => Scaffold(
appBar: AppBar(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(notificationsAppImplementation.data!.name(context)),
if (accounts.length > 1) ...[
Text(
account.client.humanReadableID,
style: Theme.of(context).textTheme.bodySmall,
),
],
],
),
),
body: notificationsAppImplementation.data!
.buildPage(context, _appsBloc),
),
),
);
},
); );
}, },
), ),
], ],
IconButton( ),
icon: IntrinsicWidth( body: Row(
child: AccountAvatar( children: [
account: account, if (navigationMode == NavigationMode.quickBar) ...[
), drawer,
), ],
onPressed: () async { Expanded(
await Navigator.of(context).push( child: Column(
MaterialPageRoute( children: [
builder: (final context) => AccountSettingsPage( ExceptionWidget(
bloc: accountsBloc, appImplementations.error,
account: account, onRetry: _appsBloc.refresh,
), ),
), if (appImplementations.data != null) ...[
); if (appImplementations.data!.isEmpty) ...[
},
),
],
),
body: Row(
children: [
if (navigationMode == NavigationMode.quickBar) ...[
drawer,
],
Expanded(
child: Column(
children: [
ExceptionWidget(
appImplementations.error,
onRetry: _appsBloc.refresh,
),
if (appImplementations.data != null) ...[
if (appImplementations.data!.isEmpty) ...[
Expanded(
child: Center(
child: Text(
AppLocalizations.of(context).errorNoCompatibleNextcloudAppsFound,
textAlign: TextAlign.center,
),
),
),
] else ...[
if (activeAppIDSnapshot.hasData) ...[
Expanded( Expanded(
child: appImplementations.data! child: Center(
.singleWhere((final a) => a.id == activeAppIDSnapshot.data!) child: Text(
.buildPage(context, _appsBloc), AppLocalizations.of(context)
.errorNoCompatibleNextcloudAppsFound,
textAlign: TextAlign.center,
),
),
), ),
] else ...[
if (activeAppIDSnapshot.hasData) ...[
Expanded(
child: appImplementations.data!
.singleWhere((final a) => a.id == activeAppIDSnapshot.data!)
.buildPage(context, _appsBloc),
),
],
], ],
], ],
], ],
], ),
), ),
), ],
], ),
), ),
), ),
), ],
], ),
), );
); }
} return Container();
return Container(); },
}, ),
), ),
), ),
), ),
), ),
), ),
), ),
), );
);
}
} }

Loading…
Cancel
Save