Chepuhagram/lib/data/datasources/local_db_service.dart

240 lines
7.3 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:sqflite/sqflite.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'package:chepuhagram/data/models/message_model.dart';
class LocalDbService {
static final LocalDbService _instance = LocalDbService._internal();
static Database? _database;
factory LocalDbService() => _instance;
LocalDbService._internal();
static const int _dbVersion = 8;
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDb();
return _database!;
}
Future<void> _createMessagesTable(Database db) async {
await db.execute('''
CREATE TABLE messages(
id INTEGER PRIMARY KEY,
sender_id INTEGER,
receiver_id INTEGER,
content TEXT,
timestamp TEXT,
delivered_at TEXT,
read_at TEXT,
reply_to_id INTEGER,
reply_to_text TEXT,
edited_at TEXT,
message_type TEXT DEFAULT 'text',
file_id TEXT,
encrypted_key TEXT,
file_name TEXT,
file_size INTEGER
)
''');
}
Future<Database> _initDb() async {
String path = join(await getDatabasesPath(), 'chat_app.db');
return await openDatabase(
path,
version: _dbVersion,
onCreate: (db, version) async {
await _createMessagesTable(db);
},
onUpgrade: (db, oldVersion, newVersion) async {
if (oldVersion < 8) {
// v8: stop storing media bytes in SQLite; rebuild messages table.
await db.execute('DROP TABLE IF EXISTS messages');
await _createMessagesTable(db);
return;
}
if (oldVersion < 2) {
await db.execute('ALTER TABLE messages ADD COLUMN delivered_at TEXT');
await db.execute('ALTER TABLE messages ADD COLUMN read_at TEXT');
}
if (oldVersion < 3) {
await db.execute(
'ALTER TABLE messages ADD COLUMN reply_to_id INTEGER',
);
await db.execute(
'ALTER TABLE messages ADD COLUMN reply_to_text TEXT',
);
}
if (oldVersion < 4) {
await db.execute('ALTER TABLE messages ADD COLUMN edited_at TEXT');
}
if (oldVersion < 5) {
try {
await db.execute(
'ALTER TABLE messages ADD COLUMN message_type TEXT',
);
} catch (e) {
print('message_type column already exists: $e');
}
try {
await db.execute('ALTER TABLE messages ADD COLUMN file_id TEXT');
} catch (e) {
print('file_id column already exists: $e');
}
}
if (oldVersion < 6) {
try {
await db.execute(
'ALTER TABLE messages ADD COLUMN encrypted_key TEXT',
);
} catch (e) {
print('encrypted_key column already exists: $e');
}
}
// old migrations kept for safety, but v8 rebuild returns early.
},
);
}
Future<void> clearDatabase() async {
final db = await database;
await db.delete('messages');
}
Future<void> saveMessages(List<dynamic> messages) async {
final db = await database;
final List<int> incomingIds = messages.map((msg) {
return (msg is MessageModel) ? msg.id! : (msg['id'] as int);
}).toList();
Batch batch = db.batch();
if (incomingIds.isNotEmpty) {
batch.delete('messages', where: 'id NOT IN (${incomingIds.join(',')})');
}
for (var msg in messages) {
if (msg is MessageModel) {
batch.insert('messages', {
'id': msg.id,
'sender_id': msg.senderId,
'receiver_id': msg.receiverId,
'content': msg.text,
'timestamp': msg.createdAt.toIso8601String(),
'delivered_at': null,
'read_at': null,
'reply_to_id': msg.replyToId,
'reply_to_text': msg.replyToText,
'edited_at': msg.editedAt?.toIso8601String(),
'message_type': msg.messageType.name,
'file_id': msg.fileId,
'encrypted_key': msg.encryptedFileKey,
'file_name': msg.fileName,
'file_size': msg.fileSize,
}, conflictAlgorithm: ConflictAlgorithm.replace);
} else {
// Если это Map из API
batch.insert('messages', {
'id': msg['id'],
'sender_id': msg['sender_id'],
'receiver_id':
msg['receiver_id'], // Убедись, что ключ совпадает с API
'content': msg['content'],
'timestamp': msg['timestamp'],
'delivered_at': msg['delivered_at'],
'read_at': msg['read_at'],
'reply_to_id': msg['reply_to_id'],
'reply_to_text': msg['reply_to_text'],
'edited_at': msg['edited_at'],
'message_type': msg['message_type'] ?? 'text',
'file_id': msg['file_id'],
'encrypted_key': msg['encrypted_key'],
'file_name': msg['file_name'],
'file_size': msg['file_size'],
}, conflictAlgorithm: ConflictAlgorithm.replace);
}
}
await batch.commit(noResult: true);
}
// Получение сообщений конкретного чата
Future<List<Map<String, dynamic>>> getChatHistory(
int contactId,
int myId,
) async {
final db = await database;
return await db.query(
'messages',
where:
'(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)',
whereArgs: [contactId, myId, myId, contactId],
orderBy: 'timestamp ASC',
);
}
Future<int> deleteChatHistory(int contactId, int myId) async {
final db = await database;
return await db.delete(
'messages',
where:
'(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)',
whereArgs: [contactId, myId, myId, contactId],
);
}
Future<Map<String, dynamic>?> getLastMessage(int contactId, int myId) async {
final db = await database;
final rows = await db.query(
'messages',
columns: ['sender_id', 'receiver_id', 'content', 'timestamp'],
where:
'(sender_id = ? AND receiver_id = ?) OR (sender_id = ? AND receiver_id = ?)',
whereArgs: [contactId, myId, myId, contactId],
orderBy: 'timestamp DESC',
limit: 1,
);
if (rows.isEmpty) return null;
return rows.first;
}
Future<void> updateDeliveredAt(int messageId, DateTime deliveredAt) async {
final db = await database;
await db.update(
'messages',
{'delivered_at': deliveredAt.toIso8601String()},
where: 'id = ?',
whereArgs: [messageId],
);
}
Future<void> updateReadAt(int messageId, DateTime readAt) async {
final db = await database;
await db.update(
'messages',
{'read_at': readAt.toIso8601String()},
where: 'id = ?',
whereArgs: [messageId],
);
}
Future<void> updateMessageContent(
int messageId,
String content,
DateTime? editedAt,
) async {
final db = await database;
await db.update(
'messages',
{'content': content, 'edited_at': editedAt?.toIso8601String()},
where: 'id = ?',
whereArgs: [messageId],
);
}
Future<void> deleteMessage(int messageId) async {
final db = await database;
await db.delete('messages', where: 'id = ?', whereArgs: [messageId]);
}
}