👽 Support the latest API
This commit is contained in:
@ -130,13 +130,13 @@ class AccountProfileScreen extends HookConsumerWidget {
|
||||
SliverToBoxAdapter(
|
||||
child: const Divider(height: 1).padding(bottom: 24),
|
||||
),
|
||||
if (data.profile.bio?.isNotEmpty ?? false)
|
||||
if (data.profile.bio.isNotEmpty)
|
||||
SliverToBoxAdapter(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text('bio').tr().bold(),
|
||||
Text(data.profile.bio!),
|
||||
Text(data.profile.bio),
|
||||
],
|
||||
).padding(horizontal: 24),
|
||||
),
|
||||
|
@ -96,7 +96,8 @@ class RelationshipListTile extends StatelessWidget {
|
||||
relationship.status == 0 && relationship.relatedId == currentUserId;
|
||||
final isWaiting =
|
||||
relationship.status == 0 && relationship.accountId == currentUserId;
|
||||
final isEstablished = relationship.status == 1 || relationship.status == 2;
|
||||
final isEstablished =
|
||||
relationship.status >= 100 || relationship.status <= -100;
|
||||
|
||||
return ListTile(
|
||||
contentPadding: const EdgeInsets.only(left: 16, right: 12),
|
||||
@ -105,13 +106,13 @@ class RelationshipListTile extends StatelessWidget {
|
||||
spacing: 6,
|
||||
children: [
|
||||
Flexible(child: Text(account.nick)),
|
||||
if (relationship.status == 1) // Friend
|
||||
if (relationship.status >= 100) // Friend
|
||||
Badge(
|
||||
label: Text('relationshipStatusFriend').tr(),
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
textColor: Theme.of(context).colorScheme.onPrimary,
|
||||
)
|
||||
else if (relationship.status == 2) // Blocked
|
||||
else if (relationship.status <= -100) // Blocked
|
||||
Badge(
|
||||
label: Text('relationshipStatusBlocked').tr(),
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
@ -171,7 +172,7 @@ class RelationshipListTile extends StatelessWidget {
|
||||
icon: const Icon(Symbols.more_vert),
|
||||
itemBuilder:
|
||||
(context) => [
|
||||
if (relationship.status == 1) // If friend
|
||||
if (relationship.status >= 100) // If friend
|
||||
PopupMenuItem(
|
||||
child: ListTile(
|
||||
leading: const Icon(Symbols.block),
|
||||
@ -179,9 +180,12 @@ class RelationshipListTile extends StatelessWidget {
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
onTap:
|
||||
() => onUpdateStatus?.call(relationship, 2),
|
||||
() => onUpdateStatus?.call(
|
||||
relationship,
|
||||
-100,
|
||||
),
|
||||
)
|
||||
else if (relationship.status == 2) // If blocked
|
||||
else if (relationship.status <= -100) // If blocked
|
||||
PopupMenuItem(
|
||||
child: ListTile(
|
||||
leading: const Icon(Symbols.person_add),
|
||||
@ -189,7 +193,8 @@ class RelationshipListTile extends StatelessWidget {
|
||||
contentPadding: EdgeInsets.zero,
|
||||
),
|
||||
onTap:
|
||||
() => onUpdateStatus?.call(relationship, 1),
|
||||
() =>
|
||||
onUpdateStatus?.call(relationship, 100),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -110,6 +110,7 @@ class TabsNavigationWidget extends HookConsumerWidget {
|
||||
Gap(MediaQuery.of(context).padding.top + 8),
|
||||
Expanded(
|
||||
child: NavigationRail(
|
||||
minExtendedWidth: 200,
|
||||
extended: useExpandableLayout,
|
||||
selectedIndex: activeIndex,
|
||||
onDestinationSelected: (index) {
|
||||
|
@ -11,6 +11,7 @@ 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_summary.dart';
|
||||
import 'package:island/pods/config.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/route.gr.dart';
|
||||
@ -24,12 +25,13 @@ import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:island/widgets/realms/selection_dropdown.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';
|
||||
|
||||
part 'chat.g.dart';
|
||||
|
||||
class ChatRoomListTile extends StatelessWidget {
|
||||
class ChatRoomListTile extends HookConsumerWidget {
|
||||
final SnChatRoom room;
|
||||
final bool isDirect;
|
||||
final Widget? subtitle;
|
||||
@ -46,7 +48,88 @@ class ChatRoomListTile extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final summary = ref
|
||||
.watch(chatSummaryProvider)
|
||||
.whenData((summaries) => summaries[room.id]);
|
||||
|
||||
Widget buildSubtitle() {
|
||||
if (subtitle != null) return subtitle!;
|
||||
|
||||
return summary.when(
|
||||
data: (data) {
|
||||
if (data == null) {
|
||||
return isDirect && room.description == null
|
||||
? Text(
|
||||
room.members!.map((e) => '@${e.account.name}').join(', '),
|
||||
maxLines: 1,
|
||||
)
|
||||
: Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1);
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (data.unreadCount > 0)
|
||||
Text(
|
||||
'unreadMessages'.plural(data.unreadCount),
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'${data.lastMessage.sender.account.name}: ',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
data.lastMessage.content ?? 'messageNone'.tr(),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
RelativeTime(context).format(data.lastMessage.createdAt),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
loading: () => const SizedBox.shrink(),
|
||||
error:
|
||||
(_, __) =>
|
||||
isDirect && room.description == null
|
||||
? Text(
|
||||
room.members!.map((e) => '@${e.account.name}').join(', '),
|
||||
maxLines: 1,
|
||||
)
|
||||
: Text(
|
||||
room.description ?? 'descriptionNone'.tr(),
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTrailing() {
|
||||
if (trailing != null) return trailing!;
|
||||
|
||||
return summary.when(
|
||||
data: (data) {
|
||||
if (data == null || data.unreadCount == 0) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return Badge(label: Text(data.unreadCount.toString()));
|
||||
},
|
||||
loading: () => const SizedBox.shrink(),
|
||||
error: (_, __) => const SizedBox.shrink(),
|
||||
);
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
leading:
|
||||
(isDirect && room.pictureId == null)
|
||||
@ -62,19 +145,20 @@ class ChatRoomListTile extends StatelessWidget {
|
||||
title: Text(
|
||||
(isDirect && room.name == null)
|
||||
? room.members!.map((e) => e.account.nick).join(', ')
|
||||
: room.name!,
|
||||
: room.name ?? '',
|
||||
),
|
||||
subtitle:
|
||||
subtitle != null
|
||||
? subtitle!
|
||||
: (isDirect && room.description == null)
|
||||
? Text(
|
||||
room.members!.map((e) => '@${e.account.name}').join(', '),
|
||||
maxLines: 1,
|
||||
)
|
||||
: Text(room.description ?? 'descriptionNone'.tr(), maxLines: 1),
|
||||
trailing: trailing,
|
||||
onTap: onTap,
|
||||
subtitle: buildSubtitle(),
|
||||
trailing: buildTrailing(),
|
||||
onTap: () async {
|
||||
// Clear unread count if there are unread messages
|
||||
final summary = await ref.read(chatSummaryProvider.future);
|
||||
if ((summary[room.id]?.unreadCount ?? 0) > 0) {
|
||||
await ref
|
||||
.read(chatSummaryProvider.notifier)
|
||||
.clearUnreadCount(room.id);
|
||||
}
|
||||
onTap?.call();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -100,9 +184,9 @@ class ChatShellScreen extends HookConsumerWidget {
|
||||
if (isWide) {
|
||||
return Row(
|
||||
children: [
|
||||
SizedBox(width: 320, child: ChatListScreen(isAside: true)),
|
||||
Flexible(flex: 2, child: ChatListScreen(isAside: true)),
|
||||
VerticalDivider(width: 1),
|
||||
Expanded(child: AutoRouter()),
|
||||
Flexible(flex: 4, child: AutoRouter()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -311,27 +311,14 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
final attachmentProgress = useState<Map<String, Map<int, double>>>({});
|
||||
|
||||
// Function to send read receipt
|
||||
void sendReadReceipt(String messageId) async {
|
||||
// Get message from repository to check read status
|
||||
final repository = await ref.read(messageRepositoryProvider(id).future);
|
||||
final message = await repository.getMessageById(messageId);
|
||||
|
||||
// Skip if message is already marked as read
|
||||
if (message?.isRead ?? false) return;
|
||||
|
||||
void sendReadReceipt() async {
|
||||
// Send websocket packet
|
||||
final wsState = ref.read(websocketStateProvider.notifier);
|
||||
wsState.sendMessage(
|
||||
jsonEncode(
|
||||
WebSocketPacket(
|
||||
type: 'messages.read',
|
||||
data: {'chat_room_id': id, 'message_id': messageId},
|
||||
),
|
||||
WebSocketPacket(type: 'messages.read', data: {'chat_room_id': id}),
|
||||
),
|
||||
);
|
||||
|
||||
// Mark as read in local database
|
||||
await repository.markMessageAsRead(messageId);
|
||||
}
|
||||
|
||||
// Add scroll listener for pagination
|
||||
@ -357,7 +344,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
case 'messages.new':
|
||||
messagesNotifier.receiveMessage(message);
|
||||
// Send read receipt for new message
|
||||
sendReadReceipt(message.id);
|
||||
sendReadReceipt();
|
||||
case 'messages.update':
|
||||
messagesNotifier.receiveMessageUpdate(message);
|
||||
case 'messages.delete':
|
||||
@ -365,6 +352,7 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
sendReadReceipt();
|
||||
final subscription = ws.dataStream.listen(onMessage);
|
||||
return () => subscription.cancel();
|
||||
}, [ws, chatRoom]);
|
||||
@ -553,8 +541,6 @@ class ChatRoomScreen extends HookConsumerWidget {
|
||||
nextMessage == null ||
|
||||
nextMessage.senderId != message.senderId;
|
||||
|
||||
sendReadReceipt(message.id);
|
||||
|
||||
return chatIdentity.when(
|
||||
skipError: true,
|
||||
data:
|
||||
|
@ -285,7 +285,7 @@ class EditPublisherScreen extends HookConsumerWidget {
|
||||
final user = ref.watch(userInfoProvider);
|
||||
nameController.text = user.value!.name;
|
||||
nickController.text = user.value!.nick;
|
||||
bioController.text = user.value!.profile.bio ?? '';
|
||||
bioController.text = user.value!.profile.bio;
|
||||
picture.value = user.value!.profile.pictureId;
|
||||
background.value = user.value!.profile.backgroundId;
|
||||
} else {
|
||||
|
@ -120,13 +120,9 @@ class RealmListScreen extends HookConsumerWidget {
|
||||
),
|
||||
loading: () => const Center(child: CircularProgressIndicator()),
|
||||
error:
|
||||
(e, _) => GestureDetector(
|
||||
child: Center(
|
||||
child: Text('Error: $e', textAlign: TextAlign.center),
|
||||
),
|
||||
onTap: () {
|
||||
ref.invalidate(realmsJoinedProvider);
|
||||
},
|
||||
(e, _) => ResponseErrorWidget(
|
||||
error: e,
|
||||
onRetry: () => ref.invalidate(realmsJoinedProvider),
|
||||
),
|
||||
),
|
||||
onRefresh: () => ref.refresh(realmsJoinedProvider.future),
|
||||
|
Reference in New Issue
Block a user