오대리ㅣㅣㅣㅣ
This commit is contained in:
119
mobile/lib/data/message_local_store.dart
Normal file
119
mobile/lib/data/message_local_store.dart
Normal file
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
import '../models/message_model.dart';
|
||||
|
||||
/// Local cache of messages keyed by context + room (plan: 맥락 ID 반영).
|
||||
/// On **web**, uses in-memory storage only (`sqflite` is not supported).
|
||||
class MessageLocalStore {
|
||||
MessageLocalStore();
|
||||
|
||||
Database? _db;
|
||||
|
||||
/// Web-only: key = "contextId::roomId"
|
||||
final Map<String, List<MessageModel>> _memory = {};
|
||||
|
||||
static String _memKey(String contextId, String roomId) => '$contextId::$roomId';
|
||||
|
||||
Future<Database> get database async {
|
||||
if (kIsWeb) {
|
||||
throw UnsupportedError('database getter should not be used on web');
|
||||
}
|
||||
if (_db != null) return _db!;
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
final path = p.join(dir.path, 'msn_messages.db');
|
||||
_db = await openDatabase(
|
||||
path,
|
||||
version: 1,
|
||||
onCreate: (db, v) async {
|
||||
await db.execute('''
|
||||
CREATE TABLE messages (
|
||||
id TEXT NOT NULL,
|
||||
context_id TEXT NOT NULL,
|
||||
room_id TEXT NOT NULL,
|
||||
sender_id TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
kind TEXT NOT NULL DEFAULT 'text',
|
||||
PRIMARY KEY (id, context_id)
|
||||
);
|
||||
''');
|
||||
await db.execute(
|
||||
'CREATE INDEX idx_room_ctx ON messages(room_id, context_id, created_at);',
|
||||
);
|
||||
},
|
||||
);
|
||||
return _db!;
|
||||
}
|
||||
|
||||
/// [roomId] required so empty API results can clear the web cache.
|
||||
Future<void> upsertMessages(
|
||||
String contextId,
|
||||
String roomId,
|
||||
List<MessageModel> list,
|
||||
) async {
|
||||
if (kIsWeb) {
|
||||
final key = _memKey(contextId, roomId);
|
||||
if (list.isEmpty) {
|
||||
_memory[key] = [];
|
||||
return;
|
||||
}
|
||||
final byId = <String, MessageModel>{
|
||||
for (final m in _memory[key] ?? []) m.id: m,
|
||||
};
|
||||
for (final m in list) {
|
||||
byId[m.id] = m;
|
||||
}
|
||||
final merged = byId.values.toList()
|
||||
..sort((a, b) => a.createdAt.compareTo(b.createdAt));
|
||||
_memory[key] = merged;
|
||||
return;
|
||||
}
|
||||
|
||||
final db = await database;
|
||||
final batch = db.batch();
|
||||
for (final m in list) {
|
||||
batch.insert(
|
||||
'messages',
|
||||
{
|
||||
'id': m.id,
|
||||
'context_id': contextId,
|
||||
'room_id': m.roomId,
|
||||
'sender_id': m.senderId,
|
||||
'body': m.body,
|
||||
'created_at': m.createdAt,
|
||||
'kind': m.kind,
|
||||
},
|
||||
conflictAlgorithm: ConflictAlgorithm.replace,
|
||||
);
|
||||
}
|
||||
await batch.commit(noResult: true);
|
||||
}
|
||||
|
||||
Future<List<MessageModel>> listForRoom(String contextId, String roomId) async {
|
||||
if (kIsWeb) {
|
||||
return List<MessageModel>.from(_memory[_memKey(contextId, roomId)] ?? []);
|
||||
}
|
||||
final db = await database;
|
||||
final rows = await db.query(
|
||||
'messages',
|
||||
where: 'context_id = ? AND room_id = ?',
|
||||
whereArgs: [contextId, roomId],
|
||||
orderBy: 'created_at ASC',
|
||||
);
|
||||
return rows
|
||||
.map(
|
||||
(r) => MessageModel(
|
||||
id: r['id']! as String,
|
||||
roomId: r['room_id']! as String,
|
||||
senderId: r['sender_id']! as String,
|
||||
body: r['body']! as String,
|
||||
createdAt: r['created_at']! as String,
|
||||
kind: r['kind'] as String? ?? 'text',
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user