diff --git a/lib/controls/timeline/timeline_panel.dart b/lib/controls/timeline/timeline_panel.dart index 8d58ae4..da24552 100644 --- a/lib/controls/timeline/timeline_panel.dart +++ b/lib/controls/timeline/timeline_panel.dart @@ -20,40 +20,35 @@ class TimelinePanel extends StatelessWidget { return Center(child: Text('Error getting timeline: ${result.error}')); } final items = result.value; - return RefreshIndicator( - onRefresh: () async { - await manager.updateTimeline(timeline, TimelineRefreshType.refresh); - }, - child: ListView.separated( - itemBuilder: (context, index) { - if (index == 0) { - return TextButton( - onPressed: () async => await manager.updateTimeline( - timeline, TimelineRefreshType.loadNewer), - child: const Text('Load newer posts')); - } + return ListView.separated( + itemBuilder: (context, index) { + if (index == 0) { + return TextButton( + onPressed: () async => await manager.updateTimeline( + timeline, TimelineRefreshType.loadNewer), + child: const Text('Load newer posts')); + } - if (index == items.length + 1) { - return TextButton( - onPressed: () async => await manager.updateTimeline( - timeline, TimelineRefreshType.loadOlder), - child: const Text('Load older posts')); - } - final itemIndex = index - 1; - final item = items[itemIndex]; - _logger.finest( - 'Building item: $itemIndex: ${item.entry.toShortString()}'); - return PostControl( - originalItem: item, - scrollToId: item.id, - openRemote: false, - showStatusOpenButton: true, - isRoot: false, - ); - }, - separatorBuilder: (context, index) => Divider(), - itemCount: items.length + 2, - ), + if (index == items.length + 1) { + return TextButton( + onPressed: () async => await manager.updateTimeline( + timeline, TimelineRefreshType.loadOlder), + child: const Text('Load older posts')); + } + final itemIndex = index - 1; + final item = items[itemIndex]; + _logger + .finest('Building item: $itemIndex: ${item.entry.toShortString()}'); + return PostControl( + originalItem: item, + scrollToId: item.id, + openRemote: false, + showStatusOpenButton: true, + isRoot: false, + ); + }, + separatorBuilder: (context, index) => Divider(), + itemCount: items.length + 2, ); } } diff --git a/lib/friendica_client/friendica_client.dart b/lib/friendica_client/friendica_client.dart index 5f3ab70..de9ffb8 100644 --- a/lib/friendica_client/friendica_client.dart +++ b/lib/friendica_client/friendica_client.dart @@ -243,21 +243,25 @@ class FriendicaClient { FutureResult, ExecError> getUserTimeline( {String userId = '', int page = 1, int count = 10}) async { _logger.finest(() => 'Getting user timeline for $userId'); + _networkStatusService.startTimelineLoading(); final baseUrl = 'https://$serverName/api/statuses/user_timeline?'; final pagingData = 'count=$count&page=$page'; final url = userId.isEmpty ? '$baseUrl$pagingData' : '${baseUrl}screen_name=$userId$pagingData'; final request = Uri.parse(url); - return (await _getApiListRequest(request).andThenSuccessAsync( + final result = (await _getApiListRequest(request).andThenSuccessAsync( (postsJson) async => postsJson.data .map((json) => TimelineEntryMastodonExtensions.fromJson(json)) .toList())) - .mapError((error) => error as ExecError); + .execErrorCast(); + _networkStatusService.finishTimelineLoading(); + return result; } FutureResult, ExecError> getTimeline( {required TimelineIdentifiers type, required PagingData page}) async { + _networkStatusService.startTimelineLoading(); final String timelinePath = _typeToTimelinePath(type); final String timelineQPs = _typeToTimelineQueryParameters(type); final baseUrl = 'https://$serverName/api/v1/$timelinePath'; @@ -265,11 +269,13 @@ class FriendicaClient { '$baseUrl?exclude_replies=true&${page.toQueryParameters()}&$timelineQPs'; final request = Uri.parse(url); _logger.finest(() => 'Getting ${type.toHumanKey()} with paging data $page'); - return (await _getApiListRequest(request).andThenSuccessAsync( + final result = (await _getApiListRequest(request).andThenSuccessAsync( (postsJson) async => postsJson.data .map((json) => TimelineEntryMastodonExtensions.fromJson(json)) .toList())) .execErrorCast(); + _networkStatusService.finishTimelineLoading(); + return result; } FutureResult getFileBytes(Uri url) async { diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 5f4e585..2d3cc37 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -6,8 +6,10 @@ import 'package:provider/provider.dart'; import '../controls/app_bottom_nav_bar.dart'; import '../controls/padding.dart'; import '../controls/timeline/timeline_panel.dart'; +import '../globals.dart'; import '../models/TimelineIdentifiers.dart'; import '../models/group_data.dart'; +import '../services/network_status_service.dart'; import '../services/timeline_manager.dart'; class HomeScreen extends StatefulWidget { @@ -34,11 +36,13 @@ class _HomeScreenState extends State { @override Widget build(BuildContext context) { _logger.finest('Build'); - final groups = context - .watch() - .getGroups() - .getValueOrElse(() => []) - .toList(); + final nss = getIt(); + final manager = context.watch(); + final groups = manager.getGroups().getValueOrElse(() => []).toList(); + final currentTimeline = TimelineIdentifiers( + timeline: currentType, + auxData: currentGroup?.id ?? '', + ); return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).canvasColor, @@ -76,6 +80,30 @@ class _HomeScreenState extends State { ], ), actions: [ + ValueListenableBuilder( + valueListenable: nss.timelineLoadingStatus, + builder: (context2, executing, _) { + if (executing) { + final theme = Theme.of(context); + final size = theme.appBarTheme.actionsIconTheme?.size ?? + theme.iconTheme.size ?? + 24; + return Center( + child: SizedBox( + width: size, + height: size, + child: CircularProgressIndicator(), + ), + ); + } + return IconButton( + onPressed: () async => await manager.updateTimeline( + currentTimeline, TimelineRefreshType.refresh), + icon: Icon( + Icons.refresh, + color: Theme.of(context).textTheme.bodyText1!.color, + )); + }), IconButton( onPressed: () { context.push('/post/new'); @@ -91,10 +119,7 @@ class _HomeScreenState extends State { children: [ Expanded( child: TimelinePanel( - timeline: TimelineIdentifiers( - timeline: currentType, - auxData: currentGroup?.id ?? '', - ), + timeline: currentTimeline, )), ], ), diff --git a/lib/services/network_status_service.dart b/lib/services/network_status_service.dart index d5afa5e..5d8ee6d 100644 --- a/lib/services/network_status_service.dart +++ b/lib/services/network_status_service.dart @@ -1,18 +1,22 @@ import 'package:flutter/foundation.dart'; -import 'package:logging/logging.dart'; class NetworkStatusService { - static final _logger = Logger('$NetworkStatusService'); - final notificationsUpdateStatus = ValueNotifier(false); + final timelineLoadingStatus = ValueNotifier(false); void startNotificationUpdate() { - _logger.info('Setting Notification Update status to true'); notificationsUpdateStatus.value = true; } void finishNotificationUpdate() { - _logger.info('Setting Notification Update status to false'); notificationsUpdateStatus.value = false; } + + void startTimelineLoading() { + timelineLoadingStatus.value = true; + } + + void finishTimelineLoading() { + timelineLoadingStatus.value = false; + } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index a3accbe..88ae243 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -8,7 +8,7 @@ import Foundation import desktop_window import flutter_secure_storage_macos import objectbox_flutter_libs -import path_provider_macos +import path_provider_foundation import shared_preferences_foundation import sqflite import url_launcher_macos