👽 Update message db to support server changes

This commit is contained in:
2025-11-30 21:57:35 +08:00
parent 27c7c8f039
commit c3a3be0807
12 changed files with 148 additions and 533 deletions

View File

@@ -1,5 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
@@ -199,24 +198,17 @@ Future<List<SnChatRoom>> chatroomsJoined(Ref ref) async {
final members =
membersRows.map((mRow) {
final account = SnAccount.fromJson(mRow.account);
SnAccountStatus? status;
if (mRow.status != null) {
status = SnAccountStatus.fromJson(jsonDecode(mRow.status!));
}
return SnChatMember(
id: mRow.id,
chatRoomId: mRow.chatRoomId,
accountId: mRow.accountId,
account: account,
nick: mRow.nick,
role: mRow.role,
notify: mRow.notify,
joinedAt: mRow.joinedAt,
breakUntil: mRow.breakUntil,
timeoutUntil: mRow.timeoutUntil,
isBot: mRow.isBot,
status: status,
lastTyped: mRow.lastTyped,
status: null,
createdAt: mRow.createdAt,
updatedAt: mRow.updatedAt,
deletedAt: mRow.deletedAt,
@@ -237,6 +229,7 @@ Future<List<SnChatRoom>> chatroomsJoined(Ref ref) async {
? SnCloudFile.fromJson(row.background!)
: null,
realmId: row.realmId,
accountId: row.accountId,
realm: null,
createdAt: row.createdAt,
updatedAt: row.updatedAt,
@@ -709,16 +702,6 @@ class _ChatInvitesSheet extends HookConsumerWidget {
subtitle: Row(
spacing: 6,
children: [
Flexible(
child:
Text(
invite.role >= 100
? 'permissionOwner'
: invite.role >= 50
? 'permissionModerator'
: 'permissionMember',
).tr(),
),
if (invite.chatRoom!.type == 1)
Badge(
label: const Text('directMessage').tr(),

View File

@@ -6,7 +6,7 @@ part of 'chat.dart';
// RiverpodGenerator
// **************************************************************************
String _$chatroomsJoinedHash() => r'9523efecd1869e7dd26adfc8ec87be48db19ee1c';
String _$chatroomsJoinedHash() => r'20a970e8456df088f41e783aa4443a59f56847db';
/// See also [chatroomsJoined].
@ProviderFor(chatroomsJoined)

View File

@@ -440,12 +440,17 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final chatIdentity = ref.watch(chatroomIdentityProvider(id));
final chatRoom = ref.watch(chatroomProvider(id));
final isManagable =
chatIdentity.value?.accountId == chatRoom.value?.accountId ||
chatRoom.value?.type == 1;
return PopupMenuButton(
icon: Icon(Icons.more_vert, shadows: [iconShadow]),
itemBuilder:
(context) => [
if ((chatIdentity.value?.role ?? 0) >= 50)
if (isManagable)
PopupMenuItem(
onTap: () {
showModalBottomSheet(
@@ -471,7 +476,7 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
],
),
),
if ((chatIdentity.value?.role ?? 0) >= 100)
if (isManagable)
PopupMenuItem(
child: Row(
children: [
@@ -644,6 +649,11 @@ class _ChatMemberListSheet extends HookConsumerWidget {
final memberNotifier = ref.read(chatMemberStateProvider(roomId).notifier);
final roomIdentity = ref.watch(chatroomIdentityProvider(roomId));
final chatRoom = ref.watch(chatroomProvider(roomId));
final isManagable =
chatRoom.value?.accountId == roomIdentity.value?.accountId ||
chatRoom.value?.type == 1;
useEffect(() {
Future(() {
@@ -752,45 +762,11 @@ class _ChatMemberListSheet extends HookConsumerWidget {
const Icon(Symbols.pending_actions, size: 20),
],
),
subtitle: Row(
children: [
Text(
member.role >= 100
? 'permissionOwner'
: member.role >= 50
? 'permissionModerator'
: 'permissionMember',
).tr(),
Text('·').bold().padding(horizontal: 6),
Expanded(child: Text("@${member.account.name}")),
],
),
subtitle: Text("@${member.account.name}"),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
if ((roomIdentity.value?.role ?? 0) >= 50)
IconButton(
icon: const Icon(Symbols.edit),
onPressed: () {
showModalBottomSheet(
isScrollControlled: true,
context: context,
builder:
(context) => _ChatMemberRoleSheet(
roomId: roomId,
member: member,
),
).then((value) {
if (value != null) {
// Refresh both providers
memberNotifier.reset();
memberNotifier.loadMore();
ref.invalidate(memberListProvider);
}
});
},
),
if ((roomIdentity.value?.role ?? 0) >= 50)
if (isManagable)
IconButton(
icon: const Icon(Symbols.delete),
onPressed: () {
@@ -829,120 +805,3 @@ class _ChatMemberListSheet extends HookConsumerWidget {
);
}
}
class _ChatMemberRoleSheet extends HookConsumerWidget {
final String roomId;
final SnChatMember member;
const _ChatMemberRoleSheet({required this.roomId, required this.member});
@override
Widget build(BuildContext context, WidgetRef ref) {
final roleController = useTextEditingController(
text: member.role.toString(),
);
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: EdgeInsets.only(
top: 16,
left: 20,
right: 16,
bottom: 12,
),
child: Row(
children: [
Text(
'memberRoleEdit'.tr(args: [member.account.name]),
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w600,
letterSpacing: -0.5,
),
),
const Spacer(),
IconButton(
icon: const Icon(Symbols.close),
onPressed: () => Navigator.pop(context),
style: IconButton.styleFrom(
minimumSize: const Size(36, 36),
),
),
],
),
),
const Divider(height: 1),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Autocomplete<int>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text.isEmpty) {
return const [100, 50, 0];
}
final int? value = int.tryParse(textEditingValue.text);
if (value == null) return const [100, 50, 0];
return [100, 50, 0].where(
(option) =>
option.toString().contains(textEditingValue.text),
);
},
onSelected: (int selection) {
roleController.text = selection.toString();
},
fieldViewBuilder: (
context,
controller,
focusNode,
onFieldSubmitted,
) {
return TextField(
controller: controller,
focusNode: focusNode,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'memberRole'.tr(),
helperText: 'memberRoleHint'.tr(),
),
onTapOutside: (event) => focusNode.unfocus(),
);
},
),
const Gap(16),
FilledButton.icon(
onPressed: () async {
try {
final newRole = int.parse(roleController.text);
if (newRole < 0 || newRole > 100) {
throw 'roleValidationHint'.tr();
}
final apiClient = ref.read(apiClientProvider);
await apiClient.patch(
'/sphere/chat/$roomId/members/${member.accountId}/role',
data: newRole,
);
if (context.mounted) Navigator.pop(context, true);
} catch (err) {
showErrorAlert(err);
}
},
icon: const Icon(Symbols.save),
label: const Text('saveChanges').tr(),
),
],
).padding(vertical: 16, horizontal: 24),
],
),
),
);
}
}

View File

@@ -7,7 +7,7 @@ part of 'notification.dart';
// **************************************************************************
String _$notificationUnreadCountNotifierHash() =>
r'08c773809958d96a7ce82acf04af1f9e0b23e119';
r'8bff5ad3b65389589b4112add3246afd9b8e38f9';
/// See also [NotificationUnreadCountNotifier].
@ProviderFor(NotificationUnreadCountNotifier)