From 84bca9601a0930d0343188dcbd5642d7a9cb2143 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 4 Dec 2025 22:20:03 +0800 Subject: [PATCH] :recycle: Move the data part out of the chat list UI --- lib/pods/chat/chat_room.dart | 432 ++++++++++++++++++ .../chat/chat_room.g.dart} | 2 +- lib/pods/chat/chat_rooms.dart | 5 - lib/pods/chat/chat_subscribe.dart | 2 +- lib/pods/chat/messages_notifier.dart | 3 +- lib/screens/chat/chat.dart | 425 +---------------- lib/screens/chat/chat_form.dart | 2 +- lib/screens/chat/public_room_preview.dart | 2 +- lib/screens/chat/room.dart | 3 +- lib/screens/chat/room_detail.dart | 2 +- lib/screens/chat/search_messages.dart | 2 +- lib/widgets/chat/message_item.dart | 2 +- lib/widgets/chat/public_room_preview.dart | 3 +- lib/widgets/share/share_sheet.dart | 2 +- 14 files changed, 444 insertions(+), 443 deletions(-) create mode 100644 lib/pods/chat/chat_room.dart rename lib/{screens/chat/chat.g.dart => pods/chat/chat_room.g.dart} (99%) delete mode 100644 lib/pods/chat/chat_rooms.dart diff --git a/lib/pods/chat/chat_room.dart b/lib/pods/chat/chat_room.dart new file mode 100644 index 00000000..d4304dc1 --- /dev/null +++ b/lib/pods/chat/chat_room.dart @@ -0,0 +1,432 @@ +import 'package:dio/dio.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/database/drift_db.dart'; +import 'package:island/models/account.dart'; +import 'package:island/models/chat.dart'; +import 'package:island/models/file.dart'; +import 'package:island/pods/database.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/pods/userinfo.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'chat_room.g.dart'; + +final isSyncingProvider = StateProvider.autoDispose((ref) => false); + +final flashingMessagesProvider = StateProvider>((ref) => {}); + +@riverpod +class ChatRoomJoinedNotifier extends _$ChatRoomJoinedNotifier { + @override + Future> build() async { + final db = ref.watch(databaseProvider); + + try { + final localRoomsData = await db.select(db.chatRooms).get(); + if (localRoomsData.isNotEmpty) { + final localRooms = await Future.wait( + localRoomsData.map((row) async { + final membersRows = + await (db.select(db.chatMembers) + ..where((m) => m.chatRoomId.equals(row.id))).get(); + final members = + membersRows.map((mRow) { + final account = SnAccount.fromJson(mRow.account); + return SnChatMember( + id: mRow.id, + chatRoomId: mRow.chatRoomId, + accountId: mRow.accountId, + account: account, + nick: mRow.nick, + notify: mRow.notify, + joinedAt: mRow.joinedAt, + breakUntil: mRow.breakUntil, + timeoutUntil: mRow.timeoutUntil, + status: null, + createdAt: mRow.createdAt, + updatedAt: mRow.updatedAt, + deletedAt: mRow.deletedAt, + chatRoom: null, + ); + }).toList(); + return SnChatRoom( + id: row.id, + name: row.name, + description: row.description, + type: row.type, + isPublic: row.isPublic!, + isCommunity: row.isCommunity!, + picture: + row.picture != null + ? SnCloudFile.fromJson(row.picture!) + : null, + background: + row.background != null + ? SnCloudFile.fromJson(row.background!) + : null, + realmId: row.realmId, + accountId: row.accountId, + realm: null, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + deletedAt: row.deletedAt, + members: members, + ); + }), + ); + + // Background sync + Future(() async { + try { + final client = ref.read(apiClientProvider); + final resp = await client.get('/sphere/chat'); + final remoteRooms = + resp.data + .map((e) => SnChatRoom.fromJson(e)) + .cast() + .toList(); + await db.saveChatRooms(remoteRooms, override: true); + // Update state with fresh data + state = AsyncData(await _buildRoomsFromDb(db)); + } catch (_) {} + }).ignore(); + + return localRooms; + } + } catch (_) {} + + // Fallback to API + final client = ref.watch(apiClientProvider); + final resp = await client.get('/sphere/chat'); + final rooms = + resp.data + .map((e) => SnChatRoom.fromJson(e)) + .cast() + .toList(); + await db.saveChatRooms(rooms, override: true); + return rooms; + } + + Future> _buildRoomsFromDb(AppDatabase db) async { + final localRoomsData = await db.select(db.chatRooms).get(); + return Future.wait( + localRoomsData.map((row) async { + final membersRows = + await (db.select(db.chatMembers) + ..where((m) => m.chatRoomId.equals(row.id))).get(); + final members = + membersRows.map((mRow) { + final account = SnAccount.fromJson(mRow.account); + return SnChatMember( + id: mRow.id, + chatRoomId: mRow.chatRoomId, + accountId: mRow.accountId, + account: account, + nick: mRow.nick, + notify: mRow.notify, + joinedAt: mRow.joinedAt, + breakUntil: mRow.breakUntil, + timeoutUntil: mRow.timeoutUntil, + status: null, + createdAt: mRow.createdAt, + updatedAt: mRow.updatedAt, + deletedAt: mRow.deletedAt, + chatRoom: null, + ); + }).toList(); + return SnChatRoom( + id: row.id, + name: row.name, + description: row.description, + type: row.type, + isPublic: row.isPublic!, + isCommunity: row.isCommunity!, + picture: + row.picture != null ? SnCloudFile.fromJson(row.picture!) : null, + background: + row.background != null + ? SnCloudFile.fromJson(row.background!) + : null, + realmId: row.realmId, + accountId: row.accountId, + realm: null, + createdAt: row.createdAt, + updatedAt: row.updatedAt, + deletedAt: row.deletedAt, + members: members, + ); + }), + ); + } +} + +@riverpod +class ChatRoomNotifier extends _$ChatRoomNotifier { + @override + Future build(String? identifier) async { + if (identifier == null) return null; + final db = ref.watch(databaseProvider); + + try { + // Try to get from local database first + final localRoomData = + await (db.select(db.chatRooms) + ..where((r) => r.id.equals(identifier))).getSingleOrNull(); + + if (localRoomData != null) { + // Fetch members for this room + final membersRows = + await (db.select(db.chatMembers) + ..where((m) => m.chatRoomId.equals(localRoomData.id))).get(); + final members = + membersRows.map((mRow) { + final account = SnAccount.fromJson(mRow.account); + return SnChatMember( + id: mRow.id, + chatRoomId: mRow.chatRoomId, + accountId: mRow.accountId, + account: account, + nick: mRow.nick, + notify: mRow.notify, + joinedAt: mRow.joinedAt, + breakUntil: mRow.breakUntil, + timeoutUntil: mRow.timeoutUntil, + status: null, + createdAt: mRow.createdAt, + updatedAt: mRow.updatedAt, + deletedAt: mRow.deletedAt, + chatRoom: null, + ); + }).toList(); + + final localRoom = SnChatRoom( + id: localRoomData.id, + name: localRoomData.name, + description: localRoomData.description, + type: localRoomData.type, + isPublic: localRoomData.isPublic!, + isCommunity: localRoomData.isCommunity!, + picture: + localRoomData.picture != null + ? SnCloudFile.fromJson(localRoomData.picture!) + : null, + background: + localRoomData.background != null + ? SnCloudFile.fromJson(localRoomData.background!) + : null, + realmId: localRoomData.realmId, + accountId: localRoomData.accountId, + realm: null, + createdAt: localRoomData.createdAt, + updatedAt: localRoomData.updatedAt, + deletedAt: localRoomData.deletedAt, + members: members, + ); + + // Background sync + Future(() async { + try { + final client = ref.read(apiClientProvider); + final resp = await client.get('/sphere/chat/$identifier'); + final remoteRoom = SnChatRoom.fromJson(resp.data); + await db.saveChatRooms([remoteRoom]); + // Update state with fresh data + state = AsyncData(await _buildRoomFromDb(db, identifier)); + } catch (_) {} + }).ignore(); + + return localRoom; + } + } catch (_) {} + + // Fallback to API + try { + final client = ref.watch(apiClientProvider); + final resp = await client.get('/sphere/chat/$identifier'); + final room = SnChatRoom.fromJson(resp.data); + // await db.saveChatRooms([room]); + return room; + } catch (err) { + if (err is DioException && err.response?.statusCode == 404) { + return null; // Chat room not found + } + rethrow; // Rethrow other errors + } + } + + Future _buildRoomFromDb( + AppDatabase db, + String identifier, + ) async { + final localRoomData = + await (db.select(db.chatRooms) + ..where((r) => r.id.equals(identifier))).getSingleOrNull(); + + if (localRoomData == null) return null; + + final membersRows = + await (db.select(db.chatMembers) + ..where((m) => m.chatRoomId.equals(localRoomData.id))).get(); + final members = + membersRows.map((mRow) { + final account = SnAccount.fromJson(mRow.account); + return SnChatMember( + id: mRow.id, + chatRoomId: mRow.chatRoomId, + accountId: mRow.accountId, + account: account, + nick: mRow.nick, + notify: mRow.notify, + joinedAt: mRow.joinedAt, + breakUntil: mRow.breakUntil, + timeoutUntil: mRow.timeoutUntil, + status: null, + createdAt: mRow.createdAt, + updatedAt: mRow.updatedAt, + deletedAt: mRow.deletedAt, + chatRoom: null, + ); + }).toList(); + + return SnChatRoom( + id: localRoomData.id, + name: localRoomData.name, + description: localRoomData.description, + type: localRoomData.type, + isPublic: localRoomData.isPublic!, + isCommunity: localRoomData.isCommunity!, + picture: + localRoomData.picture != null + ? SnCloudFile.fromJson(localRoomData.picture!) + : null, + background: + localRoomData.background != null + ? SnCloudFile.fromJson(localRoomData.background!) + : null, + realmId: localRoomData.realmId, + accountId: localRoomData.accountId, + realm: null, + createdAt: localRoomData.createdAt, + updatedAt: localRoomData.updatedAt, + deletedAt: localRoomData.deletedAt, + members: members, + ); + } +} + +@riverpod +class ChatRoomIdentityNotifier extends _$ChatRoomIdentityNotifier { + @override + Future build(String? identifier) async { + if (identifier == null) return null; + final db = ref.watch(databaseProvider); + final userInfo = ref.watch(userInfoProvider); + + try { + // Try to get from local database first + if (userInfo.value != null) { + final localMemberData = + await (db.select(db.chatMembers) + ..where((m) => m.chatRoomId.equals(identifier)) + ..where((m) => m.accountId.equals(userInfo.value!.id))) + .getSingleOrNull(); + + if (localMemberData != null) { + final account = SnAccount.fromJson(localMemberData.account); + final localMember = SnChatMember( + id: localMemberData.id, + chatRoomId: localMemberData.chatRoomId, + accountId: localMemberData.accountId, + account: account, + nick: localMemberData.nick, + notify: localMemberData.notify, + joinedAt: localMemberData.joinedAt, + breakUntil: localMemberData.breakUntil, + timeoutUntil: localMemberData.timeoutUntil, + status: null, + createdAt: localMemberData.createdAt, + updatedAt: localMemberData.updatedAt, + deletedAt: localMemberData.deletedAt, + chatRoom: null, + ); + + // Background sync + Future(() async { + try { + final client = ref.read(apiClientProvider); + final resp = await client.get( + '/sphere/chat/$identifier/members/me', + ); + final remoteMember = SnChatMember.fromJson(resp.data); + await db.saveMember(remoteMember); + // Update state with fresh data + if (userInfo.value != null) { + state = AsyncData( + await _buildMemberFromDb(db, identifier, userInfo.value!.id), + ); + } + } catch (_) {} + }).ignore(); + + return localMember; + } + } + } catch (_) {} + + // Fallback to API + try { + final client = ref.watch(apiClientProvider); + final resp = await client.get('/sphere/chat/$identifier/members/me'); + final member = SnChatMember.fromJson(resp.data); + await db.saveMember(member); + return member; + } catch (err) { + if (err is DioException && err.response?.statusCode == 404) { + return null; // Chat member not found + } + rethrow; // Rethrow other errors + } + } + + Future _buildMemberFromDb( + AppDatabase db, + String identifier, + String accountId, + ) async { + final localMemberData = + await (db.select(db.chatMembers) + ..where((m) => m.chatRoomId.equals(identifier)) + ..where((m) => m.accountId.equals(accountId))) + .getSingleOrNull(); + + if (localMemberData == null) return null; + + final account = SnAccount.fromJson(localMemberData.account); + return SnChatMember( + id: localMemberData.id, + chatRoomId: localMemberData.chatRoomId, + accountId: localMemberData.accountId, + account: account, + nick: localMemberData.nick, + notify: localMemberData.notify, + joinedAt: localMemberData.joinedAt, + breakUntil: localMemberData.breakUntil, + timeoutUntil: localMemberData.timeoutUntil, + status: null, + createdAt: localMemberData.createdAt, + updatedAt: localMemberData.updatedAt, + deletedAt: localMemberData.deletedAt, + chatRoom: null, + ); + } +} + +@riverpod +Future> chatroomInvites(Ref ref) async { + final client = ref.watch(apiClientProvider); + final resp = await client.get('/sphere/chat/invites'); + return resp.data + .map((e) => SnChatMember.fromJson(e)) + .cast() + .toList(); +} diff --git a/lib/screens/chat/chat.g.dart b/lib/pods/chat/chat_room.g.dart similarity index 99% rename from lib/screens/chat/chat.g.dart rename to lib/pods/chat/chat_room.g.dart index 4bf2ace9..5fc2a7fc 100644 --- a/lib/screens/chat/chat.g.dart +++ b/lib/pods/chat/chat_room.g.dart @@ -1,6 +1,6 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -part of 'chat.dart'; +part of 'chat_room.dart'; // ************************************************************************** // RiverpodGenerator diff --git a/lib/pods/chat/chat_rooms.dart b/lib/pods/chat/chat_rooms.dart deleted file mode 100644 index 3667bd81..00000000 --- a/lib/pods/chat/chat_rooms.dart +++ /dev/null @@ -1,5 +0,0 @@ -import "package:hooks_riverpod/hooks_riverpod.dart"; - -final isSyncingProvider = StateProvider.autoDispose((ref) => false); - -final flashingMessagesProvider = StateProvider>((ref) => {}); diff --git a/lib/pods/chat/chat_subscribe.dart b/lib/pods/chat/chat_subscribe.dart index 7961c003..2e515bcf 100644 --- a/lib/pods/chat/chat_subscribe.dart +++ b/lib/pods/chat/chat_subscribe.dart @@ -3,10 +3,10 @@ import "dart:convert"; import "package:flutter/material.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:island/models/chat.dart"; +import "package:island/pods/chat/chat_room.dart"; import "package:island/pods/lifecycle.dart"; import "package:island/pods/chat/messages_notifier.dart"; import "package:island/pods/websocket.dart"; -import "package:island/screens/chat/chat.dart"; import "package:island/widgets/chat/call_button.dart"; import "package:riverpod_annotation/riverpod_annotation.dart"; diff --git a/lib/pods/chat/messages_notifier.dart b/lib/pods/chat/messages_notifier.dart index 33e6c850..8b70ee3a 100644 --- a/lib/pods/chat/messages_notifier.dart +++ b/lib/pods/chat/messages_notifier.dart @@ -11,6 +11,7 @@ import "package:island/models/chat.dart"; import "package:island/models/file.dart"; import "package:island/models/poll.dart"; import "package:island/models/wallet.dart"; +import "package:island/pods/chat/chat_room.dart"; import "package:island/pods/database.dart"; import "package:island/pods/lifecycle.dart"; import "package:island/pods/network.dart"; @@ -19,8 +20,6 @@ import "package:island/talker.dart"; import "package:island/widgets/alert.dart"; import "package:riverpod_annotation/riverpod_annotation.dart"; import "package:uuid/uuid.dart"; -import "package:island/screens/chat/chat.dart"; -import "package:island/pods/chat/chat_rooms.dart"; import "package:island/screens/account/profile.dart"; part 'messages_notifier.g.dart'; diff --git a/lib/screens/chat/chat.dart b/lib/screens/chat/chat.dart index 5a373566..302f405d 100644 --- a/lib/screens/chat/chat.dart +++ b/lib/screens/chat/chat.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'package:dio/dio.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; @@ -7,10 +6,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/chat.dart'; -import 'package:island/models/file.dart'; -import 'package:island/models/account.dart'; -import 'package:island/database/drift_db.dart'; -import 'package:island/pods/database.dart'; import 'package:island/pods/chat/chat_summary.dart'; import 'package:island/pods/network.dart'; import 'package:island/pods/userinfo.dart'; @@ -25,11 +20,9 @@ import 'package:island/widgets/navigation/fab_menu.dart'; import 'package:island/widgets/response.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:relative_time/relative_time.dart'; -import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:super_sliver_list/super_sliver_list.dart'; - -part 'chat.g.dart'; +import 'package:island/pods/chat/chat_room.dart'; class ChatRoomListTile extends HookConsumerWidget { final SnChatRoom room; @@ -184,151 +177,6 @@ class ChatRoomListTile extends HookConsumerWidget { } } -@riverpod -class ChatRoomJoinedNotifier extends _$ChatRoomJoinedNotifier { - @override - Future> build() async { - final db = ref.watch(databaseProvider); - - try { - final localRoomsData = await db.select(db.chatRooms).get(); - if (localRoomsData.isNotEmpty) { - final localRooms = await Future.wait( - localRoomsData.map((row) async { - final membersRows = - await (db.select(db.chatMembers) - ..where((m) => m.chatRoomId.equals(row.id))).get(); - final members = - membersRows.map((mRow) { - final account = SnAccount.fromJson(mRow.account); - return SnChatMember( - id: mRow.id, - chatRoomId: mRow.chatRoomId, - accountId: mRow.accountId, - account: account, - nick: mRow.nick, - notify: mRow.notify, - joinedAt: mRow.joinedAt, - breakUntil: mRow.breakUntil, - timeoutUntil: mRow.timeoutUntil, - status: null, - createdAt: mRow.createdAt, - updatedAt: mRow.updatedAt, - deletedAt: mRow.deletedAt, - chatRoom: null, - ); - }).toList(); - return SnChatRoom( - id: row.id, - name: row.name, - description: row.description, - type: row.type, - isPublic: row.isPublic!, - isCommunity: row.isCommunity!, - picture: - row.picture != null - ? SnCloudFile.fromJson(row.picture!) - : null, - background: - row.background != null - ? SnCloudFile.fromJson(row.background!) - : null, - realmId: row.realmId, - accountId: row.accountId, - realm: null, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - deletedAt: row.deletedAt, - members: members, - ); - }), - ); - - // Background sync - Future(() async { - try { - final client = ref.read(apiClientProvider); - final resp = await client.get('/sphere/chat'); - final remoteRooms = - resp.data - .map((e) => SnChatRoom.fromJson(e)) - .cast() - .toList(); - await db.saveChatRooms(remoteRooms, override: true); - // Update state with fresh data - state = AsyncData(await _buildRoomsFromDb(db)); - } catch (_) {} - }).ignore(); - - return localRooms; - } - } catch (_) {} - - // Fallback to API - final client = ref.watch(apiClientProvider); - final resp = await client.get('/sphere/chat'); - final rooms = - resp.data - .map((e) => SnChatRoom.fromJson(e)) - .cast() - .toList(); - await db.saveChatRooms(rooms, override: true); - return rooms; - } - - Future> _buildRoomsFromDb(AppDatabase db) async { - final localRoomsData = await db.select(db.chatRooms).get(); - return Future.wait( - localRoomsData.map((row) async { - final membersRows = - await (db.select(db.chatMembers) - ..where((m) => m.chatRoomId.equals(row.id))).get(); - final members = - membersRows.map((mRow) { - final account = SnAccount.fromJson(mRow.account); - return SnChatMember( - id: mRow.id, - chatRoomId: mRow.chatRoomId, - accountId: mRow.accountId, - account: account, - nick: mRow.nick, - notify: mRow.notify, - joinedAt: mRow.joinedAt, - breakUntil: mRow.breakUntil, - timeoutUntil: mRow.timeoutUntil, - status: null, - createdAt: mRow.createdAt, - updatedAt: mRow.updatedAt, - deletedAt: mRow.deletedAt, - chatRoom: null, - ); - }).toList(); - return SnChatRoom( - id: row.id, - name: row.name, - description: row.description, - type: row.type, - isPublic: row.isPublic!, - isCommunity: row.isCommunity!, - picture: - row.picture != null ? SnCloudFile.fromJson(row.picture!) : null, - background: - row.background != null - ? SnCloudFile.fromJson(row.background!) - : null, - realmId: row.realmId, - accountId: row.accountId, - realm: null, - createdAt: row.createdAt, - updatedAt: row.updatedAt, - deletedAt: row.deletedAt, - members: members, - ); - }), - ); - } -} - class ChatListBodyWidget extends HookConsumerWidget { final bool isFloating; final TabController tabController; @@ -661,277 +509,6 @@ class ChatListScreen extends HookConsumerWidget { } } -@riverpod -class ChatRoomNotifier extends _$ChatRoomNotifier { - @override - Future build(String? identifier) async { - if (identifier == null) return null; - final db = ref.watch(databaseProvider); - - try { - // Try to get from local database first - final localRoomData = - await (db.select(db.chatRooms) - ..where((r) => r.id.equals(identifier))).getSingleOrNull(); - - if (localRoomData != null) { - // Fetch members for this room - final membersRows = - await (db.select(db.chatMembers) - ..where((m) => m.chatRoomId.equals(localRoomData.id))).get(); - final members = - membersRows.map((mRow) { - final account = SnAccount.fromJson(mRow.account); - return SnChatMember( - id: mRow.id, - chatRoomId: mRow.chatRoomId, - accountId: mRow.accountId, - account: account, - nick: mRow.nick, - notify: mRow.notify, - joinedAt: mRow.joinedAt, - breakUntil: mRow.breakUntil, - timeoutUntil: mRow.timeoutUntil, - status: null, - createdAt: mRow.createdAt, - updatedAt: mRow.updatedAt, - deletedAt: mRow.deletedAt, - chatRoom: null, - ); - }).toList(); - - final localRoom = SnChatRoom( - id: localRoomData.id, - name: localRoomData.name, - description: localRoomData.description, - type: localRoomData.type, - isPublic: localRoomData.isPublic!, - isCommunity: localRoomData.isCommunity!, - picture: - localRoomData.picture != null - ? SnCloudFile.fromJson(localRoomData.picture!) - : null, - background: - localRoomData.background != null - ? SnCloudFile.fromJson(localRoomData.background!) - : null, - realmId: localRoomData.realmId, - accountId: localRoomData.accountId, - realm: null, - createdAt: localRoomData.createdAt, - updatedAt: localRoomData.updatedAt, - deletedAt: localRoomData.deletedAt, - members: members, - ); - - // Background sync - Future(() async { - try { - final client = ref.read(apiClientProvider); - final resp = await client.get('/sphere/chat/$identifier'); - final remoteRoom = SnChatRoom.fromJson(resp.data); - await db.saveChatRooms([remoteRoom]); - // Update state with fresh data - state = AsyncData(await _buildRoomFromDb(db, identifier)); - } catch (_) {} - }).ignore(); - - return localRoom; - } - } catch (_) {} - - // Fallback to API - try { - final client = ref.watch(apiClientProvider); - final resp = await client.get('/sphere/chat/$identifier'); - final room = SnChatRoom.fromJson(resp.data); - await db.saveChatRooms([room]); - return room; - } catch (err) { - if (err is DioException && err.response?.statusCode == 404) { - return null; // Chat room not found - } - rethrow; // Rethrow other errors - } - } - - Future _buildRoomFromDb( - AppDatabase db, - String identifier, - ) async { - final localRoomData = - await (db.select(db.chatRooms) - ..where((r) => r.id.equals(identifier))).getSingleOrNull(); - - if (localRoomData == null) return null; - - final membersRows = - await (db.select(db.chatMembers) - ..where((m) => m.chatRoomId.equals(localRoomData.id))).get(); - final members = - membersRows.map((mRow) { - final account = SnAccount.fromJson(mRow.account); - return SnChatMember( - id: mRow.id, - chatRoomId: mRow.chatRoomId, - accountId: mRow.accountId, - account: account, - nick: mRow.nick, - notify: mRow.notify, - joinedAt: mRow.joinedAt, - breakUntil: mRow.breakUntil, - timeoutUntil: mRow.timeoutUntil, - status: null, - createdAt: mRow.createdAt, - updatedAt: mRow.updatedAt, - deletedAt: mRow.deletedAt, - chatRoom: null, - ); - }).toList(); - - return SnChatRoom( - id: localRoomData.id, - name: localRoomData.name, - description: localRoomData.description, - type: localRoomData.type, - isPublic: localRoomData.isPublic!, - isCommunity: localRoomData.isCommunity!, - picture: - localRoomData.picture != null - ? SnCloudFile.fromJson(localRoomData.picture!) - : null, - background: - localRoomData.background != null - ? SnCloudFile.fromJson(localRoomData.background!) - : null, - realmId: localRoomData.realmId, - accountId: localRoomData.accountId, - realm: null, - createdAt: localRoomData.createdAt, - updatedAt: localRoomData.updatedAt, - deletedAt: localRoomData.deletedAt, - members: members, - ); - } -} - -@riverpod -class ChatRoomIdentityNotifier extends _$ChatRoomIdentityNotifier { - @override - Future build(String? identifier) async { - if (identifier == null) return null; - final db = ref.watch(databaseProvider); - final userInfo = ref.watch(userInfoProvider); - - try { - // Try to get from local database first - if (userInfo.value != null) { - final localMemberData = - await (db.select(db.chatMembers) - ..where((m) => m.chatRoomId.equals(identifier)) - ..where((m) => m.accountId.equals(userInfo.value!.id))) - .getSingleOrNull(); - - if (localMemberData != null) { - final account = SnAccount.fromJson(localMemberData.account); - final localMember = SnChatMember( - id: localMemberData.id, - chatRoomId: localMemberData.chatRoomId, - accountId: localMemberData.accountId, - account: account, - nick: localMemberData.nick, - notify: localMemberData.notify, - joinedAt: localMemberData.joinedAt, - breakUntil: localMemberData.breakUntil, - timeoutUntil: localMemberData.timeoutUntil, - status: null, - createdAt: localMemberData.createdAt, - updatedAt: localMemberData.updatedAt, - deletedAt: localMemberData.deletedAt, - chatRoom: null, - ); - - // Background sync - Future(() async { - try { - final client = ref.read(apiClientProvider); - final resp = await client.get( - '/sphere/chat/$identifier/members/me', - ); - final remoteMember = SnChatMember.fromJson(resp.data); - await db.saveMember(remoteMember); - // Update state with fresh data - if (userInfo.value != null) { - state = AsyncData( - await _buildMemberFromDb(db, identifier, userInfo.value!.id), - ); - } - } catch (_) {} - }).ignore(); - - return localMember; - } - } - } catch (_) {} - - // Fallback to API - try { - final client = ref.watch(apiClientProvider); - final resp = await client.get('/sphere/chat/$identifier/members/me'); - final member = SnChatMember.fromJson(resp.data); - await db.saveMember(member); - return member; - } catch (err) { - if (err is DioException && err.response?.statusCode == 404) { - return null; // Chat member not found - } - rethrow; // Rethrow other errors - } - } - - Future _buildMemberFromDb( - AppDatabase db, - String identifier, - String accountId, - ) async { - final localMemberData = - await (db.select(db.chatMembers) - ..where((m) => m.chatRoomId.equals(identifier)) - ..where((m) => m.accountId.equals(accountId))) - .getSingleOrNull(); - - if (localMemberData == null) return null; - - final account = SnAccount.fromJson(localMemberData.account); - return SnChatMember( - id: localMemberData.id, - chatRoomId: localMemberData.chatRoomId, - accountId: localMemberData.accountId, - account: account, - nick: localMemberData.nick, - notify: localMemberData.notify, - joinedAt: localMemberData.joinedAt, - breakUntil: localMemberData.breakUntil, - timeoutUntil: localMemberData.timeoutUntil, - status: null, - createdAt: localMemberData.createdAt, - updatedAt: localMemberData.updatedAt, - deletedAt: localMemberData.deletedAt, - chatRoom: null, - ); - } -} - -@riverpod -Future> chatroomInvites(Ref ref) async { - final client = ref.watch(apiClientProvider); - final resp = await client.get('/sphere/chat/invites'); - return resp.data - .map((e) => SnChatMember.fromJson(e)) - .cast() - .toList(); -} - class _ChatInvitesSheet extends HookConsumerWidget { const _ChatInvitesSheet(); diff --git a/lib/screens/chat/chat_form.dart b/lib/screens/chat/chat_form.dart index 76fb2261..39cb6b1b 100644 --- a/lib/screens/chat/chat_form.dart +++ b/lib/screens/chat/chat_form.dart @@ -10,8 +10,8 @@ import 'package:image_picker/image_picker.dart'; import 'package:island/models/chat.dart'; import 'package:island/models/file.dart'; import 'package:island/models/realm.dart'; +import 'package:island/pods/chat/chat_room.dart'; import 'package:island/pods/network.dart'; -import 'package:island/screens/chat/chat.dart'; import 'package:island/screens/realm/realms.dart'; import 'package:island/services/file.dart'; import 'package:island/services/file_uploader.dart'; diff --git a/lib/screens/chat/public_room_preview.dart b/lib/screens/chat/public_room_preview.dart index d114133d..25fac06e 100644 --- a/lib/screens/chat/public_room_preview.dart +++ b/lib/screens/chat/public_room_preview.dart @@ -3,7 +3,7 @@ import "package:flutter_hooks/flutter_hooks.dart"; import "package:gap/gap.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:island/database/message.dart"; -import "package:island/screens/chat/chat.dart"; +import "package:island/pods/chat/chat_room.dart"; import "package:island/widgets/content/cloud_files.dart"; import "package:super_sliver_list/super_sliver_list.dart"; import "package:easy_localization/easy_localization.dart"; diff --git a/lib/screens/chat/room.dart b/lib/screens/chat/room.dart index b404e6cd..1304949a 100644 --- a/lib/screens/chat/room.dart +++ b/lib/screens/chat/room.dart @@ -14,7 +14,7 @@ import "package:island/models/chat.dart"; import "package:island/models/file.dart"; import "package:island/models/poll.dart"; import "package:island/models/wallet.dart"; -import "package:island/pods/chat/chat_rooms.dart"; +import "package:island/pods/chat/chat_room.dart"; import "package:island/pods/chat/chat_subscribe.dart"; import "package:island/pods/chat/messages_notifier.dart"; import "package:island/pods/network.dart"; @@ -23,7 +23,6 @@ import "package:island/pods/config.dart"; import "package:island/pods/userinfo.dart"; import "package:island/screens/chat/search_messages.dart"; import "package:island/services/file_uploader.dart"; -import "package:island/screens/chat/chat.dart"; import "package:island/services/responsive.dart"; import "package:island/widgets/alert.dart"; import "package:island/widgets/app_scaffold.dart"; diff --git a/lib/screens/chat/room_detail.dart b/lib/screens/chat/room_detail.dart index c719ac9c..6061a756 100644 --- a/lib/screens/chat/room_detail.dart +++ b/lib/screens/chat/room_detail.dart @@ -7,8 +7,8 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/chat.dart'; +import 'package:island/pods/chat/chat_room.dart'; import 'package:island/pods/network.dart'; -import 'package:island/screens/chat/chat.dart'; import 'package:island/widgets/account/account_pfc.dart'; import 'package:island/widgets/account/account_picker.dart'; import 'package:island/widgets/account/status.dart'; diff --git a/lib/screens/chat/search_messages.dart b/lib/screens/chat/search_messages.dart index 649b2dfb..54d99839 100644 --- a/lib/screens/chat/search_messages.dart +++ b/lib/screens/chat/search_messages.dart @@ -3,8 +3,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/pods/chat/chat_room.dart'; import 'package:island/pods/chat/messages_notifier.dart'; -import 'package:island/pods/chat/chat_rooms.dart'; import 'package:island/widgets/app_scaffold.dart'; import 'package:island/widgets/chat/message_list_tile.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; diff --git a/lib/widgets/chat/message_item.dart b/lib/widgets/chat/message_item.dart index 2915c70e..c0f6f1db 100644 --- a/lib/widgets/chat/message_item.dart +++ b/lib/widgets/chat/message_item.dart @@ -9,7 +9,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/database/message.dart'; -import 'package:island/pods/chat/chat_rooms.dart'; +import 'package:island/pods/chat/chat_room.dart'; import 'package:island/pods/chat/messages_notifier.dart'; import 'package:island/pods/translate.dart'; import 'package:island/pods/config.dart'; diff --git a/lib/widgets/chat/public_room_preview.dart b/lib/widgets/chat/public_room_preview.dart index ae588594..1c2e7f7e 100644 --- a/lib/widgets/chat/public_room_preview.dart +++ b/lib/widgets/chat/public_room_preview.dart @@ -6,6 +6,7 @@ import "package:gap/gap.dart"; import "package:hooks_riverpod/hooks_riverpod.dart"; import "package:island/database/message.dart"; import "package:island/models/chat.dart"; +import "package:island/pods/chat/chat_room.dart"; import "package:island/pods/chat/messages_notifier.dart"; import "package:island/pods/network.dart"; import "package:island/services/responsive.dart"; @@ -19,8 +20,6 @@ import "package:styled_widget/styled_widget.dart"; import "package:super_sliver_list/super_sliver_list.dart"; import "package:material_symbols_icons/symbols.dart"; -import "package:island/screens/chat/chat.dart"; - class PublicRoomPreview extends HookConsumerWidget { final String id; final SnChatRoom room; diff --git a/lib/widgets/share/share_sheet.dart b/lib/widgets/share/share_sheet.dart index 792b5910..91433fc7 100644 --- a/lib/widgets/share/share_sheet.dart +++ b/lib/widgets/share/share_sheet.dart @@ -4,6 +4,7 @@ import 'package:flutter/services.dart'; import 'package:gap/gap.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/pods/chat/chat_room.dart'; import 'package:island/pods/userinfo.dart'; import 'package:island/services/file_uploader.dart'; import 'package:island/widgets/alert.dart'; @@ -17,7 +18,6 @@ import 'package:island/pods/network.dart'; import 'package:mime/mime.dart'; import 'package:path/path.dart' as path; import 'package:island/models/chat.dart'; -import 'package:island/screens/chat/chat.dart'; import 'package:island/widgets/content/cloud_files.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:share_plus/share_plus.dart';