|
|
@ -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(); |
|
|
|
}, |
|
|
|
}, |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
), |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|