👽 Update follow server side IM changes
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@ -176,9 +177,9 @@ class ChatMessageController extends ChangeNotifier {
|
||||
|
||||
switch (message.type) {
|
||||
case 'messages.edit':
|
||||
final body = message.body;
|
||||
if (body['related_event'] != null) {
|
||||
final idx = messages.indexWhere((x) => x.id == body['related_event']);
|
||||
if (message.relatedEventId != null) {
|
||||
final idx =
|
||||
messages.indexWhere((x) => x.id == message.relatedEventId);
|
||||
if (idx != -1) {
|
||||
final newBody = message.body;
|
||||
newBody.remove('related_event');
|
||||
@ -186,17 +187,16 @@ class ChatMessageController extends ChangeNotifier {
|
||||
body: newBody,
|
||||
updatedAt: message.updatedAt,
|
||||
);
|
||||
if (_box!.containsKey(body['related_event'])) {
|
||||
await _box!.put(body['related_event'], messages[idx]);
|
||||
if (_box!.containsKey(message.relatedEventId)) {
|
||||
await _box!.put(message.relatedEventId, messages[idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
case 'messages.delete':
|
||||
final body = message.body;
|
||||
if (body['related_event'] != null) {
|
||||
messages.removeWhere((x) => x.id == body['related_event']);
|
||||
if (_box!.containsKey(body['related_event'])) {
|
||||
await _box!.delete(body['related_event']);
|
||||
if (message.relatedEventId != null) {
|
||||
messages.removeWhere((x) => x.id == message.relatedEventId);
|
||||
if (_box!.containsKey(message.relatedEventId)) {
|
||||
await _box!.delete(message.relatedEventId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,6 +208,7 @@ class ChatMessageController extends ChangeNotifier {
|
||||
int? quoteId,
|
||||
int? relatedId,
|
||||
List<String>? attachments,
|
||||
SnChatMessage? editingMessage,
|
||||
}) async {
|
||||
if (channel == null) return;
|
||||
const uuid = Uuid();
|
||||
@ -216,7 +217,7 @@ class ChatMessageController extends ChangeNotifier {
|
||||
'text': content,
|
||||
'algorithm': 'plain',
|
||||
if (quoteId != null) 'quote_event': quoteId,
|
||||
if (relatedId != null) 'quote_event': relatedId,
|
||||
if (relatedId != null) 'related_event': relatedId,
|
||||
if (attachments != null && attachments.isNotEmpty)
|
||||
'attachments': attachments,
|
||||
};
|
||||
@ -235,21 +236,40 @@ class ChatMessageController extends ChangeNotifier {
|
||||
sender: profile!,
|
||||
senderId: profile!.id,
|
||||
quoteEventId: quoteId,
|
||||
relatedEventId: relatedId,
|
||||
);
|
||||
_addUnconfirmedMessage(message);
|
||||
|
||||
// Send to server
|
||||
try {
|
||||
await _sn.client.post(
|
||||
'/cgi/im/channels/${channel!.keyPath}/messages',
|
||||
await _sn.client.request(
|
||||
editingMessage != null
|
||||
? '/cgi/im/channels/${channel!.keyPath}/messages/${editingMessage.id}'
|
||||
: '/cgi/im/channels/${channel!.keyPath}/messages',
|
||||
data: {
|
||||
'type': type,
|
||||
'uuid': nonce,
|
||||
'body': body,
|
||||
},
|
||||
options: Options(
|
||||
method: editingMessage != null ? 'PUT' : 'POST',
|
||||
),
|
||||
);
|
||||
} catch (err) {
|
||||
print(err);
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteMessage(SnChatMessage message) async {
|
||||
if (message.channelId != channel?.id) return;
|
||||
|
||||
try {
|
||||
await _sn.client.delete(
|
||||
'/cgi/im/channels/${channel!.keyPath}/messages/${message.id}',
|
||||
);
|
||||
messages.removeWhere((x) => x.id == message.id);
|
||||
} catch (err) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
@ -376,7 +396,11 @@ class ChatMessageController extends ChangeNotifier {
|
||||
}
|
||||
|
||||
// Preload sender accounts
|
||||
await _ud.listAccount(out.map((ele) => ele.sender.accountId).toSet());
|
||||
final accountId = out
|
||||
.where((ele) => ele.sender.accountId >= 0)
|
||||
.map((ele) => ele.sender.accountId)
|
||||
.toSet();
|
||||
await _ud.listAccount(accountId);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -121,8 +121,12 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
||||
onReply: (value) {
|
||||
_inputGlobalKey.currentState?.setReply(value);
|
||||
},
|
||||
onEdit: (value) {},
|
||||
onDelete: (value) {},
|
||||
onEdit: (value) {
|
||||
_inputGlobalKey.currentState?.setEdit(value);
|
||||
},
|
||||
onDelete: (value) {
|
||||
_inputGlobalKey.currentState?.deleteMessage(value);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -74,13 +74,14 @@ class SnChatMessage with _$SnChatMessage {
|
||||
@HiveField(2) required DateTime updatedAt,
|
||||
@HiveField(3) required DateTime? deletedAt,
|
||||
@HiveField(4) required String uuid,
|
||||
@HiveField(5) required Map<String, dynamic> body,
|
||||
@HiveField(5) @Default({}) Map<String, dynamic> body,
|
||||
@HiveField(6) required String type,
|
||||
@HiveField(7) required SnChannel channel,
|
||||
@HiveField(8) required SnChannelMember sender,
|
||||
@HiveField(9) required int channelId,
|
||||
@HiveField(10) required int senderId,
|
||||
@HiveField(11) required int? quoteEventId,
|
||||
@HiveField(12) required int? relatedEventId,
|
||||
SnChatMessagePreload? preload,
|
||||
}) = _SnChatMessage;
|
||||
|
||||
|
@ -1085,6 +1085,8 @@ mixin _$SnChatMessage {
|
||||
int get senderId => throw _privateConstructorUsedError;
|
||||
@HiveField(11)
|
||||
int? get quoteEventId => throw _privateConstructorUsedError;
|
||||
@HiveField(12)
|
||||
int? get relatedEventId => throw _privateConstructorUsedError;
|
||||
SnChatMessagePreload? get preload => throw _privateConstructorUsedError;
|
||||
|
||||
/// Serializes this SnChatMessage to a JSON map.
|
||||
@ -1116,6 +1118,7 @@ abstract class $SnChatMessageCopyWith<$Res> {
|
||||
@HiveField(9) int channelId,
|
||||
@HiveField(10) int senderId,
|
||||
@HiveField(11) int? quoteEventId,
|
||||
@HiveField(12) int? relatedEventId,
|
||||
SnChatMessagePreload? preload});
|
||||
|
||||
$SnChannelCopyWith<$Res> get channel;
|
||||
@ -1150,6 +1153,7 @@ class _$SnChatMessageCopyWithImpl<$Res, $Val extends SnChatMessage>
|
||||
Object? channelId = null,
|
||||
Object? senderId = null,
|
||||
Object? quoteEventId = freezed,
|
||||
Object? relatedEventId = freezed,
|
||||
Object? preload = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
@ -1201,6 +1205,10 @@ class _$SnChatMessageCopyWithImpl<$Res, $Val extends SnChatMessage>
|
||||
? _value.quoteEventId
|
||||
: quoteEventId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
relatedEventId: freezed == relatedEventId
|
||||
? _value.relatedEventId
|
||||
: relatedEventId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
preload: freezed == preload
|
||||
? _value.preload
|
||||
: preload // ignore: cast_nullable_to_non_nullable
|
||||
@ -1264,6 +1272,7 @@ abstract class _$$SnChatMessageImplCopyWith<$Res>
|
||||
@HiveField(9) int channelId,
|
||||
@HiveField(10) int senderId,
|
||||
@HiveField(11) int? quoteEventId,
|
||||
@HiveField(12) int? relatedEventId,
|
||||
SnChatMessagePreload? preload});
|
||||
|
||||
@override
|
||||
@ -1299,6 +1308,7 @@ class __$$SnChatMessageImplCopyWithImpl<$Res>
|
||||
Object? channelId = null,
|
||||
Object? senderId = null,
|
||||
Object? quoteEventId = freezed,
|
||||
Object? relatedEventId = freezed,
|
||||
Object? preload = freezed,
|
||||
}) {
|
||||
return _then(_$SnChatMessageImpl(
|
||||
@ -1350,6 +1360,10 @@ class __$$SnChatMessageImplCopyWithImpl<$Res>
|
||||
? _value.quoteEventId
|
||||
: quoteEventId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
relatedEventId: freezed == relatedEventId
|
||||
? _value.relatedEventId
|
||||
: relatedEventId // ignore: cast_nullable_to_non_nullable
|
||||
as int?,
|
||||
preload: freezed == preload
|
||||
? _value.preload
|
||||
: preload // ignore: cast_nullable_to_non_nullable
|
||||
@ -1368,13 +1382,14 @@ class _$SnChatMessageImpl extends _SnChatMessage {
|
||||
@HiveField(2) required this.updatedAt,
|
||||
@HiveField(3) required this.deletedAt,
|
||||
@HiveField(4) required this.uuid,
|
||||
@HiveField(5) required final Map<String, dynamic> body,
|
||||
@HiveField(5) final Map<String, dynamic> body = const {},
|
||||
@HiveField(6) required this.type,
|
||||
@HiveField(7) required this.channel,
|
||||
@HiveField(8) required this.sender,
|
||||
@HiveField(9) required this.channelId,
|
||||
@HiveField(10) required this.senderId,
|
||||
@HiveField(11) required this.quoteEventId,
|
||||
@HiveField(12) required this.relatedEventId,
|
||||
this.preload})
|
||||
: _body = body,
|
||||
super._();
|
||||
@ -1399,6 +1414,7 @@ class _$SnChatMessageImpl extends _SnChatMessage {
|
||||
final String uuid;
|
||||
final Map<String, dynamic> _body;
|
||||
@override
|
||||
@JsonKey()
|
||||
@HiveField(5)
|
||||
Map<String, dynamic> get body {
|
||||
if (_body is EqualUnmodifiableMapView) return _body;
|
||||
@ -1425,11 +1441,14 @@ class _$SnChatMessageImpl extends _SnChatMessage {
|
||||
@HiveField(11)
|
||||
final int? quoteEventId;
|
||||
@override
|
||||
@HiveField(12)
|
||||
final int? relatedEventId;
|
||||
@override
|
||||
final SnChatMessagePreload? preload;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnChatMessage(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, uuid: $uuid, body: $body, type: $type, channel: $channel, sender: $sender, channelId: $channelId, senderId: $senderId, quoteEventId: $quoteEventId, preload: $preload)';
|
||||
return 'SnChatMessage(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, uuid: $uuid, body: $body, type: $type, channel: $channel, sender: $sender, channelId: $channelId, senderId: $senderId, quoteEventId: $quoteEventId, relatedEventId: $relatedEventId, preload: $preload)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -1455,6 +1474,8 @@ class _$SnChatMessageImpl extends _SnChatMessage {
|
||||
other.senderId == senderId) &&
|
||||
(identical(other.quoteEventId, quoteEventId) ||
|
||||
other.quoteEventId == quoteEventId) &&
|
||||
(identical(other.relatedEventId, relatedEventId) ||
|
||||
other.relatedEventId == relatedEventId) &&
|
||||
(identical(other.preload, preload) || other.preload == preload));
|
||||
}
|
||||
|
||||
@ -1474,6 +1495,7 @@ class _$SnChatMessageImpl extends _SnChatMessage {
|
||||
channelId,
|
||||
senderId,
|
||||
quoteEventId,
|
||||
relatedEventId,
|
||||
preload);
|
||||
|
||||
/// Create a copy of SnChatMessage
|
||||
@ -1499,13 +1521,14 @@ abstract class _SnChatMessage extends SnChatMessage {
|
||||
@HiveField(2) required final DateTime updatedAt,
|
||||
@HiveField(3) required final DateTime? deletedAt,
|
||||
@HiveField(4) required final String uuid,
|
||||
@HiveField(5) required final Map<String, dynamic> body,
|
||||
@HiveField(5) final Map<String, dynamic> body,
|
||||
@HiveField(6) required final String type,
|
||||
@HiveField(7) required final SnChannel channel,
|
||||
@HiveField(8) required final SnChannelMember sender,
|
||||
@HiveField(9) required final int channelId,
|
||||
@HiveField(10) required final int senderId,
|
||||
@HiveField(11) required final int? quoteEventId,
|
||||
@HiveField(12) required final int? relatedEventId,
|
||||
final SnChatMessagePreload? preload}) = _$SnChatMessageImpl;
|
||||
const _SnChatMessage._() : super._();
|
||||
|
||||
@ -1549,6 +1572,9 @@ abstract class _SnChatMessage extends SnChatMessage {
|
||||
@HiveField(11)
|
||||
int? get quoteEventId;
|
||||
@override
|
||||
@HiveField(12)
|
||||
int? get relatedEventId;
|
||||
@override
|
||||
SnChatMessagePreload? get preload;
|
||||
|
||||
/// Create a copy of SnChatMessage
|
||||
|
@ -163,13 +163,14 @@ class SnChatMessageImplAdapter extends TypeAdapter<_$SnChatMessageImpl> {
|
||||
channelId: fields[9] as int,
|
||||
senderId: fields[10] as int,
|
||||
quoteEventId: fields[11] as int?,
|
||||
relatedEventId: fields[12] as int?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, _$SnChatMessageImpl obj) {
|
||||
writer
|
||||
..writeByte(12)
|
||||
..writeByte(13)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
@ -192,6 +193,8 @@ class SnChatMessageImplAdapter extends TypeAdapter<_$SnChatMessageImpl> {
|
||||
..write(obj.senderId)
|
||||
..writeByte(11)
|
||||
..write(obj.quoteEventId)
|
||||
..writeByte(12)
|
||||
..write(obj.relatedEventId)
|
||||
..writeByte(5)
|
||||
..write(obj.body);
|
||||
}
|
||||
@ -306,13 +309,14 @@ _$SnChatMessageImpl _$$SnChatMessageImplFromJson(Map<String, dynamic> json) =>
|
||||
? null
|
||||
: DateTime.parse(json['deleted_at'] as String),
|
||||
uuid: json['uuid'] as String,
|
||||
body: json['body'] as Map<String, dynamic>,
|
||||
body: json['body'] as Map<String, dynamic>? ?? const {},
|
||||
type: json['type'] as String,
|
||||
channel: SnChannel.fromJson(json['channel'] as Map<String, dynamic>),
|
||||
sender: SnChannelMember.fromJson(json['sender'] as Map<String, dynamic>),
|
||||
channelId: (json['channel_id'] as num).toInt(),
|
||||
senderId: (json['sender_id'] as num).toInt(),
|
||||
quoteEventId: (json['quote_event_id'] as num?)?.toInt(),
|
||||
relatedEventId: (json['related_event_id'] as num?)?.toInt(),
|
||||
preload: json['preload'] == null
|
||||
? null
|
||||
: SnChatMessagePreload.fromJson(
|
||||
@ -333,6 +337,7 @@ Map<String, dynamic> _$$SnChatMessageImplToJson(_$SnChatMessageImpl instance) =>
|
||||
'channel_id': instance.channelId,
|
||||
'sender_id': instance.senderId,
|
||||
'quote_event_id': instance.quoteEventId,
|
||||
'related_event_id': instance.relatedEventId,
|
||||
'preload': instance.preload?.toJson(),
|
||||
};
|
||||
|
||||
|
@ -147,6 +147,18 @@ class ChatMessage extends StatelessWidget {
|
||||
content: data.body['text'],
|
||||
isAutoWarp: true,
|
||||
),
|
||||
if (data.type == 'messages.delete' &&
|
||||
data.relatedEventId != null)
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Symbols.delete),
|
||||
const Gap(8),
|
||||
Text(
|
||||
'messageDeleted'
|
||||
.tr(args: ['#${data.relatedEventId}']),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
||||
bool _isBusy = false;
|
||||
double? _progress;
|
||||
|
||||
SnChatMessage? _replyingMessage;
|
||||
SnChatMessage? _replyingMessage, _editingMessage;
|
||||
|
||||
final TextEditingController _contentController = TextEditingController();
|
||||
final FocusNode _focusNode = FocusNode();
|
||||
@ -34,6 +34,26 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
||||
setState(() => _replyingMessage = value);
|
||||
}
|
||||
|
||||
void setEdit(SnChatMessage? value) {
|
||||
setState(() => _editingMessage = value);
|
||||
}
|
||||
|
||||
Future<void> deleteMessage(SnChatMessage message) async {
|
||||
final confirm = await context.showConfirmDialog(
|
||||
'messageDelete'.tr(args: ['#${message.id}']),
|
||||
'messageDeleteDescription'.tr(),
|
||||
);
|
||||
if (!confirm) return;
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() => _isBusy = true);
|
||||
|
||||
await widget.controller.deleteMessage(message);
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() => _isBusy = false);
|
||||
}
|
||||
|
||||
Future<void> _sendMessage() async {
|
||||
if (_isBusy) return;
|
||||
|
||||
@ -89,10 +109,13 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
||||
.where((e) => e.attachment != null)
|
||||
.map((e) => e.attachment!.rid)
|
||||
.toList(),
|
||||
relatedId: _editingMessage?.id,
|
||||
quoteId: _replyingMessage?.id,
|
||||
editingMessage: _editingMessage,
|
||||
);
|
||||
_contentController.clear();
|
||||
_attachments.clear();
|
||||
_editingMessage = null;
|
||||
_replyingMessage = null;
|
||||
|
||||
setState(() => _isBusy = false);
|
||||
@ -184,6 +207,42 @@ class ChatMessageInputState extends State<ChatMessageInput> {
|
||||
),
|
||||
).height(_replyingMessage != null ? 54 + 8 : 0, animate: true).animate(
|
||||
const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
|
||||
SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
child: Padding(
|
||||
padding: _editingMessage != null
|
||||
? const EdgeInsets.only(top: 8)
|
||||
: EdgeInsets.zero,
|
||||
child: _editingMessage != null
|
||||
? MaterialBanner(
|
||||
padding: const EdgeInsets.only(left: 16.0),
|
||||
leading: const Icon(Symbols.edit),
|
||||
content: SingleChildScrollView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (_editingMessage?.body['text'] != null)
|
||||
MarkdownTextContent(
|
||||
content: _editingMessage?.body['text'],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text('cancel'.tr()),
|
||||
onPressed: () {
|
||||
setState(() => _editingMessage = null);
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
).height(_editingMessage != null ? 54 + 8 : 0, animate: true).animate(
|
||||
const Duration(milliseconds: 300), Curves.fastEaseInToSlowEaseOut),
|
||||
SizedBox(
|
||||
height: 56,
|
||||
child: Row(
|
||||
|
Reference in New Issue
Block a user