Files
iykyk_msn/mobile/lib/data/message_local_store.dart
2026-04-07 16:17:03 +09:00

120 lines
3.2 KiB
Dart

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();
}
}