Browse Source

neon: prevent race condition on logout

pull/338/head
Nikolas Rimikis 2 years ago
parent
commit
d2192a50ef
No known key found for this signature in database
GPG Key ID: 85ED1DE9786A4FF2
  1. 652
      packages/neon/neon/lib/src/pages/home.dart

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

@ -197,376 +197,378 @@ class _HomePageState extends State<HomePage> {
builder: (final context) { builder: (final context) {
if (accountsSnapshot.hasData) { if (accountsSnapshot.hasData) {
final accounts = accountsSnapshot.data!; final accounts = accountsSnapshot.data!;
final account = accounts.find(_account.id)!; final account = accounts.find(_account.id);
if (account != null) {
final isQuickBar = navigationMode == NavigationMode.quickBar; final isQuickBar = navigationMode == NavigationMode.quickBar;
final drawer = Builder( final drawer = Builder(
builder: (final context) => Drawer( builder: (final context) => 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(
controller: drawerScrollController,
interactive: true,
child: ListView(
controller: drawerScrollController, controller: drawerScrollController,
// Needed for the drawer header to also render in the statusbar interactive: true,
padding: EdgeInsets.zero, child: ListView(
children: [ controller: drawerScrollController,
Builder( // Needed for the drawer header to also render in the statusbar
builder: (final context) { padding: EdgeInsets.zero,
if (accountsSnapshot.hasData) { children: [
if (isQuickBar) { Builder(
return Column( builder: (final context) {
children: [ if (accountsSnapshot.hasData) {
if (accounts.length != 1) ...[ if (isQuickBar) {
for (final account in accounts) ...[ return Column(
Container( children: [
margin: const EdgeInsets.symmetric( if (accounts.length != 1) ...[
vertical: 5, for (final account in accounts) ...[
), Container(
child: IconButton( margin: const EdgeInsets.symmetric(
onPressed: () { vertical: 5,
_accountsBloc.setActiveAccount(account); ),
}, child: IconButton(
tooltip: account.client.humanReadableID, onPressed: () {
icon: IntrinsicHeight( _accountsBloc.setActiveAccount(account);
child: NeonAccountAvatar( },
account: account, tooltip: account.client.humanReadableID,
icon: IntrinsicHeight(
child: NeonAccountAvatar(
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: NeonCachedUrlImage(
url: capabilities.data!.capabilities.theming!.logo!,
), ),
), ),
], ],
] else ...[
NeonException(
capabilities.error,
onRetry: _capabilitiesBloc.refresh,
),
NeonLinearProgressIndicator(
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: _account.id,
items: accounts
.map<DropdownMenuItem<String>>(
(final account) => DropdownMenuItem<String>(
value: account.id,
child: NeonAccountTile(
account: account,
dense: true,
textColor: Theme.of(context)
.appBarTheme
.foregroundColor,
),
),
)
.toList(),
onChanged: (final id) {
if (id != null) {
_accountsBloc.setActiveAccount(accounts.find(id));
}
},
),
),
],
],
),
);
}
return Container();
},
),
NeonException(
appImplementations.error,
onlyIcon: isQuickBar,
onRetry: _appsBloc.refresh,
),
NeonLinearProgressIndicator(
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 IconButton(
onPressed: () async {
await _appsBloc.setActiveApp(appImplementation.id);
},
tooltip: appImplementation.name(context),
icon: NeonAppImplementationIcon(
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 !=
Text( null) ...[
unreadCount.toString(), Text(
style: TextStyle( capabilities.data!.capabilities.theming!.name!,
color: Theme.of(context).colorScheme.primary, style: DefaultTextStyle.of(context).style.copyWith(
fontWeight: FontWeight.bold, color:
fontSize: 14, Theme.of(context).appBarTheme.foregroundColor,
),
),
],
if (capabilities.data!.capabilities.theming?.logo !=
null) ...[
Flexible(
child: NeonCachedUrlImage(
url: capabilities.data!.capabilities.theming!.logo!,
),
),
],
] else ...[
NeonException(
capabilities.error,
onRetry: _capabilitiesBloc.refresh,
),
NeonLinearProgressIndicator(
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: _account.id,
items: accounts
.map<DropdownMenuItem<String>>(
(final account) => DropdownMenuItem<String>(
value: account.id,
child: NeonAccountTile(
account: account,
dense: true,
textColor: Theme.of(context)
.appBarTheme
.foregroundColor,
),
),
)
.toList(),
onChanged: (final id) {
if (id != null) {
_accountsBloc.setActiveAccount(accounts.find(id));
}
},
), ),
), ),
], ],
], ],
), ),
leading: appImplementation.buildIcon(context),
minLeadingWidth: 0,
onTap: () async {
await _appsBloc.setActiveApp(appImplementation.id);
if (!mounted) {
return;
}
Scaffold.maybeOf(context)?.closeDrawer();
},
); );
}, }
), return Container();
},
),
NeonException(
appImplementations.error,
onlyIcon: isQuickBar,
onRetry: _appsBloc.refresh,
),
NeonLinearProgressIndicator(
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 IconButton(
onPressed: () async {
await _appsBloc.setActiveApp(appImplementation.id);
},
tooltip: appImplementation.name(context),
icon: NeonAppImplementationIcon(
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 (!mounted) {
return;
}
Scaffold.maybeOf(context)?.closeDrawer();
},
);
},
),
],
], ],
], ],
], ),
), ),
), ),
), if (isQuickBar) ...[
if (isQuickBar) ...[ IconButton(
IconButton( onPressed: () => const SettingsRoute().go(context),
onPressed: () => const SettingsRoute().go(context), tooltip: AppLocalizations.of(context).settings,
tooltip: AppLocalizations.of(context).settings, icon: Icon(
icon: Icon( Icons.settings,
Icons.settings, color: Theme.of(context).appBarTheme.foregroundColor,
color: Theme.of(context).appBarTheme.foregroundColor, ),
), ),
), ] else ...[
] else ...[ ListTile(
ListTile( key: const Key('settings'),
key: const Key('settings'), title: Text(AppLocalizations.of(context).settings),
title: Text(AppLocalizations.of(context).settings), leading: const Icon(Icons.settings),
leading: const Icon(Icons.settings), minLeadingWidth: 0,
minLeadingWidth: 0, onTap: () async {
onTap: () async { Scaffold.maybeOf(context)?.closeDrawer();
Scaffold.maybeOf(context)?.closeDrawer(); const SettingsRoute().go(context);
const SettingsRoute().go(context); },
}, ),
), ],
], ],
], ),
), ),
), ),
), );
);
return Row( return 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
? NeonCachedUrlImage( ? NeonCachedUrlImage(
url: capabilities.data!.capabilities.theming!.logo!, url: capabilities.data!.capabilities.theming!.logo!,
svgColor: Theme.of(context).iconTheme.color, svgColor: Theme.of(context).iconTheme.color,
) )
: 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!
.find(activeAppIDSnapshot.data!)! .find(activeAppIDSnapshot.data!)!
.name(context), .name(context),
),
), ),
), ],
], if (appImplementations.error != null) ...[
if (appImplementations.error != null) ...[ const SizedBox(
const SizedBox( width: 8,
width: 8,
),
NeonException(
appImplementations.error,
onRetry: _appsBloc.refresh,
onlyIcon: true,
),
],
if (appImplementations.loading) ...[
const SizedBox(
width: 8,
),
Expanded(
child: NeonLinearProgressIndicator(
color: Theme.of(context).appBarTheme.foregroundColor,
), ),
), NeonException(
appImplementations.error,
onRetry: _appsBloc.refresh,
onlyIcon: true,
),
],
if (appImplementations.loading) ...[
const SizedBox(
width: 8,
),
Expanded(
child: NeonLinearProgressIndicator(
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}'),
onPressed: () async {
await _openNotifications(
notificationsAppImplementation.data!,
accounts,
account,
);
},
tooltip: AppLocalizations.of(context)
.appImplementationName(notificationsAppImplementation.data!.id),
icon: NeonAppImplementationIcon(
appImplementation: notificationsAppImplementation.data!,
unreadCount: unreadCount,
color: unreadCount > 0
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onBackground,
size: const Size.square(kAvatarSize * 2 / 3),
),
);
},
), ),
], ],
], IconButton(
), onPressed: () {
actions: [ AccountSettingsRoute(accountid: account.id).go(context);
if (notificationsAppImplementation.data != null) ...[
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}'),
onPressed: () async {
await _openNotifications(
notificationsAppImplementation.data!,
accounts,
account,
);
},
tooltip: AppLocalizations.of(context)
.appImplementationName(notificationsAppImplementation.data!.id),
icon: NeonAppImplementationIcon(
appImplementation: notificationsAppImplementation.data!,
unreadCount: unreadCount,
color: unreadCount > 0
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.onBackground,
size: const Size.square(kAvatarSize * 2 / 3),
),
);
}, },
), tooltip: AppLocalizations.of(context).settingsAccount,
], icon: IntrinsicWidth(
IconButton( child: NeonAccountAvatar(
onPressed: () { account: account,
AccountSettingsRoute(accountid: account.id).go(context); ),
},
tooltip: AppLocalizations.of(context).settingsAccount,
icon: IntrinsicWidth(
child: NeonAccountAvatar(
account: account,
), ),
), ),
),
],
),
body: Row(
children: [
if (navigationMode == NavigationMode.quickBar) ...[
drawer,
], ],
Expanded( ),
child: Column( body: Row(
children: [ children: [
if (appImplementations.data != null) ...[ if (navigationMode == NavigationMode.quickBar) ...[
if (appImplementations.data!.isEmpty) ...[ drawer,
Expanded( ],
child: Center( Expanded(
child: Text( child: Column(
AppLocalizations.of(context).errorNoCompatibleNextcloudAppsFound, children: [
textAlign: TextAlign.center, if (appImplementations.data != null) ...[
), if (appImplementations.data!.isEmpty) ...[
),
),
] else ...[
if (activeAppIDSnapshot.hasData) ...[
Expanded( Expanded(
child: appImplementations.data! child: Center(
.find(activeAppIDSnapshot.data!)! child: Text(
.buildPage(context, _appsBloc), AppLocalizations.of(context)
.errorNoCompatibleNextcloudAppsFound,
textAlign: TextAlign.center,
),
),
), ),
] else ...[
if (activeAppIDSnapshot.hasData) ...[
Expanded(
child: appImplementations.data!
.find(activeAppIDSnapshot.data!)!
.buildPage(context, _appsBloc),
),
],
], ],
], ],
], ],
], ),
), ),
), ],
], ),
), ),
), ),
), ],
], );
); }
} }
return Container(); return Container();
}, },

Loading…
Cancel
Save