♻️ 使用 SQLITE 来存储本地消息记录 #1
@ -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);
|
||||||
},
|
},
|
||||||
|
@ -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,
|
||||||
|
@ -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),
|
||||||
|
Loading…
Reference in New Issue
Block a user