feat: menu de contexto em bottom sheet, painel de anexos, perfil e feed do mural
- Salas (chat, canais, murais e salas de espacos) abrem bottom sheet com grid de acoes - Botao + da barra de digitacao abre painel de anexos em grid de 3 colunas que empurra a caixa de texto - Modal do perfil permite editar nome/status e ler QR code (mobile) - Alterar status atualiza localmente via extensao do SDK matrix - Mural pre-carrega historico para exibir todas as midias em dispositivos sem cache
This commit is contained in:
@@ -22,6 +22,7 @@ import 'package:tatchat/pages/chat_list/chat_list_view.dart';
|
||||
import 'package:tatchat/pages/chat_list/tabs/home_tab.dart';
|
||||
import 'package:tatchat/utils/error_reporter.dart';
|
||||
import 'package:tatchat/utils/localized_exception_extension.dart';
|
||||
import 'package:tatchat/utils/matrix_sdk_extensions/client_presence_extension.dart';
|
||||
import 'package:tatchat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:tatchat/utils/matrix_sdk_extensions/room_extension.dart';
|
||||
import 'package:tatchat/utils/platform_infos.dart';
|
||||
@@ -30,7 +31,6 @@ import 'package:tatchat/utils/show_update_snackbar.dart';
|
||||
import 'package:tatchat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
|
||||
import 'package:tatchat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:tatchat/widgets/adaptive_dialogs/show_text_input_dialog.dart';
|
||||
import 'package:tatchat/widgets/avatar.dart';
|
||||
import 'package:tatchat/widgets/future_loading_dialog.dart';
|
||||
import 'package:tatchat/widgets/share_scaffold_dialog.dart';
|
||||
|
||||
@@ -514,21 +514,7 @@ class ChatListController extends State<ChatList>
|
||||
BuildContext posContext, [
|
||||
Room? space,
|
||||
]) async {
|
||||
final overlay =
|
||||
Overlay.of(posContext).context.findRenderObject() as RenderBox;
|
||||
|
||||
final button = posContext.findRenderObject() as RenderBox;
|
||||
|
||||
final position = RelativeRect.fromRect(
|
||||
Rect.fromPoints(
|
||||
button.localToGlobal(const Offset(0, -65), ancestor: overlay),
|
||||
button.localToGlobal(
|
||||
button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
|
||||
ancestor: overlay,
|
||||
),
|
||||
),
|
||||
Offset.zero & overlay.size,
|
||||
);
|
||||
final l10n = L10n.of(posContext);
|
||||
|
||||
final spacesWithPowerLevels = room.client.rooms
|
||||
.where(
|
||||
@@ -539,202 +525,104 @@ class ChatListController extends State<ChatList>
|
||||
)
|
||||
.toList();
|
||||
|
||||
var action = await showMenu<ChatContextAction>(
|
||||
var action = await showModalActionPopup<ChatContextAction>(
|
||||
context: posContext,
|
||||
position: position,
|
||||
items: [
|
||||
cancelLabel: l10n.cancel,
|
||||
actions: [
|
||||
if (space != null)
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.goToSpace,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: space.avatar,
|
||||
size: Avatar.defaultSize / 2,
|
||||
name: space.getLocalizedDisplayname(),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).goToSpace(space.getLocalizedDisplayname()),
|
||||
),
|
||||
],
|
||||
),
|
||||
label: l10n.goToSpace(space.getLocalizedDisplayname()),
|
||||
icon: const Icon(Icons.workspaces_outlined),
|
||||
),
|
||||
if (room.membership == Membership.join) ...[
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.mute,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? Icons.notifications_off_outlined
|
||||
: Icons.notifications_off,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? L10n.of(context).muteChat
|
||||
: L10n.of(context).unmuteChat,
|
||||
),
|
||||
],
|
||||
label: room.pushRuleState == PushRuleState.notify
|
||||
? l10n.muteChat
|
||||
: l10n.unmuteChat,
|
||||
icon: Icon(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? Icons.notifications_off_outlined
|
||||
: Icons.notifications_off,
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.markUnread,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
room.markedUnread
|
||||
? Icons.mark_as_unread
|
||||
: Icons.mark_as_unread_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.markedUnread
|
||||
? L10n.of(context).markAsRead
|
||||
: L10n.of(context).markAsUnread,
|
||||
),
|
||||
],
|
||||
label: room.markedUnread ? l10n.markAsRead : l10n.markAsUnread,
|
||||
icon: Icon(
|
||||
room.markedUnread
|
||||
? Icons.mark_as_unread
|
||||
: Icons.mark_as_unread_outlined,
|
||||
),
|
||||
),
|
||||
if (!room.isLowPriority)
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.favorite,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.isFavourite
|
||||
? L10n.of(context).unpin
|
||||
: L10n.of(context).pin,
|
||||
),
|
||||
],
|
||||
label: room.isFavourite ? l10n.unpin : l10n.pin,
|
||||
icon: Icon(
|
||||
room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined,
|
||||
),
|
||||
),
|
||||
],
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.leave,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.membership == Membership.invite
|
||||
? L10n.of(context).delete
|
||||
: L10n.of(context).leave,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
label: room.membership == Membership.invite
|
||||
? l10n.delete
|
||||
: l10n.leave,
|
||||
icon: const Icon(Icons.delete_outlined),
|
||||
isDestructive: true,
|
||||
),
|
||||
if (room.membership == Membership.invite)
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.block,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.block_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).block,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
label: l10n.block,
|
||||
icon: const Icon(Icons.block_outlined),
|
||||
isDestructive: true,
|
||||
),
|
||||
if (room.membership == Membership.join)
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.showMore,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(Icons.adaptive.more_outlined),
|
||||
const SizedBox(width: 12),
|
||||
Text(L10n.of(context).more),
|
||||
],
|
||||
),
|
||||
label: l10n.more,
|
||||
icon: Icon(Icons.adaptive.more_outlined),
|
||||
),
|
||||
],
|
||||
);
|
||||
if (!posContext.mounted || !mounted) return;
|
||||
if (action == ChatContextAction.showMore) {
|
||||
action = await showMenu<ChatContextAction>(
|
||||
action = await showModalActionPopup<ChatContextAction>(
|
||||
context: posContext,
|
||||
position: position,
|
||||
items: [
|
||||
cancelLabel: l10n.cancel,
|
||||
actions: [
|
||||
if (!room.isFavourite)
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.lowPriority,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
room.isLowPriority
|
||||
? Icons.low_priority
|
||||
: Icons.low_priority_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.isLowPriority
|
||||
? L10n.of(context).unsetLowPriority
|
||||
: L10n.of(context).setLowPriority,
|
||||
),
|
||||
],
|
||||
label: room.isLowPriority
|
||||
? l10n.unsetLowPriority
|
||||
: l10n.setLowPriority,
|
||||
icon: Icon(
|
||||
room.isLowPriority
|
||||
? Icons.low_priority
|
||||
: Icons.low_priority_outlined,
|
||||
),
|
||||
),
|
||||
if (activeTag == null)
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.addTag,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(Icons.bookmark_add_outlined),
|
||||
const SizedBox(width: 12),
|
||||
Text(L10n.of(context).addTag),
|
||||
],
|
||||
),
|
||||
label: l10n.addTag,
|
||||
icon: const Icon(Icons.bookmark_add_outlined),
|
||||
)
|
||||
else
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.removeTag,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(Icons.bookmark_remove_outlined),
|
||||
const SizedBox(width: 12),
|
||||
Text(L10n.of(context).removeTag),
|
||||
],
|
||||
),
|
||||
label: l10n.removeTag,
|
||||
icon: const Icon(Icons.bookmark_remove_outlined),
|
||||
),
|
||||
if (spacesWithPowerLevels.isNotEmpty)
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: ChatContextAction.addToSpace,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
const Icon(Icons.group_work_outlined),
|
||||
const SizedBox(width: 12),
|
||||
Text(L10n.of(context).addToSpace),
|
||||
],
|
||||
),
|
||||
label: l10n.addToSpace,
|
||||
icon: const Icon(Icons.group_work_outlined),
|
||||
),
|
||||
],
|
||||
);
|
||||
@@ -909,11 +797,7 @@ class ChatListController extends State<ChatList>
|
||||
if (!mounted) return;
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () => client.setPresence(
|
||||
client.userID!,
|
||||
PresenceType.online,
|
||||
statusMsg: input,
|
||||
),
|
||||
future: () => client.setStatusMessage(input),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,255 +4,46 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
import 'package:matrix/matrix.dart' hide Result;
|
||||
import 'package:tatchat/config/themes.dart';
|
||||
import 'package:tatchat/l10n/l10n.dart';
|
||||
import 'package:tatchat/utils/tatu_share.dart';
|
||||
import 'package:tatchat/widgets/avatar.dart';
|
||||
import 'package:tatchat/widgets/matrix.dart';
|
||||
import 'package:tatchat/widgets/self_profile_dialog.dart';
|
||||
|
||||
import 'chat_list.dart';
|
||||
|
||||
/// The header avatar of the active account. Tapping it opens the own profile
|
||||
/// dialog. Account switching and the former avatar popup actions live in the
|
||||
/// navigation drawer (Sidebar).
|
||||
class ClientChooserButton extends StatelessWidget {
|
||||
final ChatListController controller;
|
||||
|
||||
const ClientChooserButton(this.controller, {super.key});
|
||||
|
||||
List<PopupMenuEntry<Object>> _bundleMenuItems(BuildContext context) {
|
||||
final matrix = Matrix.of(context);
|
||||
final bundles = matrix.accountBundles.keys.toList()
|
||||
..sort(
|
||||
(a, b) => a!.isValidMatrixIdStrict() == b!.isValidMatrixIdStrict()
|
||||
? 0
|
||||
: a.isValidMatrixIdStrict() && !b.isValidMatrixIdStrict()
|
||||
? -1
|
||||
: 1,
|
||||
);
|
||||
if (FluffyThemes.isColumnMode(context)) {
|
||||
// In column mode the settings/actions live in the navigation sidebar,
|
||||
// so the avatar popup only offers the account switcher.
|
||||
return <PopupMenuEntry<Object>>[
|
||||
..._bundleEntries(context, matrix, bundles),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.addAccount,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.person_add_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Text(L10n.of(context).addAccount),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
return <PopupMenuEntry<Object>>[
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.newGroup,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.group_add_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Text(L10n.of(context).createGroup),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.setStatus,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.edit_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Text(L10n.of(context).setStatus),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.invite,
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.adaptive.share_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Text(L10n.of(context).inviteContact),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.archive,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.archive_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Text(L10n.of(context).archive),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.apps,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.apps_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Text(L10n.of(context).apps),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.settings,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.settings_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Text(L10n.of(context).settings),
|
||||
],
|
||||
),
|
||||
),
|
||||
const PopupMenuDivider(),
|
||||
..._bundleEntries(context, matrix, bundles),
|
||||
PopupMenuItem(
|
||||
value: SettingsAction.addAccount,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.person_add_outlined),
|
||||
const SizedBox(width: 18),
|
||||
Text(L10n.of(context).addAccount),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
List<PopupMenuEntry<Object>> _bundleEntries(
|
||||
BuildContext context,
|
||||
MatrixState matrix,
|
||||
List<String?> bundles,
|
||||
) {
|
||||
return <PopupMenuEntry<Object>>[
|
||||
for (final bundle in bundles) ...[
|
||||
if (matrix.accountBundles[bundle]!.length != 1 ||
|
||||
matrix.accountBundles[bundle]!.single!.userID != bundle)
|
||||
PopupMenuItem(
|
||||
value: null,
|
||||
child: Column(
|
||||
crossAxisAlignment: .start,
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Text(
|
||||
bundle!,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme.titleMedium!.color,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const Divider(height: 1),
|
||||
],
|
||||
),
|
||||
),
|
||||
...matrix.accountBundles[bundle]!
|
||||
.whereType<Client>()
|
||||
.where((client) => client.isLogged())
|
||||
.map(
|
||||
(client) => PopupMenuItem(
|
||||
value: client,
|
||||
child: FutureBuilder<Profile?>(
|
||||
future: client.fetchOwnProfile(),
|
||||
builder: (context, snapshot) {
|
||||
final displayname =
|
||||
snapshot.data?.displayName ?? client.userID!.localpart!;
|
||||
return Row(
|
||||
key: ValueKey('switch_account_$displayname'),
|
||||
children: [
|
||||
Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name: displayname,
|
||||
size: 32,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
displayname,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.edit_outlined),
|
||||
onPressed: () => controller.editBundlesForAccount(
|
||||
client.userID,
|
||||
bundle,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final matrix = Matrix.of(context);
|
||||
final client = Result(() => matrix.client).asValue?.value;
|
||||
|
||||
var clientCount = 0;
|
||||
matrix.accountBundles.forEach((key, value) => clientCount += value.length);
|
||||
return FutureBuilder<Profile>(
|
||||
future: client?.isLogged() == true ? client?.fetchOwnProfile() : null,
|
||||
builder: (context, snapshot) => Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: BorderRadius.circular(99),
|
||||
color: Colors.transparent,
|
||||
child: PopupMenuButton<Object>(
|
||||
key: Key('accounts_and_settings_buttons'),
|
||||
tooltip: L10n.of(context).accountsAndSettings,
|
||||
onSelected: (o) => _clientSelected(o, context),
|
||||
itemBuilder: _bundleMenuItems,
|
||||
icon: Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name: snapshot.data?.displayName ?? client?.userID?.localpart,
|
||||
size: 32,
|
||||
child: Tooltip(
|
||||
message: L10n.of(context).accountsAndSettings,
|
||||
child: InkWell(
|
||||
key: const Key('accounts_and_settings_buttons'),
|
||||
onTap: () => showSelfProfileDialog(context),
|
||||
child: Avatar(
|
||||
mxContent: snapshot.data?.avatarUrl,
|
||||
name: snapshot.data?.displayName ?? client?.userID?.localpart,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _clientSelected(Object object, BuildContext context) async {
|
||||
if (object is Client) {
|
||||
controller.setActiveClient(object);
|
||||
} else if (object is String) {
|
||||
controller.setActiveBundle(object);
|
||||
} else if (object is SettingsAction) {
|
||||
switch (object) {
|
||||
case SettingsAction.addAccount:
|
||||
if (!context.mounted) return;
|
||||
context.go('/rooms/settings/addaccount');
|
||||
break;
|
||||
case SettingsAction.newGroup:
|
||||
context.go('/rooms/newgroup');
|
||||
break;
|
||||
case SettingsAction.invite:
|
||||
TatuShare.shareInviteLink(context);
|
||||
break;
|
||||
case SettingsAction.settings:
|
||||
context.go('/rooms/settings');
|
||||
break;
|
||||
case SettingsAction.archive:
|
||||
context.go('/rooms/archive');
|
||||
break;
|
||||
case SettingsAction.apps:
|
||||
context.go('/rooms/apps');
|
||||
break;
|
||||
case SettingsAction.setStatus:
|
||||
controller.setStatus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SettingsAction { addAccount, newGroup, setStatus, invite, settings, archive, apps }
|
||||
|
||||
@@ -17,6 +17,7 @@ import 'package:tatchat/utils/matrix_sdk_extensions/room_extension.dart';
|
||||
import 'package:tatchat/utils/stream_extension.dart';
|
||||
import 'package:tatchat/utils/string_color.dart';
|
||||
import 'package:tatchat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:tatchat/widgets/adaptive_dialogs/show_modal_action_popup.dart';
|
||||
import 'package:tatchat/widgets/avatar.dart';
|
||||
import 'package:tatchat/widgets/future_loading_dialog.dart';
|
||||
import 'package:tatchat/widgets/hover_builder.dart';
|
||||
@@ -219,108 +220,50 @@ class _SpaceViewState extends State<SpaceView> {
|
||||
final space = client.getRoomById(widget.spaceId);
|
||||
final room = client.getRoomById(roomId);
|
||||
if (space == null) return;
|
||||
final overlay =
|
||||
Overlay.of(posContext).context.findRenderObject() as RenderBox;
|
||||
final l10n = L10n.of(posContext);
|
||||
|
||||
final button = posContext.findRenderObject() as RenderBox;
|
||||
|
||||
final position = RelativeRect.fromRect(
|
||||
Rect.fromPoints(
|
||||
button.localToGlobal(const Offset(0, -65), ancestor: overlay),
|
||||
button.localToGlobal(
|
||||
button.size.bottomRight(Offset.zero) + const Offset(-50, 0),
|
||||
ancestor: overlay,
|
||||
),
|
||||
),
|
||||
Offset.zero & overlay.size,
|
||||
);
|
||||
|
||||
final action = await showMenu<SpaceChildAction>(
|
||||
final action = await showModalActionPopup<SpaceChildAction>(
|
||||
context: posContext,
|
||||
position: position,
|
||||
items: [
|
||||
cancelLabel: l10n.cancel,
|
||||
actions: [
|
||||
if (room != null && room.membership == Membership.join) ...[
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: room.pushRuleState == PushRuleState.notify
|
||||
? SpaceChildAction.mute
|
||||
: SpaceChildAction.unmute,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? Icons.notifications_off_outlined
|
||||
: Icons.notifications_on_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? L10n.of(context).muteChat
|
||||
: L10n.of(context).unmuteChat,
|
||||
),
|
||||
],
|
||||
label: room.pushRuleState == PushRuleState.notify
|
||||
? l10n.muteChat
|
||||
: l10n.unmuteChat,
|
||||
icon: Icon(
|
||||
room.pushRuleState == PushRuleState.notify
|
||||
? Icons.notifications_off_outlined
|
||||
: Icons.notifications_on_outlined,
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: room.markedUnread
|
||||
? SpaceChildAction.markAsRead
|
||||
: SpaceChildAction.markAsUnread,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
room.markedUnread
|
||||
? Icons.mark_as_unread
|
||||
: Icons.mark_as_unread_outlined,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
room.isUnread
|
||||
? L10n.of(context).markAsRead
|
||||
: L10n.of(context).markAsUnread,
|
||||
),
|
||||
],
|
||||
label: room.isUnread ? l10n.markAsRead : l10n.markAsUnread,
|
||||
icon: Icon(
|
||||
room.markedUnread
|
||||
? Icons.mark_as_unread
|
||||
: Icons.mark_as_unread_outlined,
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: SpaceChildAction.leave,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.delete_outlined,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).leave,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
label: l10n.leave,
|
||||
icon: const Icon(Icons.delete_outlined),
|
||||
isDestructive: true,
|
||||
),
|
||||
],
|
||||
if (space.canChangeStateEvent(EventTypes.SpaceChild) == true)
|
||||
PopupMenuItem(
|
||||
AdaptiveModalAction(
|
||||
value: SpaceChildAction.removeFromSpace,
|
||||
child: Row(
|
||||
mainAxisSize: .min,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.remove,
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
L10n.of(context).removeFromSpace,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onErrorContainer,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
label: l10n.removeFromSpace,
|
||||
icon: const Icon(Icons.remove),
|
||||
isDestructive: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -95,6 +95,8 @@ class _ChannelsTabState extends State<ChannelsTab> {
|
||||
room,
|
||||
key: Key('channel_list_item_${room.id}'),
|
||||
onTap: () => widget.controller.onChatTap(room),
|
||||
onLongPress: (context) =>
|
||||
widget.controller.chatContextAction(room, context),
|
||||
activeChat: widget.controller.activeChat == room.id,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -101,6 +101,8 @@ class _MuralsTabState extends State<MuralsTab> {
|
||||
room,
|
||||
key: Key('mural_list_item_${room.id}'),
|
||||
onTap: () => widget.controller.onChatTap(room),
|
||||
onLongPress: (context) =>
|
||||
widget.controller.chatContextAction(room, context),
|
||||
activeChat: widget.controller.activeChat == room.id,
|
||||
displayAvatar: Uri.tryParse(
|
||||
creatorState?.content.tryGet<String>('avatar_url') ??
|
||||
|
||||
Reference in New Issue
Block a user