♻️ 使用 SQLITE 来存储本地消息记录 #1

Merged
LittleSheep merged 6 commits from features/local-message-history into master 2024-06-23 11:13:42 +00:00
3 changed files with 61 additions and 32 deletions
Showing only changes of commit 2038d33a31 - Show all commits

View File

@ -32,13 +32,14 @@ extension MessageHistoryHelper on MessageHistoryDb {
await localMessages.delete(id); await localMessages.delete(id);
} }
syncMessages(Channel channel, {String scope = 'global'}) async { syncMessages(Channel channel, {String scope = 'global', offset = 0}) async {
final lastOne = await localMessages.findLastByChannel(channel.id); final lastOne = await localMessages.findLastByChannel(channel.id);
final data = await _getRemoteMessages( final data = await _getRemoteMessages(
channel, channel,
scope, scope,
remainBreath: 5, remainBreath: 3,
offset: offset,
onBrake: (items) { onBrake: (items) {
return items.any((x) => x.id == lastOne?.id); return items.any((x) => x.id == lastOne?.id);
}, },

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:solian/exts.dart'; import 'package:solian/exts.dart';
import 'package:solian/models/call.dart'; import 'package:solian/models/call.dart';
@ -40,16 +41,20 @@ class ChannelChatScreen extends StatefulWidget {
} }
class _ChannelChatScreenState extends State<ChannelChatScreen> { class _ChannelChatScreenState extends State<ChannelChatScreen> {
final _chatScrollController = ScrollController();
bool _isBusy = false; bool _isBusy = false;
bool _isLoadingMore = false;
int? _accountId; int? _accountId;
String? _overrideAlias; String? _overrideAlias;
Channel? _channel; Channel? _channel;
ChannelMember? _channelProfile;
Call? _ongoingCall; Call? _ongoingCall;
ChannelMember? _channelProfile;
StreamSubscription<NetworkPackage>? _subscription; StreamSubscription<NetworkPackage>? _subscription;
int _nextHistorySyncOffset = 0;
MessageHistoryDb? _db; MessageHistoryDb? _db;
List<LocalMessage> _currentHistory = List.empty(); List<LocalMessage> _currentHistory = List.empty();
@ -107,13 +112,19 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
} }
Future<void> getMessages() async { Future<void> getMessages() async {
await _db!.syncMessages(_channel!, scope: widget.realm); await _db!.syncMessages(
_channel!,
scope: widget.realm,
offset: _nextHistorySyncOffset,
);
await syncHistory(); await syncHistory();
} }
Future<void> syncHistory() async { Future<void> syncHistory() async {
_currentHistory = await _db!.localMessages.findAllByChannel(_channel!.id); final data = await _db!.localMessages.findAllByChannel(_channel!.id);
setState(() {}); setState(() {
_currentHistory = data;
});
} }
void listenMessages() { void listenMessages() {
@ -171,6 +182,31 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
Message? _messageToReplying; Message? _messageToReplying;
Message? _messageToEditing; Message? _messageToEditing;
Widget buildHistoryBody(Message item, {bool isMerged = false}) {
if (item.replyTo != null) {
return Column(
children: [
ChatMessage(
key: Key('m${item.replyTo!.uuid}'),
item: item.replyTo!,
isReply: true,
).paddingOnly(left: 24, right: 4, bottom: 2),
ChatMessage(
key: Key('m${item.uuid}'),
item: item,
isMerged: isMerged,
),
],
);
}
return ChatMessage(
key: Key('m${item.uuid}'),
item: item,
isMerged: isMerged,
);
}
Widget buildHistory(context, index) { Widget buildHistory(context, index) {
bool isMerged = false, hasMerged = false; bool isMerged = false, hasMerged = false;
if (index > 0) { if (index > 0) {
@ -188,33 +224,9 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
final item = _currentHistory[index].data; final item = _currentHistory[index].data;
Widget content;
if (item.replyTo != null) {
content = Column(
children: [
ChatMessage(
key: Key('m${item.replyTo!.uuid}'),
item: item.replyTo!,
isReply: true,
).paddingOnly(left: 24, right: 4, bottom: 2),
ChatMessage(
key: Key('m${item.uuid}'),
item: item,
isMerged: isMerged,
),
],
);
} else {
content = ChatMessage(
key: Key('m${item.uuid}'),
item: item,
isMerged: isMerged,
);
}
return InkWell( return InkWell(
child: Container( child: Container(
child: content.paddingOnly( child: buildHistoryBody(item, isMerged: isMerged).paddingOnly(
top: !isMerged ? 8 : 0, top: !isMerged ? 8 : 0,
bottom: !hasMerged ? 8 : 0, bottom: !hasMerged ? 8 : 0,
), ),
@ -241,6 +253,16 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
@override @override
void initState() { void initState() {
_chatScrollController.addListener(() async {
if (_chatScrollController.position.pixels ==
_chatScrollController.position.maxScrollExtent) {
setState(() => _isLoadingMore = true);
_nextHistorySyncOffset = _currentHistory.length;
await getMessages();
setState(() => _isLoadingMore = false);
}
});
createHistoryDb().then((db) async { createHistoryDb().then((db) async {
_db = db; _db = db;
@ -330,8 +352,14 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
children: [ children: [
Column( Column(
children: [ children: [
if (_isLoadingMore)
const LinearProgressIndicator()
.paddingOnly(bottom: 4)
.animate()
.slideY(),
Expanded( Expanded(
child: ListView.builder( child: ListView.builder(
controller: _chatScrollController,
itemCount: _currentHistory.length, itemCount: _currentHistory.length,
clipBehavior: Clip.none, clipBehavior: Clip.none,
reverse: true, reverse: true,

View File

@ -101,7 +101,7 @@ class _ChatScreenState extends State<ChatScreen> {
context), context),
sliver: SliverAppBar( sliver: SliverAppBar(
title: AppBarTitle('chat'.tr), title: AppBarTitle('chat'.tr),
centerTitle: true, centerTitle: false,
floating: true, floating: true,
titleSpacing: SolianTheme.titleSpacing(context), titleSpacing: SolianTheme.titleSpacing(context),
toolbarHeight: SolianTheme.toolbarHeight(context), toolbarHeight: SolianTheme.toolbarHeight(context),