✨ Better joining and leaving
This commit is contained in:
parent
9c0221ab20
commit
ad41de3674
@ -248,5 +248,9 @@
|
|||||||
"openLinkConfirm": "Leaving the Solar Network",
|
"openLinkConfirm": "Leaving the Solar Network",
|
||||||
"openLinkConfirmDescription": "You're going to leave the Solar Network and open the link ({}) in your browser. It is not related to Solar Network. Beware of phishing and scams.",
|
"openLinkConfirmDescription": "You're going to leave the Solar Network and open the link ({}) in your browser. It is not related to Solar Network. Beware of phishing and scams.",
|
||||||
"brokenLink": "Unable open link {}... It might be broken or missing uri parts...",
|
"brokenLink": "Unable open link {}... It might be broken or missing uri parts...",
|
||||||
"copyToClipboard": "Copy to clipboard"
|
"copyToClipboard": "Copy to clipboard",
|
||||||
|
"leaveChatRoom": "Leave Chat Room",
|
||||||
|
"leaveChatRoomHint": "Are you sure to leave this chat room?",
|
||||||
|
"leaveRealm": "Leave Realm",
|
||||||
|
"leaveRealmHint": "Are you sure to leave this realm?"
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
|
|||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:island/route.gr.dart';
|
import 'package:island/route.gr.dart';
|
||||||
|
import 'package:island/services/responsive.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
@ -10,6 +11,9 @@ class TabsScreen extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final useHorizontalLayout =
|
||||||
|
MediaQuery.of(context).size.width > kWideScreenWidth;
|
||||||
|
|
||||||
return AutoTabsRouter.pageView(
|
return AutoTabsRouter.pageView(
|
||||||
routes: const [
|
routes: const [
|
||||||
ExploreRoute(),
|
ExploreRoute(),
|
||||||
@ -17,6 +21,8 @@ class TabsScreen extends StatelessWidget {
|
|||||||
RealmListRoute(),
|
RealmListRoute(),
|
||||||
AccountRoute(),
|
AccountRoute(),
|
||||||
],
|
],
|
||||||
|
scrollDirection: useHorizontalLayout ? Axis.vertical : Axis.horizontal,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
builder: (context, child, _) {
|
builder: (context, child, _) {
|
||||||
final tabsRouter = AutoTabsRouter.of(context);
|
final tabsRouter = AutoTabsRouter.of(context);
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -759,9 +759,13 @@ class _ChatInput extends StatelessWidget {
|
|||||||
controller: messageController,
|
controller: messageController,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText:
|
hintText:
|
||||||
chatRoom.type == 1
|
(chatRoom.type == 1 && chatRoom.name == null)
|
||||||
? 'chatDirectMessageHint'.tr(
|
? 'chatDirectMessageHint'.tr(
|
||||||
args: [chatRoom.members!.first.account.nick],
|
args: [
|
||||||
|
chatRoom.members!
|
||||||
|
.map((e) => e.account.nick)
|
||||||
|
.join(', '),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
: 'chatMessageHint'.tr(args: [chatRoom.name!]),
|
: 'chatMessageHint'.tr(args: [chatRoom.name!]),
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
|
@ -27,13 +27,6 @@ class ChatDetailScreen extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final roomState = ref.watch(chatroomProvider(id));
|
final roomState = ref.watch(chatroomProvider(id));
|
||||||
final roomIdentity = ref.watch(chatroomIdentityProvider(id));
|
|
||||||
|
|
||||||
final isModerator = roomIdentity.when(
|
|
||||||
loading: () => false,
|
|
||||||
error: (error, _) => false,
|
|
||||||
data: (identity) => (identity?.role ?? 0) >= 50,
|
|
||||||
);
|
|
||||||
|
|
||||||
const iconShadow = Shadow(
|
const iconShadow = Shadow(
|
||||||
color: Colors.black54,
|
color: Colors.black54,
|
||||||
@ -110,7 +103,6 @@ class ChatDetailScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (isModerator)
|
|
||||||
_ChatRoomActionMenu(id: id, iconShadow: iconShadow),
|
_ChatRoomActionMenu(id: id, iconShadow: iconShadow),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
@ -144,10 +136,13 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final chatIdentity = ref.watch(chatroomIdentityProvider(id));
|
||||||
|
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
icon: Icon(Icons.more_vert, shadows: [iconShadow]),
|
icon: Icon(Icons.more_vert, shadows: [iconShadow]),
|
||||||
itemBuilder:
|
itemBuilder:
|
||||||
(context) => [
|
(context) => [
|
||||||
|
if ((chatIdentity.value?.role ?? 0) >= 50)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.router.replace(EditChatRoute(id: id));
|
context.router.replace(EditChatRoute(id: id));
|
||||||
@ -163,6 +158,7 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if ((chatIdentity.value?.role ?? 0) >= 100)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
@ -191,6 +187,41 @@ class _ChatRoomActionMenu extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
else
|
||||||
|
PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.exit_to_app,
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Text(
|
||||||
|
'leaveChatRoom',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
showConfirmAlert(
|
||||||
|
'leaveChatRoomHint'.tr(),
|
||||||
|
'leaveChatRoom'.tr(),
|
||||||
|
).then((confirm) {
|
||||||
|
if (confirm) {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
client.delete('/chat/$id/members/me');
|
||||||
|
ref.invalidate(chatroomsJoinedProvider);
|
||||||
|
if (context.mounted) {
|
||||||
|
context.router.popUntil(
|
||||||
|
(route) => route is ChatRoomRoute,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -34,13 +34,6 @@ class RealmDetailScreen extends HookConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final realmState = ref.watch(realmProvider(slug));
|
final realmState = ref.watch(realmProvider(slug));
|
||||||
final realmIdentity = ref.watch(realmIdentityProvider(slug));
|
|
||||||
|
|
||||||
final isModerator = realmIdentity.when(
|
|
||||||
loading: () => false,
|
|
||||||
error: (error, _) => false,
|
|
||||||
data: (identity) => (identity?.role ?? 0) >= 50,
|
|
||||||
);
|
|
||||||
|
|
||||||
const iconShadow = Shadow(
|
const iconShadow = Shadow(
|
||||||
color: Colors.black54,
|
color: Colors.black54,
|
||||||
@ -88,7 +81,6 @@ class RealmDetailScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (isModerator)
|
|
||||||
_RealmActionMenu(realmSlug: slug, iconShadow: iconShadow),
|
_RealmActionMenu(realmSlug: slug, iconShadow: iconShadow),
|
||||||
const Gap(8),
|
const Gap(8),
|
||||||
],
|
],
|
||||||
@ -122,10 +114,18 @@ class _RealmActionMenu extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final realmIdentityAsync = ref.watch(realmIdentityProvider(realmSlug));
|
||||||
|
final isModerator = realmIdentityAsync.when(
|
||||||
|
data: (identity) => (identity?.role ?? 0) >= 50,
|
||||||
|
loading: () => false,
|
||||||
|
error: (_, __) => false,
|
||||||
|
);
|
||||||
|
|
||||||
return PopupMenuButton(
|
return PopupMenuButton(
|
||||||
icon: Icon(Icons.more_vert, shadows: [iconShadow]),
|
icon: Icon(Icons.more_vert, shadows: [iconShadow]),
|
||||||
itemBuilder:
|
itemBuilder:
|
||||||
(context) => [
|
(context) => [
|
||||||
|
if (isModerator)
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
context.router.replace(EditRealmRoute(slug: realmSlug));
|
context.router.replace(EditRealmRoute(slug: realmSlug));
|
||||||
@ -141,7 +141,11 @@ class _RealmActionMenu extends HookConsumerWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
realmIdentityAsync.when(
|
||||||
|
data:
|
||||||
|
(identity) =>
|
||||||
|
(identity?.role ?? 0) >= 100
|
||||||
|
? PopupMenuItem(
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.delete, color: Colors.red),
|
const Icon(Icons.delete, color: Colors.red),
|
||||||
@ -161,10 +165,84 @@ class _RealmActionMenu extends HookConsumerWidget {
|
|||||||
final client = ref.watch(apiClientProvider);
|
final client = ref.watch(apiClientProvider);
|
||||||
client.delete('/realms/$realmSlug');
|
client.delete('/realms/$realmSlug');
|
||||||
ref.invalidate(realmsJoinedProvider);
|
ref.invalidate(realmsJoinedProvider);
|
||||||
if (context.mounted) context.router.maybePop(true);
|
if (context.mounted)
|
||||||
|
context.router.maybePop(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
: PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.exit_to_app,
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Text(
|
||||||
|
'leaveRealm',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
showConfirmAlert(
|
||||||
|
'leaveRealmHint'.tr(),
|
||||||
|
'leaveRealm'.tr(),
|
||||||
|
).then((confirm) {
|
||||||
|
if (confirm) {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
client.delete(
|
||||||
|
'/realms/$realmSlug/members/me',
|
||||||
|
);
|
||||||
|
ref.invalidate(realmsJoinedProvider);
|
||||||
|
if (context.mounted) {
|
||||||
|
context.router.maybePop(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
loading:
|
||||||
|
() => const PopupMenuItem(
|
||||||
|
enabled: false,
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
|
error:
|
||||||
|
(_, __) => PopupMenuItem(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.exit_to_app,
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Text(
|
||||||
|
'leaveRealm',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
).tr(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
showConfirmAlert(
|
||||||
|
'leaveRealmHint'.tr(),
|
||||||
|
'leaveRealm'.tr(),
|
||||||
|
).then((confirm) {
|
||||||
|
if (confirm) {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
client.delete('/realms/$realmSlug/members/me');
|
||||||
|
ref.invalidate(realmsJoinedProvider);
|
||||||
|
if (context.mounted) {
|
||||||
|
context.router.maybePop(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -379,7 +379,7 @@ class _RealmInviteSheet extends HookConsumerWidget {
|
|||||||
Future<void> acceptInvite(SnRealmMember invite) async {
|
Future<void> acceptInvite(SnRealmMember invite) async {
|
||||||
try {
|
try {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
await client.post('/realms/invites/${invite.realm!.id}/accept');
|
await client.post('/realms/invites/${invite.realm!.slug}/accept');
|
||||||
ref.invalidate(realmInvitesProvider);
|
ref.invalidate(realmInvitesProvider);
|
||||||
ref.invalidate(realmsJoinedProvider);
|
ref.invalidate(realmsJoinedProvider);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -390,7 +390,7 @@ class _RealmInviteSheet extends HookConsumerWidget {
|
|||||||
Future<void> declineInvite(SnRealmMember invite) async {
|
Future<void> declineInvite(SnRealmMember invite) async {
|
||||||
try {
|
try {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
await client.post('/realms/invites/${invite.realm!.id}/decline');
|
await client.post('/realms/invites/${invite.realm!.slug}/decline');
|
||||||
ref.invalidate(realmInvitesProvider);
|
ref.invalidate(realmInvitesProvider);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorAlert(err);
|
showErrorAlert(err);
|
||||||
@ -452,7 +452,6 @@ class _RealmInviteSheet extends HookConsumerWidget {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
leading: ProfilePictureWidget(
|
leading: ProfilePictureWidget(
|
||||||
fileId: invite.realm!.pictureId,
|
fileId: invite.realm!.pictureId,
|
||||||
radius: 24,
|
|
||||||
fallbackIcon: Symbols.group,
|
fallbackIcon: Symbols.group,
|
||||||
),
|
),
|
||||||
title: Text(invite.realm!.name),
|
title: Text(invite.realm!.name),
|
||||||
|
1
lib/services/responsive.dart
Normal file
1
lib/services/responsive.dart
Normal file
@ -0,0 +1 @@
|
|||||||
|
const kWideScreenWidth = 640;
|
@ -101,6 +101,7 @@ class MessageItem extends HookConsumerWidget {
|
|||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
if (showAvatar) ...[
|
if (showAvatar) ...[
|
||||||
|
const Gap(8),
|
||||||
Row(
|
Row(
|
||||||
spacing: 8,
|
spacing: 8,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user