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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user