mirror of
https://github.com/krille-chan/fluffychat
synced 2024-09-10 01:05:17 +00:00
design: New chat access settings
This commit is contained in:
parent
cbdea13772
commit
c300220773
11 changed files with 499 additions and 355 deletions
|
@ -760,6 +760,9 @@
|
|||
"type": "text",
|
||||
"placeholders": {}
|
||||
},
|
||||
"globalChatId": "Global chat ID",
|
||||
"accessAndVisibility": "Access and visibility",
|
||||
"accessAndVisibilityDescription": "Who is allowed to join this chat and how the chat can be discovered.",
|
||||
"calls": "Calls",
|
||||
"customEmojisAndStickers": "Custom emojis and stickers",
|
||||
"customEmojisAndStickersBody": "Add or share custom emojis or stickers which can be used in any chat.",
|
||||
|
@ -2276,6 +2279,14 @@
|
|||
"user": {}
|
||||
}
|
||||
},
|
||||
"userWouldLikeToChangeTheChat": "{user} would like to join the chat.",
|
||||
"@userWouldLikeToChangeTheChat": {
|
||||
"placeholders": {
|
||||
"user": {}
|
||||
}
|
||||
},
|
||||
"noPublicLinkHasBeenCreatedYet": "No public link has been created yet",
|
||||
"knock": "Knock",
|
||||
"users": "Users",
|
||||
"@users": {},
|
||||
"unlockOldMessages": "Unlock old messages",
|
||||
|
@ -2450,6 +2461,14 @@
|
|||
"query": {}
|
||||
}
|
||||
},
|
||||
"knocking": "Knocking",
|
||||
"chatCanBeDiscoveredViaSearchOnServer": "Chat can be discovered via the search on {server}",
|
||||
"@chatCanBeDiscoveredViaSearchOnServer": {
|
||||
"type": "text",
|
||||
"placeholders": {
|
||||
"server": {}
|
||||
}
|
||||
},
|
||||
"searchChatsRooms": "Search for #chats, @users...",
|
||||
"@searchChatsRooms": {},
|
||||
"nothingFound": "Nothing found...",
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:go_router/go_router.dart';
|
|||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/pages/archive/archive.dart';
|
||||
import 'package:fluffychat/pages/chat/chat.dart';
|
||||
import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart';
|
||||
import 'package:fluffychat/pages/chat_details/chat_details.dart';
|
||||
import 'package:fluffychat/pages/chat_encryption_settings/chat_encryption_settings.dart';
|
||||
import 'package:fluffychat/pages/chat_list/chat_list.dart';
|
||||
|
@ -344,6 +345,17 @@ abstract class AppRoutes {
|
|||
),
|
||||
),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: 'access',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
context,
|
||||
state,
|
||||
ChatAccessSettings(
|
||||
roomId: state.pathParameters['roomid']!,
|
||||
),
|
||||
),
|
||||
redirect: loggedOutRedirect,
|
||||
),
|
||||
GoRoute(
|
||||
path: 'members',
|
||||
pageBuilder: (context, state) => defaultPageBuilder(
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
import 'package:flutter/material.dart' hide Visibility;
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_page.dart';
|
||||
import 'package:fluffychat/utils/localized_exception_extension.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ChatAccessSettings extends StatefulWidget {
|
||||
final String roomId;
|
||||
const ChatAccessSettings({required this.roomId, super.key});
|
||||
|
||||
@override
|
||||
State<ChatAccessSettings> createState() => ChatAccessSettingsController();
|
||||
}
|
||||
|
||||
class ChatAccessSettingsController extends State<ChatAccessSettings> {
|
||||
bool joinRulesLoading = false;
|
||||
bool visibilityLoading = false;
|
||||
bool historyVisibilityLoading = false;
|
||||
bool guestAccessLoading = false;
|
||||
Room get room => Matrix.of(context).client.getRoomById(widget.roomId)!;
|
||||
|
||||
void setJoinRule(JoinRules? newJoinRules) async {
|
||||
if (newJoinRules == null) return;
|
||||
setState(() {
|
||||
joinRulesLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await room.setJoinRules(newJoinRules);
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to change join rules', e, s);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
joinRulesLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setHistoryVisibility(HistoryVisibility? historyVisibility) async {
|
||||
if (historyVisibility == null) return;
|
||||
setState(() {
|
||||
historyVisibilityLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await room.setHistoryVisibility(historyVisibility);
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to change history visibility', e, s);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
historyVisibilityLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setGuestAccess(GuestAccess? guestAccess) async {
|
||||
if (guestAccess == null) return;
|
||||
setState(() {
|
||||
guestAccessLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await room.setGuestAccess(guestAccess);
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to change guest access', e, s);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
guestAccessLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateRoomAction() async {
|
||||
final roomVersion = room
|
||||
.getState(EventTypes.RoomCreate)!
|
||||
.content
|
||||
.tryGet<String>('room_version');
|
||||
final capabilitiesResult = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.client.getCapabilities(),
|
||||
);
|
||||
final capabilities = capabilitiesResult.result;
|
||||
if (capabilities == null) return;
|
||||
final newVersion = await showConfirmationDialog<String>(
|
||||
context: context,
|
||||
title: L10n.of(context)!.replaceRoomWithNewerVersion,
|
||||
actions: capabilities.mRoomVersions!.available.entries
|
||||
.where((r) => r.key != roomVersion)
|
||||
.map(
|
||||
(version) => AlertDialogAction(
|
||||
key: version.key,
|
||||
label:
|
||||
'${version.key} (${version.value.toString().split('.').last})',
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
if (newVersion == null ||
|
||||
OkCancelResult.cancel ==
|
||||
await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
okLabel: L10n.of(context)!.yes,
|
||||
cancelLabel: L10n.of(context)!.cancel,
|
||||
title: L10n.of(context)!.areYouSure,
|
||||
message: L10n.of(context)!.roomUpgradeDescription,
|
||||
isDestructiveAction: true,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.client.upgradeRoom(room.id, newVersion),
|
||||
);
|
||||
}
|
||||
|
||||
void setCanonicalAlias() async {
|
||||
final input = await showTextInputDialog(
|
||||
context: context,
|
||||
title: L10n.of(context)!.editRoomAliases,
|
||||
cancelLabel: L10n.of(context)!.cancel,
|
||||
okLabel: L10n.of(context)!.ok,
|
||||
textFields: [
|
||||
DialogTextField(
|
||||
prefixText: '#',
|
||||
suffixText: room.client.userID!.domain!,
|
||||
initialText: room.canonicalAlias.localpart,
|
||||
),
|
||||
],
|
||||
);
|
||||
final newAliasLocalpart = input?.singleOrNull?.trim();
|
||||
if (newAliasLocalpart == null || newAliasLocalpart.isEmpty) return;
|
||||
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setCanonicalAlias(
|
||||
'#$newAliasLocalpart:${room.client.userID!.domain!}',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void setChatVisibilityOnDirectory(bool? visibility) async {
|
||||
if (visibility == null) return;
|
||||
setState(() {
|
||||
visibilityLoading = true;
|
||||
});
|
||||
|
||||
try {
|
||||
await room.client.setRoomVisibilityOnDirectory(
|
||||
room.id,
|
||||
visibility: visibility == true ? Visibility.public : Visibility.private,
|
||||
);
|
||||
setState(() {});
|
||||
} catch (e, s) {
|
||||
Logs().w('Unable to change visibility', e, s);
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
e.toLocalizedString(context),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
visibilityLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ChatAccessSettingsPageView(this);
|
||||
}
|
||||
}
|
172
lib/pages/chat_access_settings/chat_access_settings_page.dart
Normal file
172
lib/pages/chat_access_settings/chat_access_settings_page.dart
Normal file
|
@ -0,0 +1,172 @@
|
|||
import 'package:flutter/material.dart' hide Visibility;
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pages/chat_access_settings/chat_access_settings_controller.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:fluffychat/widgets/layouts/max_width_body.dart';
|
||||
|
||||
class ChatAccessSettingsPageView extends StatelessWidget {
|
||||
final ChatAccessSettingsController controller;
|
||||
const ChatAccessSettingsPageView(this.controller, {super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final room = controller.room;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: const Center(child: BackButton()),
|
||||
title: Text(L10n.of(context)!.accessAndVisibility),
|
||||
),
|
||||
body: MaxWidthBody(
|
||||
child: StreamBuilder<Object>(
|
||||
stream: room.onUpdate.stream,
|
||||
builder: (context, snapshot) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.visibilityOfTheChatHistory,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final historyVisibility in HistoryVisibility.values)
|
||||
RadioListTile<HistoryVisibility>.adaptive(
|
||||
title: Text(
|
||||
historyVisibility
|
||||
.getLocalizedString(MatrixLocals(L10n.of(context)!)),
|
||||
),
|
||||
value: historyVisibility,
|
||||
groupValue: room.historyVisibility,
|
||||
onChanged: controller.historyVisibilityLoading ||
|
||||
!room.canChangeHistoryVisibility
|
||||
? null
|
||||
: controller.setHistoryVisibility,
|
||||
),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.whoIsAllowedToJoinThisGroup,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final joinRule in JoinRules.values)
|
||||
RadioListTile<JoinRules>.adaptive(
|
||||
title: Text(
|
||||
joinRule
|
||||
.getLocalizedString(MatrixLocals(L10n.of(context)!)),
|
||||
),
|
||||
value: joinRule,
|
||||
groupValue: room.joinRules,
|
||||
onChanged:
|
||||
controller.joinRulesLoading || !room.canChangeJoinRules
|
||||
? null
|
||||
: controller.setJoinRule,
|
||||
),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
if ({JoinRules.public, JoinRules.knock}
|
||||
.contains(room.joinRules)) ...[
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.areGuestsAllowedToJoin,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
for (final guestAccess in GuestAccess.values)
|
||||
RadioListTile<GuestAccess>.adaptive(
|
||||
title: Text(
|
||||
guestAccess
|
||||
.getLocalizedString(MatrixLocals(L10n.of(context)!)),
|
||||
),
|
||||
value: guestAccess,
|
||||
groupValue: room.guestAccess,
|
||||
onChanged: controller.guestAccessLoading ||
|
||||
!room.canChangeGuestAccess
|
||||
? null
|
||||
: controller.setGuestAccess,
|
||||
),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
FutureBuilder(
|
||||
future: room.client.getRoomVisibilityOnDirectory(room.id),
|
||||
builder: (context, snapshot) => SwitchListTile.adaptive(
|
||||
value: snapshot.data == Visibility.public,
|
||||
title: Text(
|
||||
L10n.of(context)!.chatCanBeDiscoveredViaSearchOnServer(
|
||||
room.client.userID!.domain!,
|
||||
),
|
||||
),
|
||||
onChanged: controller.setChatVisibilityOnDirectory,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(L10n.of(context)!.publicLink),
|
||||
subtitle: room.canonicalAlias.isEmpty
|
||||
? Text(
|
||||
L10n.of(context)!.noPublicLinkHasBeenCreatedYet,
|
||||
style: const TextStyle(
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
'https://matrix.to/#/${room.canonicalAlias}',
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.underline,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
onTap: room.canChangeStateEvent(EventTypes.RoomCanonicalAlias)
|
||||
? controller.setCanonicalAlias
|
||||
: null,
|
||||
trailing: room.canonicalAlias.isEmpty
|
||||
? const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.0),
|
||||
child: Icon(Icons.add),
|
||||
)
|
||||
: IconButton(
|
||||
icon: Icon(Icons.adaptive.share_outlined),
|
||||
onPressed: () => FluffyShare.share(room.id, context),
|
||||
),
|
||||
),
|
||||
],
|
||||
ListTile(
|
||||
title: Text(L10n.of(context)!.globalChatId),
|
||||
subtitle: SelectableText(room.id),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.copy_outlined),
|
||||
onPressed: () => FluffyShare.share(room.id, context),
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(L10n.of(context)!.roomVersion),
|
||||
subtitle: SelectableText(
|
||||
room
|
||||
.getState(EventTypes.RoomCreate)!
|
||||
.content
|
||||
.tryGet<String>('room_version') ??
|
||||
'Unknown',
|
||||
),
|
||||
trailing: room.canSendEvent(EventTypes.RoomTombstone)
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.upgrade_outlined),
|
||||
onPressed: controller.updateRoomAction,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
@ -8,7 +7,6 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:matrix/matrix.dart' as matrix;
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pages/chat_details/chat_details_view.dart';
|
||||
|
@ -71,116 +69,6 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||
}
|
||||
}
|
||||
|
||||
void editAliases() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!);
|
||||
|
||||
final aliasesResult = await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room!.client.getLocalAliases(room.id),
|
||||
);
|
||||
|
||||
final aliases = aliasesResult.result;
|
||||
|
||||
if (aliases == null) return;
|
||||
final adminMode = room!.canSendEvent(EventTypes.RoomCanonicalAlias);
|
||||
if (aliases.isEmpty && (room.canonicalAlias.isNotEmpty)) {
|
||||
aliases.add(room.canonicalAlias);
|
||||
}
|
||||
if (aliases.isEmpty && adminMode) {
|
||||
return setAliasAction();
|
||||
}
|
||||
final select = await showConfirmationDialog(
|
||||
context: context,
|
||||
title: L10n.of(context)!.editRoomAliases,
|
||||
actions: [
|
||||
if (adminMode)
|
||||
AlertDialogAction(label: L10n.of(context)!.create, key: 'new'),
|
||||
...aliases.map((alias) => AlertDialogAction(key: alias, label: alias)),
|
||||
],
|
||||
);
|
||||
if (select == null) return;
|
||||
if (select == 'new') {
|
||||
return setAliasAction();
|
||||
}
|
||||
final option = await showConfirmationDialog<AliasActions>(
|
||||
context: context,
|
||||
title: select,
|
||||
actions: [
|
||||
AlertDialogAction(
|
||||
label: L10n.of(context)!.copyToClipboard,
|
||||
key: AliasActions.copy,
|
||||
isDefaultAction: true,
|
||||
),
|
||||
if (adminMode) ...{
|
||||
AlertDialogAction(
|
||||
label: L10n.of(context)!.setAsCanonicalAlias,
|
||||
key: AliasActions.setCanonical,
|
||||
isDestructiveAction: true,
|
||||
),
|
||||
AlertDialogAction(
|
||||
label: L10n.of(context)!.delete,
|
||||
key: AliasActions.delete,
|
||||
isDestructiveAction: true,
|
||||
),
|
||||
},
|
||||
],
|
||||
);
|
||||
if (option == null) return;
|
||||
switch (option) {
|
||||
case AliasActions.copy:
|
||||
await Clipboard.setData(ClipboardData(text: select));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(L10n.of(context)!.copiedToClipboard)),
|
||||
);
|
||||
break;
|
||||
case AliasActions.delete:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.client.deleteRoomAlias(select),
|
||||
);
|
||||
break;
|
||||
case AliasActions.setCanonical:
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.client.setRoomStateWithKey(
|
||||
room.id,
|
||||
EventTypes.RoomCanonicalAlias,
|
||||
'',
|
||||
{
|
||||
'alias': select,
|
||||
},
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setAliasAction() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
final domain = room.client.userID!.domain;
|
||||
|
||||
final input = await showTextInputDialog(
|
||||
context: context,
|
||||
title: L10n.of(context)!.setInvitationLink,
|
||||
okLabel: L10n.of(context)!.ok,
|
||||
cancelLabel: L10n.of(context)!.cancel,
|
||||
textFields: [
|
||||
DialogTextField(
|
||||
prefixText: '#',
|
||||
suffixText: domain,
|
||||
hintText: L10n.of(context)!.alias,
|
||||
initialText: room.canonicalAlias.localpart,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (input == null) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () =>
|
||||
room.client.setRoomAlias('#${input.single}:${domain!}', room.id),
|
||||
);
|
||||
}
|
||||
|
||||
void setTopicAction() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
final input = await showTextInputDialog(
|
||||
|
@ -211,91 +99,6 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||
}
|
||||
}
|
||||
|
||||
void setGuestAccess() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
final currentGuestAccess = room.guestAccess;
|
||||
final newGuestAccess = await showConfirmationDialog<GuestAccess>(
|
||||
context: context,
|
||||
title: L10n.of(context)!.areGuestsAllowedToJoin,
|
||||
actions: GuestAccess.values
|
||||
.map(
|
||||
(guestAccess) => AlertDialogAction(
|
||||
key: guestAccess,
|
||||
label: guestAccess
|
||||
.getLocalizedString(MatrixLocals(L10n.of(context)!)),
|
||||
isDefaultAction: guestAccess == currentGuestAccess,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
if (newGuestAccess == null || newGuestAccess == currentGuestAccess) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setGuestAccess(newGuestAccess),
|
||||
);
|
||||
}
|
||||
|
||||
void setHistoryVisibility() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
final currentHistoryVisibility = room.historyVisibility;
|
||||
final newHistoryVisibility =
|
||||
await showConfirmationDialog<HistoryVisibility>(
|
||||
context: context,
|
||||
title: L10n.of(context)!.visibilityOfTheChatHistory,
|
||||
actions: HistoryVisibility.values
|
||||
.map(
|
||||
(visibility) => AlertDialogAction(
|
||||
key: visibility,
|
||||
label: visibility
|
||||
.getLocalizedString(MatrixLocals(L10n.of(context)!)),
|
||||
isDefaultAction: visibility == currentHistoryVisibility,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
if (newHistoryVisibility == null ||
|
||||
newHistoryVisibility == currentHistoryVisibility) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.setHistoryVisibility(newHistoryVisibility),
|
||||
);
|
||||
}
|
||||
|
||||
void setJoinRules() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
final currentJoinRule = room.joinRules;
|
||||
final newJoinRule = await showConfirmationDialog<JoinRules>(
|
||||
context: context,
|
||||
title: L10n.of(context)!.whoIsAllowedToJoinThisGroup,
|
||||
actions: JoinRules.values
|
||||
.map(
|
||||
(joinRule) => AlertDialogAction(
|
||||
key: joinRule,
|
||||
label:
|
||||
joinRule.getLocalizedString(MatrixLocals(L10n.of(context)!)),
|
||||
isDefaultAction: joinRule == currentJoinRule,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
if (newJoinRule == null || newJoinRule == currentJoinRule) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async {
|
||||
await room.setJoinRules(newJoinRule);
|
||||
room.client.setRoomVisibilityOnDirectory(
|
||||
roomId!,
|
||||
visibility: {
|
||||
JoinRules.public,
|
||||
JoinRules.knock,
|
||||
}.contains(newJoinRule)
|
||||
? matrix.Visibility.public
|
||||
: matrix.Visibility.private,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void goToEmoteSettings() async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
// okay, we need to test if there are any emote state events other than the default one
|
||||
|
|
|
@ -267,23 +267,6 @@ class ChatDetailsView extends StatelessWidget {
|
|||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
if (room.joinRules == JoinRules.public)
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: iconColor,
|
||||
child: const Icon(Icons.link_outlined),
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
onTap: controller.editAliases,
|
||||
title: Text(L10n.of(context)!.editRoomAliases),
|
||||
subtitle: Text(
|
||||
(room.canonicalAlias.isNotEmpty)
|
||||
? room.canonicalAlias
|
||||
: L10n.of(context)!.none,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
|
@ -308,69 +291,14 @@ class ChatDetailsView extends StatelessWidget {
|
|||
child: const Icon(Icons.shield_outlined),
|
||||
),
|
||||
title: Text(
|
||||
L10n.of(context)!.whoIsAllowedToJoinThisGroup,
|
||||
),
|
||||
trailing: room.canChangeJoinRules
|
||||
? const Icon(Icons.chevron_right_outlined)
|
||||
: null,
|
||||
subtitle: Text(
|
||||
room.joinRules?.getLocalizedString(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
) ??
|
||||
L10n.of(context)!.none,
|
||||
),
|
||||
onTap: room.canChangeJoinRules
|
||||
? controller.setJoinRules
|
||||
: null,
|
||||
),
|
||||
if (!room.isDirectChat)
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: iconColor,
|
||||
child: const Icon(Icons.visibility_outlined),
|
||||
),
|
||||
trailing: room.canChangeHistoryVisibility
|
||||
? const Icon(Icons.chevron_right_outlined)
|
||||
: null,
|
||||
title: Text(
|
||||
L10n.of(context)!.visibilityOfTheChatHistory,
|
||||
L10n.of(context)!.accessAndVisibility,
|
||||
),
|
||||
subtitle: Text(
|
||||
room.historyVisibility?.getLocalizedString(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
) ??
|
||||
L10n.of(context)!.none,
|
||||
L10n.of(context)!.accessAndVisibilityDescription,
|
||||
),
|
||||
onTap: room.canChangeHistoryVisibility
|
||||
? controller.setHistoryVisibility
|
||||
: null,
|
||||
),
|
||||
if (room.joinRules == JoinRules.public)
|
||||
ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: iconColor,
|
||||
child: const Icon(
|
||||
Icons.person_add_alt_1_outlined,
|
||||
),
|
||||
),
|
||||
trailing: room.canChangeGuestAccess
|
||||
? const Icon(Icons.chevron_right_outlined)
|
||||
: null,
|
||||
title: Text(
|
||||
L10n.of(context)!.areGuestsAllowedToJoin,
|
||||
),
|
||||
subtitle: Text(
|
||||
room.guestAccess.getLocalizedString(
|
||||
MatrixLocals(L10n.of(context)!),
|
||||
),
|
||||
),
|
||||
onTap: room.canChangeGuestAccess
|
||||
? controller.setGuestAccess
|
||||
: null,
|
||||
onTap: () => context
|
||||
.push('/rooms/${room.id}/details/access'),
|
||||
trailing: const Icon(Icons.chevron_right_outlined),
|
||||
),
|
||||
if (!room.isDirectChat)
|
||||
ListTile(
|
||||
|
|
|
@ -14,12 +14,14 @@ class ParticipantListItem extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final membershipBatch = <Membership, String>{
|
||||
Membership.join: '',
|
||||
Membership.ban: L10n.of(context)!.banned,
|
||||
Membership.invite: L10n.of(context)!.invited,
|
||||
Membership.leave: L10n.of(context)!.leftTheChat,
|
||||
final membershipBatch = switch (user.membership) {
|
||||
Membership.ban => L10n.of(context)!.banned,
|
||||
Membership.invite => L10n.of(context)!.invited,
|
||||
Membership.join => null,
|
||||
Membership.knock => L10n.of(context)!.knocking,
|
||||
Membership.leave => L10n.of(context)!.leftTheChat,
|
||||
};
|
||||
|
||||
final permissionBatch = user.powerLevel == 100
|
||||
? L10n.of(context)!.admin
|
||||
: user.powerLevel >= 50
|
||||
|
@ -66,7 +68,7 @@ class ParticipantListItem extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
membershipBatch[user.membership]!.isEmpty
|
||||
membershipBatch == null
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
|
@ -75,8 +77,7 @@ class ParticipantListItem extends StatelessWidget {
|
|||
color: Theme.of(context).secondaryHeaderColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child:
|
||||
Center(child: Text(membershipBatch[user.membership]!)),
|
||||
child: Center(child: Text(membershipBatch)),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:developer';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:adaptive_dialog/adaptive_dialog.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
@ -71,44 +70,6 @@ class ChatPermissionsSettingsController extends State<ChatPermissionsSettings> {
|
|||
false),
|
||||
);
|
||||
|
||||
void updateRoomAction(Capabilities capabilities) async {
|
||||
final room = Matrix.of(context).client.getRoomById(roomId!)!;
|
||||
final roomVersion = room
|
||||
.getState(EventTypes.RoomCreate)!
|
||||
.content['room_version'] as String? ??
|
||||
'1';
|
||||
final newVersion = await showConfirmationDialog<String>(
|
||||
context: context,
|
||||
title: L10n.of(context)!.replaceRoomWithNewerVersion,
|
||||
actions: capabilities.mRoomVersions!.available.entries
|
||||
.where((r) => r.key != roomVersion)
|
||||
.map(
|
||||
(version) => AlertDialogAction(
|
||||
key: version.key,
|
||||
label:
|
||||
'${version.key} (${version.value.toString().split('.').last})',
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
if (newVersion == null ||
|
||||
OkCancelResult.cancel ==
|
||||
await showOkCancelAlertDialog(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
okLabel: L10n.of(context)!.yes,
|
||||
cancelLabel: L10n.of(context)!.cancel,
|
||||
title: L10n.of(context)!.areYouSure,
|
||||
message: L10n.of(context)!.roomUpgradeDescription,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => room.client.upgradeRoom(roomId!, newVersion),
|
||||
).then((_) => context.pop());
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ChatPermissionsSettingsView(this);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
entry.value,
|
||||
),
|
||||
),
|
||||
const Divider(thickness: 1),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.notifications,
|
||||
|
@ -87,7 +87,7 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
);
|
||||
},
|
||||
),
|
||||
const Divider(thickness: 1),
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.configureChat,
|
||||
|
@ -109,33 +109,6 @@ class ChatPermissionsSettingsView extends StatelessWidget {
|
|||
category: 'events',
|
||||
),
|
||||
),
|
||||
if (room.canSendEvent(EventTypes.RoomTombstone)) ...{
|
||||
const Divider(thickness: 1),
|
||||
FutureBuilder<Capabilities>(
|
||||
future: room.client.getCapabilities(),
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
final roomVersion = room
|
||||
.getState(EventTypes.RoomCreate)!
|
||||
.content['room_version'] as String? ??
|
||||
'1';
|
||||
|
||||
return ListTile(
|
||||
title: Text(
|
||||
'${L10n.of(context)!.roomVersion}: $roomVersion',
|
||||
),
|
||||
onTap: () =>
|
||||
controller.updateRoomAction(snapshot.data!),
|
||||
);
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
|
|
|
@ -2,8 +2,10 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:future_loading_dialog/future_loading_dialog.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/utils/date_time_extension.dart';
|
||||
import 'package:fluffychat/utils/fluffy_share.dart';
|
||||
import 'package:fluffychat/utils/url_launcher.dart';
|
||||
|
@ -105,6 +107,57 @@ class UserBottomSheetView extends StatelessWidget {
|
|||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
if (user?.membership == Membership.knock)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.tertiaryContainer,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
|
||||
child: ListTile(
|
||||
minVerticalPadding: 16,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Text(
|
||||
L10n.of(context)!
|
||||
.userWouldLikeToChangeTheChat(displayname),
|
||||
),
|
||||
),
|
||||
subtitle: Row(
|
||||
children: [
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.background,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
onPressed: () => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => user!.room.invite(user.id),
|
||||
),
|
||||
icon: const Icon(Icons.check_outlined),
|
||||
label: Text(L10n.of(context)!.accept),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.errorContainer,
|
||||
foregroundColor:
|
||||
Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
onPressed: () => showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => user!.room.kick(user.id),
|
||||
),
|
||||
icon: const Icon(Icons.cancel_outlined),
|
||||
label: Text(L10n.of(context)!.decline),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
|
|
|
@ -31,22 +31,26 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
|||
void _joinRoom(BuildContext context) async {
|
||||
final client = Matrix.of(outerContext).client;
|
||||
final chunk = this.chunk;
|
||||
final knock = chunk?.joinRule == 'knock';
|
||||
final result = await showFutureLoadingDialog<String>(
|
||||
context: context,
|
||||
future: () async {
|
||||
if (chunk != null && client.getRoomById(chunk.roomId) != null) {
|
||||
return chunk.roomId;
|
||||
}
|
||||
final roomId = chunk != null && chunk.joinRule == 'knock'
|
||||
final roomId = chunk != null && knock
|
||||
? await client.knockRoom(chunk.roomId)
|
||||
: await client.joinRoom(roomAlias ?? chunk!.roomId);
|
||||
|
||||
if (client.getRoomById(roomId) == null) {
|
||||
if (!knock && client.getRoomById(roomId) == null) {
|
||||
await client.waitForRoomInSync(roomId);
|
||||
}
|
||||
return roomId;
|
||||
},
|
||||
);
|
||||
if (knock) {
|
||||
return;
|
||||
}
|
||||
if (result.error == null) {
|
||||
Navigator.of(context).pop();
|
||||
// don't open the room if the joined room is a space
|
||||
|
@ -138,9 +142,11 @@ class PublicRoomBottomSheet extends StatelessWidget {
|
|||
child: ElevatedButton.icon(
|
||||
onPressed: () => _joinRoom(context),
|
||||
label: Text(
|
||||
chunk?.roomType == 'm.space'
|
||||
? L10n.of(context)!.joinSpace
|
||||
: L10n.of(context)!.joinRoom,
|
||||
chunk?.joinRule == 'knock'
|
||||
? L10n.of(context)!.knock
|
||||
: chunk?.roomType == 'm.space'
|
||||
? L10n.of(context)!.joinSpace
|
||||
: L10n.of(context)!.joinRoom,
|
||||
),
|
||||
icon: const Icon(Icons.login_outlined),
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue