2024-02-18 17:36:58 +01:00
import ' package:flutter/material.dart ' ;
2024-07-12 23:08:56 +02:00
import ' package:font_awesome_flutter/font_awesome_flutter.dart ' ;
2024-02-18 17:36:58 +01:00
import ' package:infinite_scroll_pagination/infinite_scroll_pagination.dart ' ;
import ' package:provider/provider.dart ' ;
import ' package:simplecloudnotifier/api/api_client.dart ' ;
import ' package:simplecloudnotifier/models/channel.dart ' ;
2024-06-15 16:33:30 +02:00
import ' package:simplecloudnotifier/state/app_bar_state.dart ' ;
2024-05-26 00:20:25 +02:00
import ' package:simplecloudnotifier/state/application_log.dart ' ;
2024-06-02 17:09:57 +02:00
import ' package:simplecloudnotifier/state/app_auth.dart ' ;
2024-05-25 22:06:43 +02:00
import ' package:simplecloudnotifier/pages/channel_list/channel_list_item.dart ' ;
2024-06-25 12:00:34 +02:00
import ' package:simplecloudnotifier/utils/navi.dart ' ;
2024-02-18 17:36:58 +01:00
class ChannelRootPage extends StatefulWidget {
2024-06-15 15:56:50 +02:00
const ChannelRootPage ( { super . key , required this . isVisiblePage } ) ;
final bool isVisiblePage ;
2024-02-18 17:36:58 +01:00
@ override
State < ChannelRootPage > createState ( ) = > _ChannelRootPageState ( ) ;
}
2024-06-26 14:54:34 +02:00
class _ChannelRootPageState extends State < ChannelRootPage > with RouteAware {
2024-06-25 12:00:34 +02:00
final PagingController < int , ChannelWithSubscription > _pagingController = PagingController . fromValue ( PagingState ( nextPageKey: null , itemList: [ ] , error: null ) , firstPageKey: 0 ) ;
2024-06-15 15:56:50 +02:00
bool _isInitialized = false ;
2024-02-18 17:36:58 +01:00
2024-06-26 14:54:34 +02:00
bool _reloadEnqueued = false ;
2024-02-18 17:36:58 +01:00
@ override
void initState ( ) {
super . initState ( ) ;
2024-06-15 15:56:50 +02:00
_pagingController . addPageRequestListener ( _fetchPage ) ;
2024-06-15 16:33:30 +02:00
if ( widget . isVisiblePage & & ! _isInitialized ) _realInitState ( ) ;
2024-02-18 17:36:58 +01:00
}
2024-06-26 14:54:34 +02:00
@ override
void didChangeDependencies ( ) {
super . didChangeDependencies ( ) ;
Navi . modalRouteObserver . subscribe ( this , ModalRoute . of ( context ) ! ) ;
}
2024-02-18 17:36:58 +01:00
@ override
void dispose ( ) {
2024-06-15 16:33:30 +02:00
ApplicationLog . debug ( ' ChannelRootPage::dispose ' ) ;
2024-02-18 17:36:58 +01:00
_pagingController . dispose ( ) ;
2024-06-26 14:54:34 +02:00
Navi . modalRouteObserver . unsubscribe ( this ) ;
2024-02-18 17:36:58 +01:00
super . dispose ( ) ;
}
2024-06-15 15:56:50 +02:00
@ override
void didUpdateWidget ( ChannelRootPage oldWidget ) {
super . didUpdateWidget ( oldWidget ) ;
if ( oldWidget . isVisiblePage ! = widget . isVisiblePage & & widget . isVisiblePage ) {
if ( ! _isInitialized ) {
2024-06-15 16:33:30 +02:00
_realInitState ( ) ;
2024-06-15 15:56:50 +02:00
} else {
2024-06-15 16:33:30 +02:00
_backgroundRefresh ( ) ;
2024-06-15 15:56:50 +02:00
}
}
}
2024-06-26 14:54:34 +02:00
@ override
void didPush ( ) {
// ...
}
@ override
void didPopNext ( ) {
if ( _reloadEnqueued ) {
ApplicationLog . debug ( ' [ChannelList::RouteObserver] --> didPopNext (will background-refresh) (_reloadEnqueued == true) ' ) ;
( ) async {
_reloadEnqueued = false ;
AppBarState ( ) . setLoadingIndeterminate ( true ) ;
2024-09-19 19:46:46 +02:00
await Future . delayed ( const Duration ( milliseconds: 500 ) , ( ) { } ) ; // prevents flutter bug where the whole process crashes ?!?
2024-06-26 14:54:34 +02:00
await _backgroundRefresh ( ) ;
} ( ) ;
}
}
2024-06-15 16:33:30 +02:00
void _realInitState ( ) {
ApplicationLog . debug ( ' ChannelRootPage::_realInitState ' ) ;
2024-06-15 15:56:50 +02:00
_pagingController . refresh ( ) ;
_isInitialized = true ;
}
2024-02-18 17:36:58 +01:00
Future < void > _fetchPage ( int pageKey ) async {
2024-06-02 17:09:57 +02:00
final acc = Provider . of < AppAuth > ( context , listen: false ) ;
2024-02-18 17:36:58 +01:00
2024-06-15 15:56:50 +02:00
ApplicationLog . debug ( ' Start ChannelList::_pagingController::_fetchPage [ ${ pageKey } ] ' ) ;
2024-06-02 17:09:57 +02:00
if ( ! acc . isAuth ( ) ) {
2024-02-18 17:36:58 +01:00
_pagingController . error = ' Not logged in ' ;
return ;
}
try {
2024-06-25 12:00:34 +02:00
final items = ( await APIClient . getChannelList ( acc , ChannelSelector . all ) ) . toList ( ) ;
2024-02-18 17:36:58 +01:00
2024-06-25 12:00:34 +02:00
items . sort ( ( a , b ) = > - 1 * ( a . channel . timestampLastSent ? ? ' ' ) . compareTo ( b . channel . timestampLastSent ? ? ' ' ) ) ;
2024-05-26 19:24:19 +02:00
2024-06-15 16:33:30 +02:00
_pagingController . value = PagingState ( nextPageKey: null , itemList: items , error: null ) ;
2024-05-26 00:20:25 +02:00
} catch ( exc , trace ) {
_pagingController . error = exc . toString ( ) ;
ApplicationLog . error ( ' Failed to list channels: ' + exc . toString ( ) , trace: trace ) ;
2024-02-18 17:36:58 +01:00
}
}
2024-06-15 16:33:30 +02:00
Future < void > _backgroundRefresh ( ) async {
final acc = Provider . of < AppAuth > ( context , listen: false ) ;
ApplicationLog . debug ( ' Start background refresh of channel list ' ) ;
if ( ! acc . isAuth ( ) ) {
_pagingController . error = ' Not logged in ' ;
return ;
}
try {
await Future . delayed ( const Duration ( seconds: 0 ) , ( ) { } ) ; // this is annoyingly important - otherwise we call setLoadingIndeterminate directly in initStat() and get an exception....
AppBarState ( ) . setLoadingIndeterminate ( true ) ;
2024-06-25 12:00:34 +02:00
final items = ( await APIClient . getChannelList ( acc , ChannelSelector . all ) ) . toList ( ) ;
2024-06-15 16:33:30 +02:00
2024-06-25 12:00:34 +02:00
items . sort ( ( a , b ) = > - 1 * ( a . channel . timestampLastSent ? ? ' ' ) . compareTo ( b . channel . timestampLastSent ? ? ' ' ) ) ;
2024-06-15 16:33:30 +02:00
2024-06-26 14:54:34 +02:00
setState ( ( ) {
_pagingController . value = PagingState ( nextPageKey: null , itemList: items , error: null ) ;
} ) ;
2024-06-15 16:33:30 +02:00
} catch ( exc , trace ) {
2024-06-26 14:54:34 +02:00
setState ( ( ) {
_pagingController . error = exc . toString ( ) ;
} ) ;
2024-06-15 16:33:30 +02:00
ApplicationLog . error ( ' Failed to list channels: ' + exc . toString ( ) , trace: trace ) ;
} finally {
AppBarState ( ) . setLoadingIndeterminate ( false ) ;
}
}
2024-02-18 17:36:58 +01:00
@ override
Widget build ( BuildContext context ) {
2024-07-12 23:08:56 +02:00
return Scaffold (
body: RefreshIndicator (
onRefresh: ( ) = > Future . sync (
( ) = > _pagingController . refresh ( ) ,
) ,
child: PagedListView < int , ChannelWithSubscription > (
pagingController: _pagingController ,
builderDelegate: PagedChildBuilderDelegate < ChannelWithSubscription > (
itemBuilder: ( context , item , index ) = > ChannelListItem (
channel: item . channel ,
subscription: item . subscription ,
2024-10-19 19:42:05 +02:00
mode: ChannelListItemMode . Messages ,
onChannelListReloadTrigger: _enqueueReload ,
onSubscriptionChanged: ( channelID , subscription ) {
setState ( ( ) {
final idx = _pagingController . itemList ? . indexWhere ( ( p ) = > p . channel . channelID = = channelID ) ;
if ( idx ! = null & & idx > = 0 ) _pagingController . itemList ! [ idx ] = ChannelWithSubscription ( channel: _pagingController . itemList ! [ idx ] . channel , subscription: subscription ) ;
} ) ;
2024-07-12 23:08:56 +02:00
} ,
) ,
2024-05-26 19:24:19 +02:00
) ,
2024-02-18 17:36:58 +01:00
) ,
) ,
2024-07-12 23:08:56 +02:00
floatingActionButton: FloatingActionButton (
2024-07-13 00:11:13 +02:00
heroTag: ' fab_channel_list_qr ' ,
2024-07-12 23:08:56 +02:00
onPressed: ( ) {
//TODO scan qr code to subscribe channel
} ,
child: const Icon ( FontAwesomeIcons . qrcode ) ,
) ,
2024-02-18 17:36:58 +01:00
) ;
}
2024-06-26 14:54:34 +02:00
void _enqueueReload ( ) {
_reloadEnqueued = true ;
}
2024-02-18 17:36:58 +01:00
}