feat: история изменения сообщений
This commit is contained in:
@@ -538,6 +538,19 @@ class ChatsModule {
|
||||
mergedPayload[entry.key.toString()] = entry.value;
|
||||
}
|
||||
final newRow = Map<String, dynamic>.from(existing);
|
||||
if (KometSettings.viewRedacted.value) {
|
||||
final oldText = existing['text']?.toString();
|
||||
if ((oldText ?? '') != (msgText ?? '') &&
|
||||
oldText != null &&
|
||||
oldText.isNotEmpty) {
|
||||
final history = CachedMessage.appendEditHistory(
|
||||
CachedMessage.parseEditHistory(existing['edit_history']),
|
||||
oldText,
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
newRow['edit_history'] = jsonEncode(history);
|
||||
}
|
||||
}
|
||||
newRow['text'] = msgText;
|
||||
newRow['status'] = status;
|
||||
newRow['payload'] = jsonEncode(mergedPayload);
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import '../api.dart';
|
||||
import '../../core/protocol/opcode_map.dart';
|
||||
|
||||
class ComplaintReason {
|
||||
final int reasonId;
|
||||
final String reasonTitle;
|
||||
|
||||
const ComplaintReason({required this.reasonId, required this.reasonTitle});
|
||||
}
|
||||
|
||||
class ComplaintsModule {
|
||||
static Map<int, List<ComplaintReason>>? _cache;
|
||||
|
||||
static Future<Map<int, List<ComplaintReason>>> fetchReasons(Api api) async {
|
||||
final cached = _cache;
|
||||
if (cached != null) return cached;
|
||||
|
||||
final response = await api.sendRequest(
|
||||
Opcode.complainReasonsGet,
|
||||
{'complainSync': 0},
|
||||
);
|
||||
if (!response.isOk) return cached ?? const {};
|
||||
|
||||
final payload = response.payload;
|
||||
if (payload is! Map) return const {};
|
||||
|
||||
final complains = payload['complains'];
|
||||
final map = <int, List<ComplaintReason>>{};
|
||||
if (complains is List) {
|
||||
for (final entry in complains) {
|
||||
if (entry is! Map) continue;
|
||||
final typeId = entry['typeId'];
|
||||
final reasons = entry['reasons'];
|
||||
if (typeId is! int || reasons is! List) continue;
|
||||
map[typeId] = reasons
|
||||
.whereType<Map>()
|
||||
.map(
|
||||
(r) => ComplaintReason(
|
||||
reasonId: r['reasonId'] is int ? r['reasonId'] as int : 0,
|
||||
reasonTitle: r['reasonTitle']?.toString() ?? '',
|
||||
),
|
||||
)
|
||||
.where((r) => r.reasonId != 0)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
_cache = map;
|
||||
return map;
|
||||
}
|
||||
|
||||
static Future<List<ComplaintReason>> reasonsFor(Api api, int typeId) async {
|
||||
final map = await fetchReasons(api);
|
||||
final forType = map[typeId];
|
||||
if (forType != null && forType.isNotEmpty) return forType;
|
||||
for (final list in map.values) {
|
||||
if (list.isNotEmpty) return list;
|
||||
}
|
||||
return const [];
|
||||
}
|
||||
|
||||
static Future<bool> sendComplaint(
|
||||
Api api, {
|
||||
required int reasonId,
|
||||
required int typeId,
|
||||
required List<int> ids,
|
||||
required int parentId,
|
||||
}) async {
|
||||
final response = await api.sendRequest(Opcode.complain, {
|
||||
'reasonId': reasonId,
|
||||
'typeId': typeId,
|
||||
'ids': ids,
|
||||
'parentId': parentId,
|
||||
});
|
||||
if (!response.isOk) return false;
|
||||
final payload = response.payload;
|
||||
return payload is Map && payload['success'] == true;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../api.dart';
|
||||
import '../../core/config/komet_settings.dart';
|
||||
import '../../core/protocol/opcode_map.dart';
|
||||
import '../../core/protocol/packet.dart';
|
||||
import '../../core/storage/app_database.dart';
|
||||
@@ -365,6 +366,7 @@ class CachedMessage {
|
||||
final List<MessageAttachment>? attachments;
|
||||
final bool isControl;
|
||||
final bool deleted;
|
||||
final List<Map<String, dynamic>>? editHistory;
|
||||
|
||||
const CachedMessage({
|
||||
required this.id,
|
||||
@@ -378,12 +380,14 @@ class CachedMessage {
|
||||
this.attachments,
|
||||
this.isControl = false,
|
||||
this.deleted = false,
|
||||
this.editHistory,
|
||||
});
|
||||
|
||||
CachedMessage copyWith({
|
||||
String? status,
|
||||
bool? deleted,
|
||||
List<MessageAttachment>? attachments,
|
||||
List<Map<String, dynamic>>? editHistory,
|
||||
}) => CachedMessage(
|
||||
id: id,
|
||||
accountId: accountId,
|
||||
@@ -396,8 +400,39 @@ class CachedMessage {
|
||||
attachments: attachments ?? this.attachments,
|
||||
isControl: isControl,
|
||||
deleted: deleted ?? this.deleted,
|
||||
editHistory: editHistory ?? this.editHistory,
|
||||
);
|
||||
|
||||
static List<Map<String, dynamic>>? parseEditHistory(dynamic raw) {
|
||||
if (raw is! String || raw.isEmpty) return null;
|
||||
try {
|
||||
final decoded = jsonDecode(raw);
|
||||
if (decoded is List) {
|
||||
final list = decoded
|
||||
.whereType<Map>()
|
||||
.map((e) => Map<String, dynamic>.from(e))
|
||||
.toList();
|
||||
return list.isEmpty ? null : list;
|
||||
}
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<Map<String, dynamic>> appendEditHistory(
|
||||
List<Map<String, dynamic>>? current,
|
||||
String? oldText,
|
||||
int time,
|
||||
) {
|
||||
final list = current != null
|
||||
? List<Map<String, dynamic>>.from(current)
|
||||
: <Map<String, dynamic>>[];
|
||||
if (list.isNotEmpty && (list.last['text'] as String?) == oldText) {
|
||||
return list;
|
||||
}
|
||||
list.add({'text': oldText, 'time': time});
|
||||
return list;
|
||||
}
|
||||
|
||||
factory CachedMessage.fromDbRow(Map<String, dynamic> row) {
|
||||
Map<String, dynamic>? payload;
|
||||
final payloadRaw = row['payload'];
|
||||
@@ -449,6 +484,7 @@ class CachedMessage {
|
||||
deleted: row['deleted'] is int
|
||||
? row['deleted'] == 1
|
||||
: row['deleted']?.toString() == '1',
|
||||
editHistory: parseEditHistory(row['edit_history']),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -488,6 +524,7 @@ class CachedMessage {
|
||||
'status': status,
|
||||
'payload': payload != null ? jsonEncode(payload) : null,
|
||||
'deleted': deleted ? 1 : 0,
|
||||
'edit_history': editHistory != null ? jsonEncode(editHistory) : null,
|
||||
};
|
||||
|
||||
static CachedMessage fromPushPayload(int accountId, int chatId, Map msg) {
|
||||
@@ -551,7 +588,6 @@ class MessagesModule {
|
||||
if (messagesData is! List) return [];
|
||||
|
||||
final List<CachedMessage> results = [];
|
||||
final List<Map<String, dynamic>> rows = [];
|
||||
|
||||
for (var i = 0; i < messagesData.length; i++) {
|
||||
final m = messagesData[i];
|
||||
@@ -560,7 +596,6 @@ class MessagesModule {
|
||||
final msg = _parseMessage(m.cast<dynamic, dynamic>(), accountId, chatId);
|
||||
if (msg != null) {
|
||||
results.add(msg);
|
||||
rows.add(msg.toDbRow());
|
||||
}
|
||||
|
||||
if (i > 0 && i % 20 == 0) {
|
||||
@@ -568,15 +603,57 @@ class MessagesModule {
|
||||
}
|
||||
}
|
||||
|
||||
if (rows.isNotEmpty) {
|
||||
final toSave = KometSettings.viewRedacted.value && results.isNotEmpty
|
||||
? await _mergeEditHistory(accountId, chatId, results)
|
||||
: results;
|
||||
|
||||
if (toSave.isNotEmpty) {
|
||||
try {
|
||||
await AppDatabase.saveMessages(rows);
|
||||
await AppDatabase.saveMessages(
|
||||
toSave.map((m) => m.toDbRow()).toList(),
|
||||
);
|
||||
} catch (e) {
|
||||
logger.e('saveMessages error: $e');
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
return toSave;
|
||||
}
|
||||
|
||||
Future<List<CachedMessage>> _mergeEditHistory(
|
||||
int accountId,
|
||||
int chatId,
|
||||
List<CachedMessage> serverMessages,
|
||||
) async {
|
||||
final cachedRows = await AppDatabase.loadMessagesByIds(
|
||||
accountId,
|
||||
chatId,
|
||||
serverMessages.map((m) => m.id).toList(),
|
||||
);
|
||||
final byId = <String, Map<String, dynamic>>{};
|
||||
for (final row in cachedRows) {
|
||||
final id = row['id']?.toString();
|
||||
if (id != null) byId[id] = row;
|
||||
}
|
||||
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
final out = <CachedMessage>[];
|
||||
for (final msg in serverMessages) {
|
||||
final existing = byId[msg.id];
|
||||
if (existing == null) {
|
||||
out.add(msg);
|
||||
continue;
|
||||
}
|
||||
var history = CachedMessage.parseEditHistory(existing['edit_history']);
|
||||
final oldText = existing['text']?.toString();
|
||||
if ((oldText ?? '') != (msg.text ?? '') &&
|
||||
oldText != null &&
|
||||
oldText.isNotEmpty) {
|
||||
history = CachedMessage.appendEditHistory(history, oldText, now);
|
||||
}
|
||||
out.add(history == null ? msg : msg.copyWith(editHistory: history));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Загружает сообщения из локальной базы данных.
|
||||
|
||||
Reference in New Issue
Block a user