Channel content auto refresh after long time background activity

This commit is contained in:
LittleSheep 2024-08-10 00:43:55 +08:00
parent 2356eac118
commit 9910fc7a92
3 changed files with 109 additions and 71 deletions

View File

@ -20,7 +20,6 @@ import 'package:solian/widgets/app_bar_leading.dart';
import 'package:solian/widgets/app_bar_title.dart'; import 'package:solian/widgets/app_bar_title.dart';
import 'package:solian/widgets/channel/channel_call_indicator.dart'; import 'package:solian/widgets/channel/channel_call_indicator.dart';
import 'package:solian/widgets/chat/call/chat_call_action.dart'; import 'package:solian/widgets/chat/call/chat_call_action.dart';
import 'package:solian/widgets/chat/chat_event.dart';
import 'package:solian/widgets/chat/chat_event_list.dart'; import 'package:solian/widgets/chat/chat_event_list.dart';
import 'package:solian/widgets/chat/chat_message_input.dart'; import 'package:solian/widgets/chat/chat_message_input.dart';
import 'package:solian/widgets/current_state_action.dart'; import 'package:solian/widgets/current_state_action.dart';
@ -39,7 +38,10 @@ class ChannelChatScreen extends StatefulWidget {
State<ChannelChatScreen> createState() => _ChannelChatScreenState(); State<ChannelChatScreen> createState() => _ChannelChatScreenState();
} }
class _ChannelChatScreenState extends State<ChannelChatScreen> { class _ChannelChatScreenState extends State<ChannelChatScreen>
with WidgetsBindingObserver {
DateTime? _isOutOfSyncSince;
bool _isBusy = false; bool _isBusy = false;
int? _accountId; int? _accountId;
@ -123,20 +125,38 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
}); });
} }
void _keepUpdateWithServer() {
_getOngoingCall();
_chatController.getEvents(_channel!, widget.realm);
setState(() => _isOutOfSyncSince = null);
}
Event? _messageToReplying; Event? _messageToReplying;
Event? _messageToEditing; Event? _messageToEditing;
Widget buildHistoryBody(Event item, {bool isMerged = false}) { @override
return ChatEvent( void didChangeAppLifecycleState(AppLifecycleState state) {
key: Key('m${item.uuid}'), switch (state) {
item: item, case AppLifecycleState.resumed:
isMerged: isMerged, if (_isOutOfSyncSince == null) break;
chatController: _chatController, if (DateTime.now().difference(_isOutOfSyncSince!).inSeconds < 60) break;
); _keepUpdateWithServer();
break;
case AppLifecycleState.paused:
if (mounted) {
setState(() => _isOutOfSyncSince = DateTime.now());
}
break;
default:
break;
}
} }
@override @override
void initState() { void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_accountId = Get.find<AuthProvider>().userProfile.value!['id']; _accountId = Get.find<AuthProvider>().userProfile.value!['id'];
_chatController = ChatEventController(); _chatController = ChatEventController();
@ -147,21 +167,10 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
_chatController.getEvents(_channel!, widget.realm); _chatController.getEvents(_channel!, widget.realm);
_listenMessages(); _listenMessages();
}); });
super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (_isBusy || _channel == null) {
return Material(
color: Theme.of(context).colorScheme.surface,
child: const Center(
child: CircularProgressIndicator(),
),
);
}
String title = _channel?.name ?? 'loading'.tr; String title = _channel?.name ?? 'loading'.tr;
String? placeholder; String? placeholder;
@ -185,7 +194,8 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
actions: [ actions: [
const BackgroundStateWidget(), const BackgroundStateWidget(),
Builder(builder: (context) { Builder(builder: (context) {
if (_isBusy) return const SizedBox(); if (_isBusy || _channel == null) return const SizedBox();
return ChatCallButton( return ChatCallButton(
realm: _channel!.realm, realm: _channel!.realm,
channel: _channel!, channel: _channel!,
@ -195,6 +205,8 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
IconButton( IconButton(
icon: const Icon(Icons.more_vert), icon: const Icon(Icons.more_vert),
onPressed: () { onPressed: () {
if (_channel == null) return;
AppRouter.instance AppRouter.instance
.pushNamed( .pushNamed(
'channelDetail', 'channelDetail',
@ -219,66 +231,87 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
), ),
], ],
), ),
body: Column( body: Builder(builder: (context) {
children: [ if (_isBusy || _channel == null) {
if (_ongoingCall != null) return const Center(
ChannelCallIndicator( child: CircularProgressIndicator(),
channel: _channel!, );
ongoingCall: _ongoingCall!, }
return Column(
children: [
if (_ongoingCall != null)
ChannelCallIndicator(
channel: _channel!,
ongoingCall: _ongoingCall!,
),
Expanded(
child: ChatEventList(
scope: widget.realm,
channel: _channel!,
chatController: _chatController,
onEdit: (item) {
setState(() => _messageToEditing = item);
},
onReply: (item) {
setState(() => _messageToReplying = item);
},
),
), ),
Expanded( if (_isOutOfSyncSince != null)
child: ChatEventList( ListTile(
scope: widget.realm, tileColor: Theme.of(context).colorScheme.surfaceContainerLow,
channel: _channel!, leading: const Icon(Icons.history_toggle_off),
chatController: _chatController, title: Text('messageOutOfSync'.tr),
onEdit: (item) { subtitle: Text('messageOutOfSyncCaption'.tr),
setState(() => _messageToEditing = item); onTap: _isBusy
}, ? null
onReply: (item) { : () {
setState(() => _messageToReplying = item); _keepUpdateWithServer();
}, },
), ),
), Obx(() {
Obx(() { if (_chatController.isLoading.isTrue) {
if (_chatController.isLoading.isTrue) { return const LinearProgressIndicator().animate().slideY();
return const LinearProgressIndicator().animate().slideY(); } else {
} else { return const SizedBox();
return const SizedBox(); }
} }),
}), ClipRect(
ClipRect( child: BackdropFilter(
child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50),
filter: ImageFilter.blur(sigmaX: 50, sigmaY: 50), child: SafeArea(
child: SafeArea( child: ChatMessageInput(
child: ChatMessageInput( edit: _messageToEditing,
edit: _messageToEditing, reply: _messageToReplying,
reply: _messageToReplying, realm: widget.realm,
realm: widget.realm, placeholder: placeholder,
placeholder: placeholder, channel: _channel!,
channel: _channel!, onSent: (Event item) {
onSent: (Event item) { setState(() {
setState(() { _chatController.addPendingEvent(item);
_chatController.addPendingEvent(item); });
}); },
}, onReset: () {
onReset: () { setState(() {
setState(() { _messageToReplying = null;
_messageToReplying = null; _messageToEditing = null;
_messageToEditing = null; });
}); },
}, ),
), ),
), ),
), ),
), ],
], );
), }),
); );
} }
@override @override
void dispose() { void dispose() {
_subscription?.cancel(); _subscription?.cancel();
WidgetsBinding.instance.removeObserver(this);
super.dispose(); super.dispose();
} }
} }

View File

@ -370,4 +370,7 @@ const i18nEnglish = {
'callStatusDisconnected': 'Disconnected', 'callStatusDisconnected': 'Disconnected',
'callStatusConnecting': 'Connecting', 'callStatusConnecting': 'Connecting',
'callStatusReconnected': 'Reconnecting', 'callStatusReconnected': 'Reconnecting',
'messageOutOfSync': 'May Out of Sync with Server',
'messageOutOfSyncCaption':
'Since the App has entered the background, there may be a time difference between the message list and the server. Click to Refresh.',
}; };

View File

@ -337,4 +337,6 @@ const i18nSimplifiedChinese = {
'callStatusDisconnected': '已断开', 'callStatusDisconnected': '已断开',
'callStatusConnecting': '连接中', 'callStatusConnecting': '连接中',
'callStatusReconnected': '重连中', 'callStatusReconnected': '重连中',
'messageOutOfSync': '消息可能与服务器脱节',
'messageOutOfSyncCaption': '由于 App 进入后台,消息列表可能与服务器存在时差,点击刷新。',
}; };