♻️ Refactored the chat loading to use more local data
This commit is contained in:
@@ -358,8 +358,12 @@ class AppDatabase extends _$AppDatabase {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> saveChatRooms(List<SnChatRoom> rooms) async {
|
||||
Future<void> saveChatRooms(
|
||||
List<SnChatRoom> rooms, {
|
||||
bool override = false,
|
||||
}) async {
|
||||
await transaction(() async {
|
||||
if (override) {
|
||||
// 1. Identify rooms to remove
|
||||
final remoteRoomIds = rooms.map((r) => r.id).toSet();
|
||||
final currentRooms = await select(chatRooms).get();
|
||||
@@ -369,13 +373,15 @@ class AppDatabase extends _$AppDatabase {
|
||||
if (idsToRemove.isNotEmpty) {
|
||||
final idsList = idsToRemove.toList();
|
||||
// Remove messages
|
||||
await (delete(chatMessages)..where((t) => t.roomId.isIn(idsList))).go();
|
||||
await (delete(chatMessages)
|
||||
..where((t) => t.roomId.isIn(idsList))).go();
|
||||
// Remove members
|
||||
await (delete(chatMembers)
|
||||
..where((t) => t.chatRoomId.isIn(idsList))).go();
|
||||
// Remove rooms
|
||||
await (delete(chatRooms)..where((t) => t.id.isIn(idsList))).go();
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Upsert remote rooms
|
||||
await batch((batch) {
|
||||
|
||||
@@ -16,10 +16,9 @@ final currentSubscribedChatIdProvider = StateProvider<String?>((ref) => null);
|
||||
|
||||
@riverpod
|
||||
class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
|
||||
late final String _roomId;
|
||||
late final SnChatRoom _chatRoom;
|
||||
late final SnChatMember _chatIdentity;
|
||||
late final MessagesNotifier _messagesNotifier;
|
||||
late SnChatRoom _chatRoom;
|
||||
late SnChatMember _chatIdentity;
|
||||
late MessagesNotifier _messagesNotifier;
|
||||
|
||||
final List<SnChatMember> _typingStatuses = [];
|
||||
Timer? _typingCleanupTimer;
|
||||
@@ -29,10 +28,11 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
|
||||
|
||||
@override
|
||||
List<SnChatMember> build(String roomId) {
|
||||
_roomId = roomId;
|
||||
final ws = ref.watch(websocketProvider);
|
||||
final chatRoomAsync = ref.watch(chatroomProvider(roomId));
|
||||
final chatIdentityAsync = ref.watch(chatroomIdentityProvider(roomId));
|
||||
final chatRoomAsync = ref.watch(ChatRoomNotifierProvider(roomId));
|
||||
final chatIdentityAsync = ref.watch(
|
||||
ChatRoomIdentityNotifierProvider(roomId),
|
||||
);
|
||||
_messagesNotifier = ref.watch(messagesNotifierProvider(roomId).notifier);
|
||||
|
||||
if (chatRoomAsync.isLoading || chatIdentityAsync.isLoading) {
|
||||
@@ -199,7 +199,7 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
|
||||
jsonEncode(
|
||||
WebSocketPacket(
|
||||
type: 'messages.read',
|
||||
data: {'chat_room_id': _roomId},
|
||||
data: {'chat_room_id': roomId},
|
||||
endpoint: 'sphere',
|
||||
),
|
||||
),
|
||||
@@ -216,7 +216,7 @@ class ChatSubscribeNotifier extends _$ChatSubscribeNotifier {
|
||||
jsonEncode(
|
||||
WebSocketPacket(
|
||||
type: 'messages.typing',
|
||||
data: {'chat_room_id': _roomId},
|
||||
data: {'chat_room_id': roomId},
|
||||
endpoint: 'sphere',
|
||||
),
|
||||
),
|
||||
|
||||
@@ -7,7 +7,7 @@ part of 'chat_subscribe.dart';
|
||||
// **************************************************************************
|
||||
|
||||
String _$chatSubscribeNotifierHash() =>
|
||||
r'c605e0c9c45df64e5ba7b65f8de9b47bde8e2b3b';
|
||||
r'beec1ddf2e13f6d5af8a08c2c81eff740ae9b986';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
||||
@@ -27,10 +27,10 @@ part 'messages_notifier.g.dart';
|
||||
|
||||
@riverpod
|
||||
class MessagesNotifier extends _$MessagesNotifier {
|
||||
late final Dio _apiClient;
|
||||
late final AppDatabase _database;
|
||||
late final SnChatRoom _room;
|
||||
late final SnChatMember _identity;
|
||||
late Dio _apiClient;
|
||||
late AppDatabase _database;
|
||||
late SnChatRoom _room;
|
||||
late SnChatMember _identity;
|
||||
|
||||
final Map<String, LocalChatMessage> _pendingMessages = {};
|
||||
final Map<String, Map<int, double?>> _fileUploadProgress = {};
|
||||
@@ -39,7 +39,6 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
bool? _withLinks;
|
||||
bool? _withAttachments;
|
||||
|
||||
late final String _roomId;
|
||||
static const int _pageSize = 20;
|
||||
bool _hasMore = true;
|
||||
bool _isSyncing = false;
|
||||
@@ -48,15 +47,16 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
bool _allRemoteMessagesFetched = false;
|
||||
DateTime? _lastPauseTime;
|
||||
|
||||
late final Future<SnAccount?> Function(String) _fetchAccount;
|
||||
late Future<SnAccount?> Function(String) _fetchAccount;
|
||||
|
||||
@override
|
||||
FutureOr<List<LocalChatMessage>> build(String roomId) async {
|
||||
_roomId = roomId;
|
||||
_apiClient = ref.watch(apiClientProvider);
|
||||
_database = ref.watch(databaseProvider);
|
||||
final room = await ref.watch(chatroomProvider(roomId).future);
|
||||
final identity = await ref.watch(chatroomIdentityProvider(roomId).future);
|
||||
final room = await ref.watch(ChatRoomNotifierProvider(roomId).future);
|
||||
final identity = await ref.watch(
|
||||
ChatRoomIdentityNotifierProvider(roomId).future,
|
||||
);
|
||||
|
||||
// Initialize fetch account method for corrupted data recovery
|
||||
_fetchAccount = (String accountId) async {
|
||||
@@ -144,14 +144,14 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
|
||||
if (searchQuery != null && searchQuery.isNotEmpty) {
|
||||
dbMessages = await _database.searchMessages(
|
||||
_roomId,
|
||||
roomId,
|
||||
searchQuery,
|
||||
withAttachments: withAttachments,
|
||||
fetchAccount: _fetchAccount,
|
||||
);
|
||||
} else {
|
||||
final chatMessagesFromDb = await _database.getMessagesForRoom(
|
||||
_roomId,
|
||||
roomId,
|
||||
offset: offset,
|
||||
limit: take,
|
||||
);
|
||||
@@ -194,9 +194,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
|
||||
if (offset == 0) {
|
||||
final pendingForRoom =
|
||||
_pendingMessages.values
|
||||
.where((msg) => msg.roomId == _roomId)
|
||||
.toList();
|
||||
_pendingMessages.values.where((msg) => msg.roomId == roomId).toList();
|
||||
|
||||
final allMessages = [...pendingForRoom, ...uniqueMessages];
|
||||
_sortMessages(allMessages); // Use the helper function
|
||||
@@ -221,7 +219,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
}) async {
|
||||
talker.log('Getting all messages for jump from offset $offset, take $take');
|
||||
final chatMessagesFromDb = await _database.getMessagesForRoom(
|
||||
_roomId,
|
||||
roomId,
|
||||
offset: offset,
|
||||
limit: take,
|
||||
);
|
||||
@@ -245,9 +243,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
|
||||
if (offset == 0) {
|
||||
final pendingForRoom =
|
||||
_pendingMessages.values
|
||||
.where((msg) => msg.roomId == _roomId)
|
||||
.toList();
|
||||
_pendingMessages.values.where((msg) => msg.roomId == roomId).toList();
|
||||
|
||||
final allMessages = [...pendingForRoom, ...uniqueMessages];
|
||||
_sortMessages(allMessages);
|
||||
@@ -272,7 +268,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
talker.log('Fetching messages from API, offset $offset, take $take');
|
||||
if (_totalCount == null) {
|
||||
final response = await _apiClient.get(
|
||||
'/sphere/chat/$_roomId/messages',
|
||||
'/sphere/chat/$roomId/messages',
|
||||
queryParameters: {'offset': 0, 'take': 1},
|
||||
);
|
||||
_totalCount = int.parse(response.headers['x-total']?.firstOrNull ?? '0');
|
||||
@@ -284,7 +280,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
}
|
||||
|
||||
final response = await _apiClient.get(
|
||||
'/sphere/chat/$_roomId/messages',
|
||||
'/sphere/chat/$roomId/messages',
|
||||
queryParameters: {'offset': offset, 'take': take},
|
||||
);
|
||||
|
||||
@@ -546,7 +542,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
|
||||
final mockMessage = SnChatMessage(
|
||||
id: 'pending_$nonce',
|
||||
chatRoomId: _roomId,
|
||||
chatRoomId: roomId,
|
||||
senderId: _identity.id,
|
||||
content: content,
|
||||
createdAt: DateTime.now(),
|
||||
@@ -590,8 +586,8 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
|
||||
final response = await _apiClient.request(
|
||||
editingTo == null
|
||||
? '/sphere/chat/$_roomId/messages'
|
||||
: '/sphere/chat/$_roomId/messages/${editingTo.id}',
|
||||
? '/sphere/chat/$roomId/messages'
|
||||
: '/sphere/chat/$roomId/messages/${editingTo.id}',
|
||||
data: {
|
||||
'content': content,
|
||||
'attachments_id': cloudAttachments.map((e) => e.id).toList(),
|
||||
@@ -731,7 +727,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
}
|
||||
|
||||
Future<void> receiveMessage(SnChatMessage remoteMessage) async {
|
||||
if (remoteMessage.chatRoomId != _roomId) return;
|
||||
if (remoteMessage.chatRoomId != roomId) return;
|
||||
|
||||
// Block message receiving during jumps to prevent list resets
|
||||
if (_isJumping) {
|
||||
@@ -783,7 +779,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
}
|
||||
|
||||
Future<void> receiveMessageUpdate(SnChatMessage remoteMessage) async {
|
||||
if (remoteMessage.chatRoomId != _roomId) return;
|
||||
if (remoteMessage.chatRoomId != roomId) return;
|
||||
|
||||
// Block message updates during jumps to prevent list resets
|
||||
if (_isJumping) {
|
||||
@@ -883,7 +879,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
}
|
||||
|
||||
try {
|
||||
await _apiClient.delete('/sphere/chat/$_roomId/messages/$messageId');
|
||||
await _apiClient.delete('/sphere/chat/$roomId/messages/$messageId');
|
||||
await receiveMessageDeletion(messageId);
|
||||
} catch (err, stackTrace) {
|
||||
talker.log(
|
||||
@@ -991,7 +987,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
}
|
||||
|
||||
final response = await _apiClient.get(
|
||||
'/sphere/chat/$_roomId/messages/$messageId',
|
||||
'/sphere/chat/$roomId/messages/$messageId',
|
||||
);
|
||||
final remoteMessage = SnChatMessage.fromJson(response.data);
|
||||
final message = LocalChatMessage.fromRemoteMessage(
|
||||
@@ -1048,7 +1044,7 @@ class MessagesNotifier extends _$MessagesNotifier {
|
||||
final query = _database.customSelect(
|
||||
'SELECT COUNT(*) as count FROM chat_messages WHERE room_id = ? AND created_at > ?',
|
||||
variables: [
|
||||
Variable.withString(_roomId),
|
||||
Variable.withString(roomId),
|
||||
Variable.withDateTime(message.createdAt),
|
||||
],
|
||||
readsFrom: {_database.chatMessages},
|
||||
|
||||
@@ -6,7 +6,7 @@ part of 'messages_notifier.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$messagesNotifierHash() => r'27ce32c54e317a04e1d554ed4a70a24e4503fdd1';
|
||||
String _$messagesNotifierHash() => r'd76d799494b06fac2adc42d94b7ecd7b8d68c352';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
|
||||
@@ -9,6 +9,7 @@ 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';
|
||||
@@ -184,13 +185,100 @@ class ChatRoomListTile extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<List<SnChatRoom>> chatroomsJoined(Ref ref) async {
|
||||
class ChatRoomJoinedNotifier extends _$ChatRoomJoinedNotifier {
|
||||
@override
|
||||
Future<List<SnChatRoom>> 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<SnChatRoom>()
|
||||
.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<SnChatRoom>()
|
||||
.toList();
|
||||
await db.saveChatRooms(rooms, override: true);
|
||||
return rooms;
|
||||
}
|
||||
|
||||
Future<List<SnChatRoom>> _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)
|
||||
@@ -238,33 +326,7 @@ Future<List<SnChatRoom>> chatroomsJoined(Ref ref) async {
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
// 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<SnChatRoom>()
|
||||
.toList();
|
||||
await db.saveChatRooms(remoteRooms);
|
||||
ref.invalidateSelf();
|
||||
} 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<SnChatRoom>().toList();
|
||||
await db.saveChatRooms(rooms);
|
||||
return rooms;
|
||||
}
|
||||
|
||||
class ChatListBodyWidget extends HookConsumerWidget {
|
||||
@@ -281,7 +343,7 @@ class ChatListBodyWidget extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final chats = ref.watch(chatroomsJoinedProvider);
|
||||
final chats = ref.watch(chatRoomJoinedNotifierProvider);
|
||||
|
||||
Widget bodyWidget = Column(
|
||||
children: [
|
||||
@@ -304,7 +366,7 @@ class ChatListBodyWidget extends HookConsumerWidget {
|
||||
(items) => RefreshIndicator(
|
||||
onRefresh:
|
||||
() => Future.sync(() {
|
||||
ref.invalidate(chatroomsJoinedProvider);
|
||||
ref.invalidate(chatRoomJoinedNotifierProvider);
|
||||
}),
|
||||
child: SuperListView.builder(
|
||||
padding: EdgeInsets.only(bottom: 96),
|
||||
@@ -354,7 +416,7 @@ class ChatListBodyWidget extends HookConsumerWidget {
|
||||
(error, stack) => ResponseErrorWidget(
|
||||
error: error,
|
||||
onRetry: () {
|
||||
ref.invalidate(chatroomsJoinedProvider);
|
||||
ref.invalidate(chatRoomJoinedNotifierProvider);
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -431,7 +493,7 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
|
||||
// Listen for chat rooms refresh events
|
||||
final subscription = eventBus.on<ChatRoomsRefreshEvent>().listen((event) {
|
||||
ref.invalidate(chatroomsJoinedProvider);
|
||||
ref.invalidate(chatRoomJoinedNotifierProvider);
|
||||
});
|
||||
|
||||
return () {
|
||||
@@ -600,12 +662,91 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
@riverpod
|
||||
Future<SnChatRoom?> chatroom(Ref ref, String? identifier) async {
|
||||
class ChatRoomNotifier extends _$ChatRoomNotifier {
|
||||
@override
|
||||
Future<SnChatRoom?> 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');
|
||||
return SnChatRoom.fromJson(resp.data);
|
||||
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
|
||||
@@ -614,13 +755,132 @@ Future<SnChatRoom?> chatroom(Ref ref, String? identifier) async {
|
||||
}
|
||||
}
|
||||
|
||||
Future<SnChatRoom?> _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
|
||||
Future<SnChatMember?> chatroomIdentity(Ref ref, String? identifier) async {
|
||||
class ChatRoomIdentityNotifier extends _$ChatRoomIdentityNotifier {
|
||||
@override
|
||||
Future<SnChatMember?> 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');
|
||||
return SnChatMember.fromJson(resp.data);
|
||||
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
|
||||
@@ -629,6 +889,39 @@ Future<SnChatMember?> chatroomIdentity(Ref ref, String? identifier) async {
|
||||
}
|
||||
}
|
||||
|
||||
Future<SnChatMember?> _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<List<SnChatMember>> chatroomInvites(Ref ref) async {
|
||||
final client = ref.watch(apiClientProvider);
|
||||
@@ -651,7 +944,7 @@ class _ChatInvitesSheet extends HookConsumerWidget {
|
||||
final client = ref.read(apiClientProvider);
|
||||
await client.post('/sphere/chat/invites/${invite.chatRoom!.id}/accept');
|
||||
ref.invalidate(chatroomInvitesProvider);
|
||||
ref.invalidate(chatroomsJoinedProvider);
|
||||
ref.invalidate(chatRoomJoinedNotifierProvider);
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
}
|
||||
|
||||
@@ -6,26 +6,46 @@ part of 'chat.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$chatroomsJoinedHash() => r'50abce4f03a7a8509f16d5ad0b1dbf8e3aeb73b6';
|
||||
String _$chatroomInvitesHash() => r'5cd6391b09c5517ede19bacce43b45c8d71dd087';
|
||||
|
||||
/// See also [chatroomsJoined].
|
||||
@ProviderFor(chatroomsJoined)
|
||||
final chatroomsJoinedProvider =
|
||||
AutoDisposeFutureProvider<List<SnChatRoom>>.internal(
|
||||
chatroomsJoined,
|
||||
name: r'chatroomsJoinedProvider',
|
||||
/// See also [chatroomInvites].
|
||||
@ProviderFor(chatroomInvites)
|
||||
final chatroomInvitesProvider =
|
||||
AutoDisposeFutureProvider<List<SnChatMember>>.internal(
|
||||
chatroomInvites,
|
||||
name: r'chatroomInvitesProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$chatroomsJoinedHash,
|
||||
: _$chatroomInvitesHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef ChatroomsJoinedRef = AutoDisposeFutureProviderRef<List<SnChatRoom>>;
|
||||
String _$chatroomHash() => r'2b17d94728026420d18d6c383d2400cf4a070913';
|
||||
typedef ChatroomInvitesRef = AutoDisposeFutureProviderRef<List<SnChatMember>>;
|
||||
String _$chatRoomJoinedNotifierHash() =>
|
||||
r'c8092225ba0d9c08b2b5bca6f800f1877303b4ff';
|
||||
|
||||
/// See also [ChatRoomJoinedNotifier].
|
||||
@ProviderFor(ChatRoomJoinedNotifier)
|
||||
final chatRoomJoinedNotifierProvider = AutoDisposeAsyncNotifierProvider<
|
||||
ChatRoomJoinedNotifier,
|
||||
List<SnChatRoom>
|
||||
>.internal(
|
||||
ChatRoomJoinedNotifier.new,
|
||||
name: r'chatRoomJoinedNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$chatRoomJoinedNotifierHash,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
typedef _$ChatRoomJoinedNotifier = AutoDisposeAsyncNotifier<List<SnChatRoom>>;
|
||||
String _$chatRoomNotifierHash() => r'978bd602cf5e93e60e3c7b9f5799d46a87495c79';
|
||||
|
||||
/// Copied from Dart SDK
|
||||
class _SystemHash {
|
||||
@@ -48,141 +68,30 @@ class _SystemHash {
|
||||
}
|
||||
}
|
||||
|
||||
/// See also [chatroom].
|
||||
@ProviderFor(chatroom)
|
||||
const chatroomProvider = ChatroomFamily();
|
||||
abstract class _$ChatRoomNotifier
|
||||
extends BuildlessAutoDisposeAsyncNotifier<SnChatRoom?> {
|
||||
late final String? identifier;
|
||||
|
||||
/// See also [chatroom].
|
||||
class ChatroomFamily extends Family<AsyncValue<SnChatRoom?>> {
|
||||
/// See also [chatroom].
|
||||
const ChatroomFamily();
|
||||
FutureOr<SnChatRoom?> build(String? identifier);
|
||||
}
|
||||
|
||||
/// See also [chatroom].
|
||||
ChatroomProvider call(String? identifier) {
|
||||
return ChatroomProvider(identifier);
|
||||
/// See also [ChatRoomNotifier].
|
||||
@ProviderFor(ChatRoomNotifier)
|
||||
const chatRoomNotifierProvider = ChatRoomNotifierFamily();
|
||||
|
||||
/// See also [ChatRoomNotifier].
|
||||
class ChatRoomNotifierFamily extends Family<AsyncValue<SnChatRoom?>> {
|
||||
/// See also [ChatRoomNotifier].
|
||||
const ChatRoomNotifierFamily();
|
||||
|
||||
/// See also [ChatRoomNotifier].
|
||||
ChatRoomNotifierProvider call(String? identifier) {
|
||||
return ChatRoomNotifierProvider(identifier);
|
||||
}
|
||||
|
||||
@override
|
||||
ChatroomProvider getProviderOverride(covariant ChatroomProvider provider) {
|
||||
return call(provider.identifier);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'chatroomProvider';
|
||||
}
|
||||
|
||||
/// See also [chatroom].
|
||||
class ChatroomProvider extends AutoDisposeFutureProvider<SnChatRoom?> {
|
||||
/// See also [chatroom].
|
||||
ChatroomProvider(String? identifier)
|
||||
: this._internal(
|
||||
(ref) => chatroom(ref as ChatroomRef, identifier),
|
||||
from: chatroomProvider,
|
||||
name: r'chatroomProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$chatroomHash,
|
||||
dependencies: ChatroomFamily._dependencies,
|
||||
allTransitiveDependencies: ChatroomFamily._allTransitiveDependencies,
|
||||
identifier: identifier,
|
||||
);
|
||||
|
||||
ChatroomProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.identifier,
|
||||
}) : super.internal();
|
||||
|
||||
final String? identifier;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<SnChatRoom?> Function(ChatroomRef provider) create,
|
||||
) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: ChatroomProvider._internal(
|
||||
(ref) => create(ref as ChatroomRef),
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
identifier: identifier,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<SnChatRoom?> createElement() {
|
||||
return _ChatroomProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ChatroomProvider && other.identifier == identifier;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, identifier.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin ChatroomRef on AutoDisposeFutureProviderRef<SnChatRoom?> {
|
||||
/// The parameter `identifier` of this provider.
|
||||
String? get identifier;
|
||||
}
|
||||
|
||||
class _ChatroomProviderElement
|
||||
extends AutoDisposeFutureProviderElement<SnChatRoom?>
|
||||
with ChatroomRef {
|
||||
_ChatroomProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String? get identifier => (origin as ChatroomProvider).identifier;
|
||||
}
|
||||
|
||||
String _$chatroomIdentityHash() => r'35e19a5a3e31752c79b97ba0358a7ec8fb8f6e99';
|
||||
|
||||
/// See also [chatroomIdentity].
|
||||
@ProviderFor(chatroomIdentity)
|
||||
const chatroomIdentityProvider = ChatroomIdentityFamily();
|
||||
|
||||
/// See also [chatroomIdentity].
|
||||
class ChatroomIdentityFamily extends Family<AsyncValue<SnChatMember?>> {
|
||||
/// See also [chatroomIdentity].
|
||||
const ChatroomIdentityFamily();
|
||||
|
||||
/// See also [chatroomIdentity].
|
||||
ChatroomIdentityProvider call(String? identifier) {
|
||||
return ChatroomIdentityProvider(identifier);
|
||||
}
|
||||
|
||||
@override
|
||||
ChatroomIdentityProvider getProviderOverride(
|
||||
covariant ChatroomIdentityProvider provider,
|
||||
ChatRoomNotifierProvider getProviderOverride(
|
||||
covariant ChatRoomNotifierProvider provider,
|
||||
) {
|
||||
return call(provider.identifier);
|
||||
}
|
||||
@@ -199,29 +108,30 @@ class ChatroomIdentityFamily extends Family<AsyncValue<SnChatMember?>> {
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'chatroomIdentityProvider';
|
||||
String? get name => r'chatRoomNotifierProvider';
|
||||
}
|
||||
|
||||
/// See also [chatroomIdentity].
|
||||
class ChatroomIdentityProvider
|
||||
extends AutoDisposeFutureProvider<SnChatMember?> {
|
||||
/// See also [chatroomIdentity].
|
||||
ChatroomIdentityProvider(String? identifier)
|
||||
/// See also [ChatRoomNotifier].
|
||||
class ChatRoomNotifierProvider
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderImpl<ChatRoomNotifier, SnChatRoom?> {
|
||||
/// See also [ChatRoomNotifier].
|
||||
ChatRoomNotifierProvider(String? identifier)
|
||||
: this._internal(
|
||||
(ref) => chatroomIdentity(ref as ChatroomIdentityRef, identifier),
|
||||
from: chatroomIdentityProvider,
|
||||
name: r'chatroomIdentityProvider',
|
||||
() => ChatRoomNotifier()..identifier = identifier,
|
||||
from: chatRoomNotifierProvider,
|
||||
name: r'chatRoomNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$chatroomIdentityHash,
|
||||
dependencies: ChatroomIdentityFamily._dependencies,
|
||||
: _$chatRoomNotifierHash,
|
||||
dependencies: ChatRoomNotifierFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
ChatroomIdentityFamily._allTransitiveDependencies,
|
||||
ChatRoomNotifierFamily._allTransitiveDependencies,
|
||||
identifier: identifier,
|
||||
);
|
||||
|
||||
ChatroomIdentityProvider._internal(
|
||||
ChatRoomNotifierProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
@@ -234,13 +144,16 @@ class ChatroomIdentityProvider
|
||||
final String? identifier;
|
||||
|
||||
@override
|
||||
Override overrideWith(
|
||||
FutureOr<SnChatMember?> Function(ChatroomIdentityRef provider) create,
|
||||
) {
|
||||
FutureOr<SnChatRoom?> runNotifierBuild(covariant ChatRoomNotifier notifier) {
|
||||
return notifier.build(identifier);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(ChatRoomNotifier Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: ChatroomIdentityProvider._internal(
|
||||
(ref) => create(ref as ChatroomIdentityRef),
|
||||
override: ChatRoomNotifierProvider._internal(
|
||||
() => create()..identifier = identifier,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
@@ -252,13 +165,14 @@ class ChatroomIdentityProvider
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeFutureProviderElement<SnChatMember?> createElement() {
|
||||
return _ChatroomIdentityProviderElement(this);
|
||||
AutoDisposeAsyncNotifierProviderElement<ChatRoomNotifier, SnChatRoom?>
|
||||
createElement() {
|
||||
return _ChatRoomNotifierProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ChatroomIdentityProvider && other.identifier == identifier;
|
||||
return other is ChatRoomNotifierProvider && other.identifier == identifier;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -272,38 +186,170 @@ class ChatroomIdentityProvider
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
mixin ChatroomIdentityRef on AutoDisposeFutureProviderRef<SnChatMember?> {
|
||||
mixin ChatRoomNotifierRef on AutoDisposeAsyncNotifierProviderRef<SnChatRoom?> {
|
||||
/// The parameter `identifier` of this provider.
|
||||
String? get identifier;
|
||||
}
|
||||
|
||||
class _ChatroomIdentityProviderElement
|
||||
extends AutoDisposeFutureProviderElement<SnChatMember?>
|
||||
with ChatroomIdentityRef {
|
||||
_ChatroomIdentityProviderElement(super.provider);
|
||||
class _ChatRoomNotifierProviderElement
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderElement<ChatRoomNotifier, SnChatRoom?>
|
||||
with ChatRoomNotifierRef {
|
||||
_ChatRoomNotifierProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String? get identifier => (origin as ChatroomIdentityProvider).identifier;
|
||||
String? get identifier => (origin as ChatRoomNotifierProvider).identifier;
|
||||
}
|
||||
|
||||
String _$chatroomInvitesHash() => r'5cd6391b09c5517ede19bacce43b45c8d71dd087';
|
||||
String _$chatRoomIdentityNotifierHash() =>
|
||||
r'27c17d55366d39be81d7209837e5c01f80a68a24';
|
||||
|
||||
/// See also [chatroomInvites].
|
||||
@ProviderFor(chatroomInvites)
|
||||
final chatroomInvitesProvider =
|
||||
AutoDisposeFutureProvider<List<SnChatMember>>.internal(
|
||||
chatroomInvites,
|
||||
name: r'chatroomInvitesProvider',
|
||||
abstract class _$ChatRoomIdentityNotifier
|
||||
extends BuildlessAutoDisposeAsyncNotifier<SnChatMember?> {
|
||||
late final String? identifier;
|
||||
|
||||
FutureOr<SnChatMember?> build(String? identifier);
|
||||
}
|
||||
|
||||
/// See also [ChatRoomIdentityNotifier].
|
||||
@ProviderFor(ChatRoomIdentityNotifier)
|
||||
const chatRoomIdentityNotifierProvider = ChatRoomIdentityNotifierFamily();
|
||||
|
||||
/// See also [ChatRoomIdentityNotifier].
|
||||
class ChatRoomIdentityNotifierFamily extends Family<AsyncValue<SnChatMember?>> {
|
||||
/// See also [ChatRoomIdentityNotifier].
|
||||
const ChatRoomIdentityNotifierFamily();
|
||||
|
||||
/// See also [ChatRoomIdentityNotifier].
|
||||
ChatRoomIdentityNotifierProvider call(String? identifier) {
|
||||
return ChatRoomIdentityNotifierProvider(identifier);
|
||||
}
|
||||
|
||||
@override
|
||||
ChatRoomIdentityNotifierProvider getProviderOverride(
|
||||
covariant ChatRoomIdentityNotifierProvider provider,
|
||||
) {
|
||||
return call(provider.identifier);
|
||||
}
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _dependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
|
||||
|
||||
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
|
||||
|
||||
@override
|
||||
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
|
||||
_allTransitiveDependencies;
|
||||
|
||||
@override
|
||||
String? get name => r'chatRoomIdentityNotifierProvider';
|
||||
}
|
||||
|
||||
/// See also [ChatRoomIdentityNotifier].
|
||||
class ChatRoomIdentityNotifierProvider
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderImpl<
|
||||
ChatRoomIdentityNotifier,
|
||||
SnChatMember?
|
||||
> {
|
||||
/// See also [ChatRoomIdentityNotifier].
|
||||
ChatRoomIdentityNotifierProvider(String? identifier)
|
||||
: this._internal(
|
||||
() => ChatRoomIdentityNotifier()..identifier = identifier,
|
||||
from: chatRoomIdentityNotifierProvider,
|
||||
name: r'chatRoomIdentityNotifierProvider',
|
||||
debugGetCreateSourceHash:
|
||||
const bool.fromEnvironment('dart.vm.product')
|
||||
? null
|
||||
: _$chatroomInvitesHash,
|
||||
: _$chatRoomIdentityNotifierHash,
|
||||
dependencies: ChatRoomIdentityNotifierFamily._dependencies,
|
||||
allTransitiveDependencies:
|
||||
ChatRoomIdentityNotifierFamily._allTransitiveDependencies,
|
||||
identifier: identifier,
|
||||
);
|
||||
|
||||
ChatRoomIdentityNotifierProvider._internal(
|
||||
super._createNotifier, {
|
||||
required super.name,
|
||||
required super.dependencies,
|
||||
required super.allTransitiveDependencies,
|
||||
required super.debugGetCreateSourceHash,
|
||||
required super.from,
|
||||
required this.identifier,
|
||||
}) : super.internal();
|
||||
|
||||
final String? identifier;
|
||||
|
||||
@override
|
||||
FutureOr<SnChatMember?> runNotifierBuild(
|
||||
covariant ChatRoomIdentityNotifier notifier,
|
||||
) {
|
||||
return notifier.build(identifier);
|
||||
}
|
||||
|
||||
@override
|
||||
Override overrideWith(ChatRoomIdentityNotifier Function() create) {
|
||||
return ProviderOverride(
|
||||
origin: this,
|
||||
override: ChatRoomIdentityNotifierProvider._internal(
|
||||
() => create()..identifier = identifier,
|
||||
from: from,
|
||||
name: null,
|
||||
dependencies: null,
|
||||
allTransitiveDependencies: null,
|
||||
debugGetCreateSourceHash: null,
|
||||
identifier: identifier,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
AutoDisposeAsyncNotifierProviderElement<
|
||||
ChatRoomIdentityNotifier,
|
||||
SnChatMember?
|
||||
>
|
||||
createElement() {
|
||||
return _ChatRoomIdentityNotifierProviderElement(this);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is ChatRoomIdentityNotifierProvider &&
|
||||
other.identifier == identifier;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
var hash = _SystemHash.combine(0, runtimeType.hashCode);
|
||||
hash = _SystemHash.combine(hash, identifier.hashCode);
|
||||
|
||||
return _SystemHash.finish(hash);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||
// ignore: unused_element
|
||||
typedef ChatroomInvitesRef = AutoDisposeFutureProviderRef<List<SnChatMember>>;
|
||||
mixin ChatRoomIdentityNotifierRef
|
||||
on AutoDisposeAsyncNotifierProviderRef<SnChatMember?> {
|
||||
/// The parameter `identifier` of this provider.
|
||||
String? get identifier;
|
||||
}
|
||||
|
||||
class _ChatRoomIdentityNotifierProviderElement
|
||||
extends
|
||||
AutoDisposeAsyncNotifierProviderElement<
|
||||
ChatRoomIdentityNotifier,
|
||||
SnChatMember?
|
||||
>
|
||||
with ChatRoomIdentityNotifierRef {
|
||||
_ChatRoomIdentityNotifierProviderElement(super.provider);
|
||||
|
||||
@override
|
||||
String? get identifier =>
|
||||
(origin as ChatRoomIdentityNotifierProvider).identifier;
|
||||
}
|
||||
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package
|
||||
|
||||
@@ -47,7 +47,7 @@ class EditChatScreen extends HookConsumerWidget {
|
||||
final isPublic = useState(true);
|
||||
final isCommunity = useState(false);
|
||||
|
||||
final chat = ref.watch(chatroomProvider(id));
|
||||
final chat = ref.watch(ChatRoomNotifierProvider(id));
|
||||
|
||||
final joinedRealms = ref.watch(realmsJoinedProvider);
|
||||
final currentRealm = useState<SnRealm?>(null);
|
||||
|
||||
@@ -203,7 +203,7 @@ class PublicRoomPreview extends HookConsumerWidget {
|
||||
showLoadingModal(context);
|
||||
final apiClient = ref.read(apiClientProvider);
|
||||
await apiClient.post('/sphere/chat/${room.id}/members/me');
|
||||
ref.invalidate(chatroomIdentityProvider(id));
|
||||
ref.invalidate(ChatRoomIdentityNotifierProvider(id));
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
} finally {
|
||||
|
||||
@@ -20,6 +20,7 @@ import "package:island/pods/chat/messages_notifier.dart";
|
||||
import "package:island/pods/network.dart";
|
||||
import "package:island/pods/chat/chat_online_count.dart";
|
||||
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";
|
||||
@@ -48,8 +49,8 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final chatRoom = ref.watch(chatroomProvider(id));
|
||||
final chatIdentity = ref.watch(chatroomIdentityProvider(id));
|
||||
final chatRoom = ref.watch(ChatRoomNotifierProvider(id));
|
||||
final chatIdentity = ref.watch(ChatRoomIdentityNotifierProvider(id));
|
||||
final isSyncing = ref.watch(isSyncingProvider);
|
||||
final onlineCount = ref.watch(chatOnlineCountNotifierProvider(id));
|
||||
final settings = ref.watch(appSettingsNotifierProvider);
|
||||
@@ -100,7 +101,9 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
await apiClient.post(
|
||||
'/sphere/chat/${room.id}/members/me',
|
||||
);
|
||||
ref.invalidate(chatroomIdentityProvider(id));
|
||||
ref.invalidate(
|
||||
ChatRoomIdentityNotifierProvider(id),
|
||||
);
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
} finally {
|
||||
@@ -129,7 +132,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
appBar: AppBar(leading: const PageBackButton()),
|
||||
body: ResponseErrorWidget(
|
||||
error: error,
|
||||
onRetry: () => ref.refresh(chatroomProvider(id)),
|
||||
onRetry: () => ref.refresh(ChatRoomNotifierProvider(id)),
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -408,6 +411,14 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
|
||||
final compactHeader = isWideScreen(context);
|
||||
|
||||
final userInfo = ref.watch(userInfoProvider);
|
||||
|
||||
List<SnChatMember> getValidMembers(List<SnChatMember> members) {
|
||||
return members
|
||||
.where((member) => member.accountId != userInfo.value?.id)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Widget comfortHeaderWidget(SnChatRoom? room) => Column(
|
||||
spacing: 4,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
@@ -428,9 +439,9 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
(room!.type == 1 && room.picture?.id == null)
|
||||
? SplitAvatarWidget(
|
||||
filesId:
|
||||
room.members!
|
||||
.map((e) => e.account.profile.picture?.id)
|
||||
.toList(),
|
||||
getValidMembers(
|
||||
room.members!,
|
||||
).map((e) => e.account.profile.picture?.id).toList(),
|
||||
)
|
||||
: room.picture?.id != null
|
||||
? ProfilePictureWidget(
|
||||
@@ -447,7 +458,9 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
),
|
||||
Text(
|
||||
(room.type == 1 && room.name == null)
|
||||
? room.members!.map((e) => e.account.nick).join(', ')
|
||||
? getValidMembers(
|
||||
room.members!,
|
||||
).map((e) => e.account.nick).join(', ')
|
||||
: room.name!,
|
||||
).fontSize(15),
|
||||
],
|
||||
@@ -473,9 +486,9 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
(room!.type == 1 && room.picture?.id == null)
|
||||
? SplitAvatarWidget(
|
||||
filesId:
|
||||
room.members!
|
||||
.map((e) => e.account.profile.picture?.id)
|
||||
.toList(),
|
||||
getValidMembers(
|
||||
room.members!,
|
||||
).map((e) => e.account.profile.picture?.id).toList(),
|
||||
)
|
||||
: room.picture?.id != null
|
||||
? ProfilePictureWidget(
|
||||
@@ -492,7 +505,9 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
),
|
||||
Text(
|
||||
(room.type == 1 && room.name == null)
|
||||
? room.members!.map((e) => e.account.nick).join(', ')
|
||||
? getValidMembers(
|
||||
room.members!,
|
||||
).map((e) => e.account.nick).join(', ')
|
||||
: room.name!,
|
||||
).fontSize(19),
|
||||
],
|
||||
|
||||
@@ -39,8 +39,8 @@ class ChatDetailScreen extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final roomState = ref.watch(chatroomProvider(id));
|
||||
final roomIdentity = ref.watch(chatroomIdentityProvider(id));
|
||||
final roomState = ref.watch(ChatRoomNotifierProvider(id));
|
||||
final roomIdentity = ref.watch(ChatRoomIdentityNotifierProvider(id));
|
||||
final totalMessages = ref.watch(totalMessagesCountProvider(id));
|
||||
|
||||
const kNotifyLevelText = [
|
||||
@@ -56,7 +56,7 @@ class ChatDetailScreen extends HookConsumerWidget {
|
||||
'/sphere/chat/$id/members/me/notify',
|
||||
data: {'notify_level': level},
|
||||
);
|
||||
ref.invalidate(chatroomIdentityProvider(id));
|
||||
ref.invalidate(ChatRoomIdentityNotifierProvider(id));
|
||||
if (context.mounted) {
|
||||
showSnackBar(
|
||||
'chatNotifyLevelUpdated'.tr(args: [kNotifyLevelText[level].tr()]),
|
||||
@@ -74,7 +74,7 @@ class ChatDetailScreen extends HookConsumerWidget {
|
||||
'/sphere/chat/$id/members/me/notify',
|
||||
data: {'break_until': until.toUtc().toIso8601String()},
|
||||
);
|
||||
ref.invalidate(chatroomProvider(id));
|
||||
ref.invalidate(ChatRoomNotifierProvider(id));
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
}
|
||||
@@ -439,8 +439,8 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final chatIdentity = ref.watch(chatroomIdentityProvider(id));
|
||||
final chatRoom = ref.watch(chatroomProvider(id));
|
||||
final chatIdentity = ref.watch(ChatRoomIdentityNotifierProvider(id));
|
||||
final chatRoom = ref.watch(ChatRoomNotifierProvider(id));
|
||||
|
||||
final isManagable =
|
||||
chatIdentity.value?.accountId == chatRoom.value?.accountId ||
|
||||
@@ -461,7 +461,7 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
|
||||
).then((value) {
|
||||
if (value != null) {
|
||||
// Invalidate to refresh room data after edit
|
||||
ref.invalidate(chatroomProvider(id));
|
||||
ref.invalidate(ChatRoomNotifierProvider(id));
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -497,7 +497,7 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
|
||||
if (confirm) {
|
||||
final client = ref.watch(apiClientProvider);
|
||||
await client.delete('/sphere/chat/$id');
|
||||
ref.invalidate(chatroomsJoinedProvider);
|
||||
ref.invalidate(chatRoomJoinedNotifierProvider);
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
@@ -530,7 +530,7 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
|
||||
if (confirm) {
|
||||
final client = ref.watch(apiClientProvider);
|
||||
await client.delete('/sphere/chat/$id/members/me');
|
||||
ref.invalidate(chatroomsJoinedProvider);
|
||||
ref.invalidate(chatRoomJoinedNotifierProvider);
|
||||
if (context.mounted) {
|
||||
context.pop();
|
||||
}
|
||||
@@ -648,8 +648,8 @@ class _ChatMemberListSheet extends HookConsumerWidget {
|
||||
final memberState = ref.watch(chatMemberStateProvider(roomId));
|
||||
final memberNotifier = ref.read(chatMemberStateProvider(roomId).notifier);
|
||||
|
||||
final roomIdentity = ref.watch(chatroomIdentityProvider(roomId));
|
||||
final chatRoom = ref.watch(chatroomProvider(roomId));
|
||||
final roomIdentity = ref.watch(ChatRoomIdentityNotifierProvider(roomId));
|
||||
final chatRoom = ref.watch(ChatRoomNotifierProvider(roomId));
|
||||
|
||||
final isManagable =
|
||||
chatRoom.value?.accountId == roomIdentity.value?.accountId ||
|
||||
|
||||
@@ -17,6 +17,7 @@ import "package:island/models/wallet.dart";
|
||||
import "package:island/models/realm.dart";
|
||||
import "package:island/models/sticker.dart";
|
||||
import "package:island/pods/config.dart";
|
||||
import "package:island/pods/userinfo.dart";
|
||||
import "package:island/services/autocomplete_service.dart";
|
||||
import "package:island/services/responsive.dart";
|
||||
import "package:island/widgets/content/attachment_preview.dart";
|
||||
@@ -344,6 +345,14 @@ class ChatInput extends HookConsumerWidget {
|
||||
final double rightMargin = isWideScreen(context) ? leftMargin + 8 : 16;
|
||||
const double bottomMargin = 16;
|
||||
|
||||
final userInfo = ref.watch(userInfoProvider);
|
||||
|
||||
List<SnChatMember> getValidMembers(List<SnChatMember> members) {
|
||||
return members
|
||||
.where((member) => member.accountId != userInfo.value?.id)
|
||||
.toList();
|
||||
}
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.only(
|
||||
left: leftMargin,
|
||||
@@ -878,9 +887,9 @@ class ChatInput extends HookConsumerWidget {
|
||||
(chatRoom.type == 1 && chatRoom.name == null)
|
||||
? 'chatDirectMessageHint'.tr(
|
||||
args: [
|
||||
chatRoom.members!
|
||||
.map((e) => e.account.nick)
|
||||
.join(', '),
|
||||
getValidMembers(
|
||||
chatRoom.members!,
|
||||
).map((e) => e.account.nick).join(', '),
|
||||
],
|
||||
)
|
||||
: 'chatMessageHint'.tr(
|
||||
|
||||
@@ -205,7 +205,7 @@ class PublicRoomPreview extends HookConsumerWidget {
|
||||
showLoadingModal(context);
|
||||
final apiClient = ref.read(apiClientProvider);
|
||||
await apiClient.post('/sphere/chat/${room.id}/members/me');
|
||||
ref.invalidate(chatroomIdentityProvider(id));
|
||||
ref.invalidate(ChatRoomIdentityNotifierProvider(id));
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
} finally {
|
||||
|
||||
@@ -664,7 +664,7 @@ class _ChatRoomsList extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final chatRooms = ref.watch(chatroomsJoinedProvider);
|
||||
final chatRooms = ref.watch(chatRoomJoinedNotifierProvider);
|
||||
|
||||
return chatRooms.when(
|
||||
data: (rooms) {
|
||||
|
||||
Reference in New Issue
Block a user