forked from Nexus/nexus
make hovering a reaction show its shortcode
This commit is contained in:
parent
d5f29dc344
commit
ffbb0ad9e2
5 changed files with 73 additions and 12 deletions
31
lib/controllers/emoji_shortcodes.dart
Normal file
31
lib/controllers/emoji_shortcodes.dart
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import "package:collection/collection.dart";
|
||||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:material_emoji_picker/material_emoji_picker.dart";
|
||||
|
||||
/// Maps each unicode emoji, without variation selectors, to its shortcode.
|
||||
class EmojiShortcodesController extends Notifier<IMap<String, String>> {
|
||||
static final _variationSelectors = RegExp("[\uFE0E\uFE0F]");
|
||||
|
||||
static String normalize(String emoji) =>
|
||||
emoji.replaceAll(_variationSelectors, "");
|
||||
|
||||
@override
|
||||
IMap<String, String> build() => .fromEntries(
|
||||
ref
|
||||
.watch(EmojiController.provider)
|
||||
.requireValue
|
||||
.map((category) => category.emojis)
|
||||
.flattened
|
||||
.where((emoji) => emoji.aliases.isNotEmpty)
|
||||
.map(
|
||||
(emoji) =>
|
||||
MapEntry(normalize(emoji.value), ":${emoji.aliases.first}:"),
|
||||
),
|
||||
);
|
||||
|
||||
static final provider =
|
||||
NotifierProvider<EmojiShortcodesController, IMap<String, String>>(
|
||||
EmojiShortcodesController.new,
|
||||
);
|
||||
}
|
||||
|
|
@ -1,14 +1,18 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/emoji_shortcodes.dart";
|
||||
import "package:nexus/controllers/rooms.dart";
|
||||
import "package:nexus/models/configs/event.dart";
|
||||
import "package:nexus/models/content/reaction.dart";
|
||||
import "package:nexus/models/reaction_group.dart";
|
||||
|
||||
class ReactionsController(final EventConfig config)
|
||||
extends AsyncNotifier<IMap<String, IList<String>>> {
|
||||
extends AsyncNotifier<IMap<String, ReactionGroup>> {
|
||||
@override
|
||||
Future<IMap<String, IList<String>>> build() async {
|
||||
Future<IMap<String, ReactionGroup>> build() async {
|
||||
final unicodeShortcodes = ref.watch(EmojiShortcodesController.provider);
|
||||
|
||||
final eventInfo = ref.watch(
|
||||
RoomsController.provider.select((value) {
|
||||
final event = value[config.roomId]?.events[config.eventRowId];
|
||||
|
|
@ -30,12 +34,25 @@ class ReactionsController(final EventConfig config)
|
|||
|
||||
return reactionEvents
|
||||
?.where((event) => event.redactedBy == null)
|
||||
.fold<IMap<String, IList<String>>>(.new(), (acc, event) {
|
||||
if (event.content case ReactionContent(:final key?)) {
|
||||
.fold<IMap<String, ReactionGroup>>(.new(), (acc, event) {
|
||||
if (event.content case ReactionContent(
|
||||
:final key?,
|
||||
:final shortcode,
|
||||
)) {
|
||||
return acc.update(
|
||||
key,
|
||||
(list) => list.add(event.sender),
|
||||
ifAbsent: () => .new([event.sender]),
|
||||
(group) => group.copyWith(
|
||||
senders: group.senders.add(event.sender),
|
||||
shortcode: group.shortcode ?? shortcode,
|
||||
),
|
||||
ifAbsent: () => .new(
|
||||
senders: .new([event.sender]),
|
||||
shortcode:
|
||||
shortcode ??
|
||||
unicodeShortcodes[EmojiShortcodesController.normalize(
|
||||
key,
|
||||
)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -47,7 +64,7 @@ class ReactionsController(final EventConfig config)
|
|||
static final provider =
|
||||
AsyncNotifierProvider.family<
|
||||
ReactionsController,
|
||||
IMap<String, IList<String>>,
|
||||
IMap<String, ReactionGroup>,
|
||||
EventConfig
|
||||
>(ReactionsController.new);
|
||||
}
|
||||
|
|
|
|||
10
lib/models/reaction_group.dart
Normal file
10
lib/models/reaction_group.dart
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import "package:fast_immutable_collections/fast_immutable_collections.dart";
|
||||
import "package:freezed_annotation/freezed_annotation.dart";
|
||||
|
||||
part "reaction_group.freezed.dart";
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
class const ReactionGroup({
|
||||
required final IList<String> senders,
|
||||
final String? shortcode,
|
||||
}) with _$ReactionGroup;
|
||||
|
|
@ -7,6 +7,7 @@ import "package:nexus/controllers/reactions.dart";
|
|||
import "package:nexus/controllers/room_chat.dart";
|
||||
import "package:material_adaptive_sheets/material_adaptive_sheets.dart";
|
||||
import "package:nexus/models/event.dart";
|
||||
import "package:nexus/models/reaction_group.dart";
|
||||
import "package:nexus/widgets/common/error_dialog.dart";
|
||||
import "package:nexus/widgets/reactions/reaction_key.dart";
|
||||
import "package:nexus/widgets/reactions/reactions_popover.dart";
|
||||
|
|
@ -25,7 +26,7 @@ class const ReactionRow(final Event event, {super.key}) extends ConsumerWidget {
|
|||
.new(roomId: event.roomId, eventRowId: event.rowId),
|
||||
),
|
||||
)) {
|
||||
AsyncData(value: final IMap<String, IList<String>>? reactors) ||
|
||||
AsyncData(value: final IMap<String, ReactionGroup>? reactors) ||
|
||||
AsyncLoading(value: final reactors) => Wrap(
|
||||
spacing: 4,
|
||||
runSpacing: 4,
|
||||
|
|
@ -37,7 +38,9 @@ class const ReactionRow(final Event event, {super.key}) extends ConsumerWidget {
|
|||
final enabled = useState(true);
|
||||
|
||||
final selected =
|
||||
reactors?[reaction]?.contains(clientState!.userId) ??
|
||||
reactors?[reaction]?.senders.contains(
|
||||
clientState!.userId,
|
||||
) ??
|
||||
false;
|
||||
void showReactions(PositionedGestureDetails details) =>
|
||||
context.showAdaptivePopover(
|
||||
|
|
@ -52,7 +55,7 @@ class const ReactionRow(final Event event, {super.key}) extends ConsumerWidget {
|
|||
onSecondaryTapUp: showReactions,
|
||||
onLongPressStart: showReactions,
|
||||
child: Tooltip(
|
||||
message: reactors?[reaction]?.join(", ") ?? "",
|
||||
message: reactors?[reaction]?.shortcode ?? reaction,
|
||||
child: ChoiceChip(
|
||||
showCheckmark: false,
|
||||
selected: selected,
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class const ReactionsPopover(
|
|||
spacing: 8,
|
||||
children: [
|
||||
ReactionKey(reaction, height: 20),
|
||||
Text(reactions[reaction]!.length.toString()),
|
||||
Text(reactions[reaction]!.senders.length.toString()),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|
@ -98,7 +98,7 @@ class const ReactionsPopover(
|
|||
padding: .symmetric(vertical: 8),
|
||||
child: Column(
|
||||
crossAxisAlignment: .stretch,
|
||||
children: reactions[reaction]!
|
||||
children: reactions[reaction]!.senders
|
||||
.map(
|
||||
(userId) => Consumer(
|
||||
builder: (context, ref, _) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue