✨ Chat context menu (w.i.p)
This commit is contained in:
parent
5032cccf38
commit
ce414d92a2
@ -47,6 +47,7 @@
|
|||||||
"compress": "Compress",
|
"compress": "Compress",
|
||||||
"report": "Report",
|
"report": "Report",
|
||||||
"repost": "Repost",
|
"repost": "Repost",
|
||||||
|
"replyPost": "Reply",
|
||||||
"reply": "Reply",
|
"reply": "Reply",
|
||||||
"unset": "Unset",
|
"unset": "Unset",
|
||||||
"untitled": "Untitled",
|
"untitled": "Untitled",
|
||||||
@ -158,5 +159,6 @@
|
|||||||
"realmDeleted": "Realm {} has been deleted.",
|
"realmDeleted": "Realm {} has been deleted.",
|
||||||
"realmDelete": "Delete realm {}",
|
"realmDelete": "Delete realm {}",
|
||||||
"realmDeleteDescription": "Are you sure you want to delete this realm? This operation is irreversible, all resources (posts, chat channels, publishers, etc) belonging to this realm will be permanently deleted. Be careful and think twice!",
|
"realmDeleteDescription": "Are you sure you want to delete this realm? This operation is irreversible, all resources (posts, chat channels, publishers, etc) belonging to this realm will be permanently deleted. Be careful and think twice!",
|
||||||
"fieldChatMessage": "Message in {}"
|
"fieldChatMessage": "Message in {}",
|
||||||
|
"eventResourceTag": "Event {}"
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,8 @@
|
|||||||
"compress": "压缩",
|
"compress": "压缩",
|
||||||
"report": "检举",
|
"report": "检举",
|
||||||
"repost": "转帖",
|
"repost": "转帖",
|
||||||
"reply": "回贴",
|
"replyPost": "回贴",
|
||||||
|
"reply": "回复",
|
||||||
"unset": "未设置",
|
"unset": "未设置",
|
||||||
"untitled": "无题",
|
"untitled": "无题",
|
||||||
"postDetail": "帖子详情",
|
"postDetail": "帖子详情",
|
||||||
@ -158,5 +159,6 @@
|
|||||||
"realmDeleted": "领域 {} 已被删除" ,
|
"realmDeleted": "领域 {} 已被删除" ,
|
||||||
"realmDelete": "删除领域 {}",
|
"realmDelete": "删除领域 {}",
|
||||||
"realmDeleteDescription": "你确定要删除这个领域吗?该操作不可撤销,其隶属于该领域的所有资源(帖子、聊天频道、发布者、制品等)都将被永久删除。三思而后行!",
|
"realmDeleteDescription": "你确定要删除这个领域吗?该操作不可撤销,其隶属于该领域的所有资源(帖子、聊天频道、发布者、制品等)都将被永久删除。三思而后行!",
|
||||||
"fieldChatMessage": "在 {} 中发消息"
|
"fieldChatMessage": "在 {} 中发消息",
|
||||||
|
"eventResourceTag": "消息 {}"
|
||||||
}
|
}
|
||||||
|
@ -118,9 +118,11 @@ class _ChatRoomScreenState extends State<ChatRoomScreen> {
|
|||||||
hasMerged: canMergePrevious,
|
hasMerged: canMergePrevious,
|
||||||
isPending: _messageController.unconfirmedMessages
|
isPending: _messageController.unconfirmedMessages
|
||||||
.contains(message.uuid),
|
.contains(message.uuid),
|
||||||
onReply: () {
|
onReply: (value) {
|
||||||
_inputGlobalKey.currentState?.setReply(message);
|
_inputGlobalKey.currentState?.setReply(value);
|
||||||
},
|
},
|
||||||
|
onEdit: (value) {},
|
||||||
|
onDelete: (value) {},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
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:flutter_context_menu/flutter_context_menu.dart';
|
||||||
import 'package:gap/gap.dart';
|
import 'package:gap/gap.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'package:surface/providers/user_directory.dart';
|
import 'package:surface/providers/user_directory.dart';
|
||||||
|
import 'package:surface/providers/userinfo.dart';
|
||||||
import 'package:surface/types/chat.dart';
|
import 'package:surface/types/chat.dart';
|
||||||
import 'package:surface/widgets/account/account_image.dart';
|
import 'package:surface/widgets/account/account_image.dart';
|
||||||
import 'package:surface/widgets/attachment/attachment_list.dart';
|
import 'package:surface/widgets/attachment/attachment_list.dart';
|
||||||
@ -17,7 +19,9 @@ class ChatMessage extends StatelessWidget {
|
|||||||
final bool isMerged;
|
final bool isMerged;
|
||||||
final bool hasMerged;
|
final bool hasMerged;
|
||||||
final bool isPending;
|
final bool isPending;
|
||||||
final Function()? onReply;
|
final Function(SnChatMessage)? onReply;
|
||||||
|
final Function(SnChatMessage)? onEdit;
|
||||||
|
final Function(SnChatMessage)? onDelete;
|
||||||
const ChatMessage({
|
const ChatMessage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.data,
|
required this.data,
|
||||||
@ -26,100 +30,139 @@ class ChatMessage extends StatelessWidget {
|
|||||||
this.hasMerged = false,
|
this.hasMerged = false,
|
||||||
this.isPending = false,
|
this.isPending = false,
|
||||||
this.onReply,
|
this.onReply,
|
||||||
|
this.onEdit,
|
||||||
|
this.onDelete,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final ua = context.read<UserProvider>();
|
||||||
final ud = context.read<UserDirectoryProvider>();
|
final ud = context.read<UserDirectoryProvider>();
|
||||||
final user = ud.getAccountFromCache(data.sender.accountId);
|
final user = ud.getAccountFromCache(data.sender.accountId);
|
||||||
|
|
||||||
|
final isOwner = ua.isAuthorized && data.sender.accountId == ua.user!.id;
|
||||||
|
|
||||||
final dateFormatter = DateFormat('MM/dd HH:mm');
|
final dateFormatter = DateFormat('MM/dd HH:mm');
|
||||||
|
|
||||||
return SwipeTo(
|
return SwipeTo(
|
||||||
key: Key('chat-message-${data.id}'),
|
key: Key('chat-message-${data.id}'),
|
||||||
iconOnLeftSwipe: Symbols.reply,
|
iconOnLeftSwipe: Symbols.reply,
|
||||||
swipeSensitivity: 20,
|
swipeSensitivity: 20,
|
||||||
onLeftSwipe: onReply != null ? (_) => onReply!() : null,
|
onLeftSwipe: onReply != null ? (_) => onReply!(data) : null,
|
||||||
child: Column(
|
child: ContextMenuRegion(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
contextMenu: ContextMenu(
|
||||||
children: [
|
entries: [
|
||||||
Row(
|
MenuHeader(text: "eventResourceTag".tr(args: ['#${data.id}'])),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
if (onReply != null)
|
||||||
children: [
|
MenuItem(
|
||||||
if (!isMerged && !isCompact)
|
label: 'reply'.tr(),
|
||||||
AccountImage(
|
icon: Symbols.reply,
|
||||||
content: user?.avatar,
|
onSelected: () {
|
||||||
)
|
onReply!(data);
|
||||||
else if (isMerged)
|
},
|
||||||
const Gap(40),
|
),
|
||||||
const Gap(8),
|
if (isOwner && onEdit != null)
|
||||||
Expanded(
|
MenuItem(
|
||||||
child: Column(
|
label: 'edit'.tr(),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
icon: Symbols.edit,
|
||||||
children: [
|
onSelected: () {
|
||||||
if (!isMerged)
|
onEdit!(data);
|
||||||
Row(
|
},
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
),
|
||||||
textBaseline: TextBaseline.alphabetic,
|
if (isOwner && onDelete != null)
|
||||||
children: [
|
MenuItem(
|
||||||
if (isCompact)
|
label: 'delete'.tr(),
|
||||||
AccountImage(
|
icon: Symbols.delete,
|
||||||
content: user?.avatar,
|
onSelected: () {
|
||||||
radius: 12,
|
onDelete!(data);
|
||||||
).padding(right: 6),
|
},
|
||||||
Text(
|
),
|
||||||
(data.sender.nick?.isNotEmpty ?? false)
|
],
|
||||||
? data.sender.nick!
|
),
|
||||||
: user!.nick,
|
child: Column(
|
||||||
).bold(),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
const Gap(6),
|
children: [
|
||||||
Text(
|
Row(
|
||||||
dateFormatter.format(data.createdAt.toLocal()),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
).fontSize(13),
|
children: [
|
||||||
],
|
if (!isMerged && !isCompact)
|
||||||
),
|
AccountImage(
|
||||||
if (isCompact) const Gap(4),
|
content: user?.avatar,
|
||||||
if (data.preload?.quoteEvent != null)
|
)
|
||||||
StyledWidget(Container(
|
else if (isMerged)
|
||||||
decoration: BoxDecoration(
|
const Gap(40),
|
||||||
borderRadius:
|
const Gap(8),
|
||||||
const BorderRadius.all(Radius.circular(8)),
|
Expanded(
|
||||||
border: Border.all(
|
child: Column(
|
||||||
color: Theme.of(context).dividerColor,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
width: 1,
|
children: [
|
||||||
|
if (!isMerged)
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
|
textBaseline: TextBaseline.alphabetic,
|
||||||
|
children: [
|
||||||
|
if (isCompact)
|
||||||
|
AccountImage(
|
||||||
|
content: user?.avatar,
|
||||||
|
radius: 12,
|
||||||
|
).padding(right: 6),
|
||||||
|
Text(
|
||||||
|
(data.sender.nick?.isNotEmpty ?? false)
|
||||||
|
? data.sender.nick!
|
||||||
|
: user!.nick,
|
||||||
|
).bold(),
|
||||||
|
const Gap(6),
|
||||||
|
Text(
|
||||||
|
dateFormatter.format(data.createdAt.toLocal()),
|
||||||
|
).fontSize(13),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
if (isCompact) const Gap(4),
|
||||||
|
if (data.preload?.quoteEvent != null)
|
||||||
|
StyledWidget(Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius:
|
||||||
|
const BorderRadius.all(Radius.circular(8)),
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context).dividerColor,
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 4,
|
||||||
|
right: 4,
|
||||||
|
top: 8,
|
||||||
|
bottom: 6,
|
||||||
|
),
|
||||||
|
child: ChatMessage(
|
||||||
|
data: data.preload!.quoteEvent!,
|
||||||
|
isCompact: true,
|
||||||
|
onReply: onReply,
|
||||||
|
onEdit: onEdit,
|
||||||
|
onDelete: onDelete,
|
||||||
|
),
|
||||||
|
)).padding(bottom: 4, top: isMerged ? 4 : 2),
|
||||||
|
if (data.body['text'] != null)
|
||||||
|
MarkdownTextContent(
|
||||||
|
content: data.body['text'],
|
||||||
|
isAutoWarp: true,
|
||||||
),
|
),
|
||||||
padding: const EdgeInsets.only(
|
],
|
||||||
left: 4,
|
),
|
||||||
right: 4,
|
)
|
||||||
top: 8,
|
],
|
||||||
bottom: 6,
|
).opacity(isPending ? 0.5 : 1),
|
||||||
),
|
if (data.preload?.attachments?.isNotEmpty ?? false)
|
||||||
child: ChatMessage(
|
AttachmentList(
|
||||||
data: data.preload!.quoteEvent!,
|
data: data.preload!.attachments!,
|
||||||
isCompact: true,
|
bordered: true,
|
||||||
),
|
noGrow: true,
|
||||||
)).padding(bottom: 4, top: isMerged ? 4 : 2),
|
maxHeight: 520,
|
||||||
if (data.body['text'] != null)
|
listPadding: const EdgeInsets.only(top: 8),
|
||||||
MarkdownTextContent(
|
),
|
||||||
content: data.body['text'],
|
if (!hasMerged && !isCompact) const Gap(12),
|
||||||
isAutoWarp: true,
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
).opacity(isPending ? 0.5 : 1),
|
|
||||||
if (data.preload?.attachments?.isNotEmpty ?? false)
|
|
||||||
AttachmentList(
|
|
||||||
data: data.preload!.attachments!,
|
|
||||||
bordered: true,
|
|
||||||
noGrow: true,
|
|
||||||
maxHeight: 520,
|
|
||||||
listPadding: const EdgeInsets.only(top: 8),
|
|
||||||
),
|
|
||||||
if (!hasMerged && !isCompact) const Gap(12),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -248,7 +248,7 @@ class _PostContentHeader extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const Icon(Symbols.reply),
|
const Icon(Symbols.reply),
|
||||||
const Gap(16),
|
const Gap(16),
|
||||||
Text('reply').tr(),
|
Text('replyPost').tr(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -76,11 +76,15 @@ class PostMediaPendingListRaw extends StatelessWidget {
|
|||||||
final media = attachments[idx];
|
final media = attachments[idx];
|
||||||
final result = (!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
final result = (!kIsWeb && (Platform.isIOS || Platform.isMacOS))
|
||||||
? await showCupertinoImageCropper(
|
? await showCupertinoImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
context,
|
context,
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
imageProvider: media.getImageProvider(context)!,
|
imageProvider: media.getImageProvider(context)!,
|
||||||
)
|
)
|
||||||
: await showMaterialImageCropper(
|
: await showMaterialImageCropper(
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
context,
|
context,
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
imageProvider: media.getImageProvider(context)!,
|
imageProvider: media.getImageProvider(context)!,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user