feat: menu de contexto em bottom sheet, painel de anexos, perfil e feed do mural
Some checks failed
Main Deploy Workflow / deploy_web (push) Has been cancelled
Main Deploy Workflow / deploy_playstore_internal (push) Has been cancelled
Close stale issues and PRs / stale (push) Has been cancelled

- 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:
2026-09-21 14:30:47 -03:00
parent 762178d9f9
commit 5a72c71cfc
14 changed files with 861 additions and 584 deletions

View File

@@ -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,
),
],
);