Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Chat-ng]: Appflowy Editor editor integration #2332

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changes/2332-chat-ng-editor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Chat-NG now supports Appflowy editor with proper markdown support. Some/Other features might be disabled or limited for now.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Chat-NG now supports Appflowy editor with proper markdown support. Some/Other features might be disabled or limited for now.
- [Labs] Chat-NG now offers a fresh new WYSIWYG editor. More features upcoming.

2 changes: 1 addition & 1 deletion app/integration_test/tests/pins.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:acter/common/utils/constants.dart';
import 'package:acter/common/widgets/html_editor.dart';
import 'package:acter/common/widgets/html_editor/html_editor.dart';
import 'package:acter/common/widgets/spaces/select_space_form_field.dart';
import 'package:acter/features/home/data/keys.dart';
import 'package:acter/features/pins/pages/create_pin_page.dart';
Expand Down
2 changes: 1 addition & 1 deletion app/lib/common/widgets/edit_html_description_sheet.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:acter/common/themes/colors/color_scheme.dart';
import 'package:acter/common/toolkit/buttons/primary_action_button.dart';
import 'package:acter/common/widgets/html_editor.dart';
import 'package:acter/common/widgets/html_editor/html_editor.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
Expand Down
92 changes: 92 additions & 0 deletions app/lib/common/widgets/html_editor/components/mention_block.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'package:acter/common/providers/room_providers.dart';
import 'package:acter/common/widgets/html_editor/models/mention_block_keys.dart';
import 'package:acter_avatar/acter_avatar.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class MentionBlock extends ConsumerWidget {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The UI component of the inline mentions in editor (of how it should be displayed when triggered)

const MentionBlock({
super.key,
required this.node,
required this.index,
required this.mention,
required this.userRoomId,
});

final Map<String, dynamic> mention;
final String userRoomId;
final Node node;
final int index;
Comment on lines +17 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't we usually keep the list above the constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yes. I can change it as I didn't really seem to thought much about it before.

@override
Widget build(BuildContext context, WidgetRef ref) {
Comment on lines +20 to +22
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
final int index;
@override
Widget build(BuildContext context, WidgetRef ref) {
final int index;
@override
Widget build(BuildContext context, WidgetRef ref) {

final String type = mention[MentionBlockKeys.type];
final String displayName = mention[MentionBlockKeys.displayName];
final String mentionId = type == 'user'
? mention[MentionBlockKeys.userId]
: mention[MentionBlockKeys.roomId];
final avatarInfo = type == 'user'
? ref.watch(
memberAvatarInfoProvider((roomId: userRoomId, userId: mentionId)),
)
: ref.watch(roomAvatarInfoProvider(mentionId));
final options = type == 'user'
? AvatarOptions.DM(avatarInfo, size: 8)
: AvatarOptions(avatarInfo, size: 16);

return _mentionContent(
context: context,
mentionId: mentionId,
displayName: displayName,
avatarOptions: options,
ref: ref,
);
}

Widget _mentionContent({
required BuildContext context,
required String mentionId,
required String displayName,
required WidgetRef ref,
required AvatarOptions avatarOptions,
}) {
final desktopPlatforms = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mouse can also be plugged/connected to phones. don't do a check like that.

TargetPlatform.linux,
TargetPlatform.macOS,
TargetPlatform.windows,
];
final name = displayName.isNotEmpty ? displayName : mentionId;
final mentionContentWidget = Container(
padding: const EdgeInsets.symmetric(horizontal: 12),
decoration: BoxDecoration(
color: Theme.of(context).unselectedWidgetColor,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
ActerAvatar(options: avatarOptions),
const SizedBox(width: 4),
Text(name, style: Theme.of(context).textTheme.bodyMedium),
],
),
);

final Widget content = GestureDetector(
onTap: _handleUserTap,
behavior: HitTestBehavior.opaque,
child: desktopPlatforms.contains(Theme.of(context).platform)
? MouseRegion(
cursor: SystemMouseCursors.click,
child: mentionContentWidget,
)
: mentionContentWidget,
);

return content;
}

void _handleUserTap() {
// Implement user tap action (e.g., show profile, start chat)
}
}
50 changes: 50 additions & 0 deletions app/lib/common/widgets/html_editor/components/mention_item.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:acter/common/utils/constants.dart';
import 'package:acter/common/widgets/html_editor/models/mention_type.dart';
import 'package:acter_avatar/acter_avatar.dart';
import 'package:flutter/material.dart';

class MentionItem extends StatelessWidget {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mention list UI item component which gets displayed as part of mention menu list.

const MentionItem({
super.key,
required this.mentionId,
required this.mentionType,
required this.displayName,
required this.avatarOptions,
required this.isSelected,
required this.onTap,
});

final String mentionId;
final MentionType mentionType;
final String displayName;
final AvatarOptions avatarOptions;
final bool isSelected;

final VoidCallback onTap;

@override
Widget build(BuildContext context) {
final isDesktop = desktopPlatforms.contains(Theme.of(context).platform);

return Container(
height: 60,
// selection color is only for desktop with keyboard navigation
color: (isSelected && isDesktop)
? Theme.of(context).colorScheme.primary
: null,
Comment on lines +27 to +34
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDesktop is an incorrect check here: pads can also have keyboards and laptops might also have touch devices. what I think you want to detect for here is a hardware keyboard, no?

but then also, how would isSelected even come about if not through a hardware-keyboard? I think this check is pointless and should be removed and the rest left to the outer part.

Suggested change
final isDesktop = desktopPlatforms.contains(Theme.of(context).platform);
return Container(
height: 60,
// selection color is only for desktop with keyboard navigation
color: (isSelected && isDesktop)
? Theme.of(context).colorScheme.primary
: null,
return Container(
height: 60,
color: isSelected ? Theme.of(context).colorScheme.primary : null,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, good point. I'll change the conditional 👍🏻 !

child: ListTile(
dense: true,
onTap: onTap,
contentPadding: const EdgeInsets.symmetric(horizontal: 12),
leading: ActerAvatar(options: avatarOptions),
title: Text(
displayName.isNotEmpty ? displayName : mentionId,
style: Theme.of(context).textTheme.bodyMedium,
),
subtitle: displayName.isNotEmpty
? Text(mentionId, style: Theme.of(context).textTheme.labelMedium)
: null,
),
);
}
}
Loading
Loading