You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fluffychat/lib/pangea/lemmas/lemma_emoji_row.dart

284 lines
8.1 KiB
Dart

import 'dart:developer';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/app_emojis.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/utils/overlay.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/lemmas/user_set_lemma_info.dart';
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.dart';
import 'package:fluffychat/widgets/matrix.dart';
class LemmaEmojiRow extends StatefulWidget {
final ConstructIdentifier cId;
final VoidCallback? onTapOverride;
final bool isSelected;
final bool shouldShowEmojis;
final double? iconSize;
/// if a setState is defined then we're in a context where
/// we allow removing an emoji
/// later we'll probably want to allow this everywhere
final void Function()? emojiSetCallback;
const LemmaEmojiRow({
super.key,
required this.cId,
required this.onTapOverride,
required this.isSelected,
required this.shouldShowEmojis,
this.emojiSetCallback,
this.iconSize,
});
@override
LemmaEmojiRowState createState() => LemmaEmojiRowState();
}
class LemmaEmojiRowState extends State<LemmaEmojiRow> {
String? displayEmoji;
@override
void initState() {
super.initState();
displayEmoji = widget.cId.userSetEmoji.firstOrNull;
}
@override
didUpdateWidget(LemmaEmojiRow oldWidget) {
if (oldWidget.isSelected != widget.isSelected ||
widget.cId.userSetEmoji != oldWidget.cId.userSetEmoji) {
setState(() => displayEmoji = widget.cId.userSetEmoji.firstOrNull);
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
MatrixState.pAnyState.closeOverlay(widget.cId.string);
super.dispose();
}
void openEmojiSetOverlay() async {
List<String> emojiChoices = [];
try {
final info = await widget.cId.getLemmaInfo();
emojiChoices = info.emoji;
} catch (e, s) {
for (int i = 0; i < 3; i++) {
emojiChoices
.add(AppEmojis.emojis[Random().nextInt(AppEmojis.emojis.length)]);
}
debugger(when: kDebugMode);
ErrorHandler.logError(data: widget.cId.toJson(), e: e, s: s);
}
try {
OverlayUtil.showOverlay(
context: context,
child: EmojiEditOverlay(
cId: widget.cId,
onSelectEmoji: setEmoji,
emojis: emojiChoices,
),
transformTargetId: widget.cId.string,
backDropToDismiss: true,
blurBackground: false,
borderColor: Theme.of(context).colorScheme.primary,
closePrevOverlay: false,
followerAnchor: Alignment.topCenter,
targetAnchor: Alignment.bottomCenter,
);
} catch (e, s) {
debugger(when: kDebugMode);
ErrorHandler.logError(data: widget.cId.toJson(), e: e, s: s);
}
}
Future<void> setEmoji(String emoji) async {
try {
displayEmoji = emoji;
widget.cId
.setUserLemmaInfo(
UserSetLemmaInfo(
emojis: [emoji],
),
)
.catchError((e, s) {
debugger(when: kDebugMode);
ErrorHandler.logError(data: widget.cId.toJson(), e: e, s: s);
}).then((_) {
if (mounted) {
widget.emojiSetCallback?.call();
setState(() {});
}
});
MatrixState.pAnyState.closeOverlay();
widget.emojiSetCallback?.call();
setState(() {});
} catch (e, s) {
debugger(when: kDebugMode);
ErrorHandler.logError(data: widget.cId.toJson(), e: e, s: s);
}
}
@override
Widget build(BuildContext context) {
return Material(
child: CompositedTransformTarget(
link: MatrixState.pAnyState
.layerLinkAndKey(
widget.cId.string,
)
.link,
child: Container(
key: MatrixState.pAnyState
.layerLinkAndKey(
widget.cId.string,
)
.key,
height: 50,
width: 50,
alignment: Alignment.center,
child: displayEmoji != null && widget.shouldShowEmojis
? InkWell(
hoverColor:
Theme.of(context).colorScheme.primary.withAlpha(50),
onTap: widget.onTapOverride ?? openEmojiSetOverlay,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
displayEmoji!,
style: TextStyle(fontSize: widget.iconSize ?? 20),
),
),
)
: WordZoomActivityButton(
icon: Icon(
Icons.add_reaction_outlined,
color: widget.isSelected
? Theme.of(context).colorScheme.primary
: null,
),
isSelected: widget.isSelected,
onPressed: widget.onTapOverride ?? openEmojiSetOverlay,
opacity: widget.isSelected ? 1 : 0.4,
tooltip: MessageMode.wordEmoji.title(context),
),
),
),
);
}
}
class EmojiEditOverlay extends StatelessWidget {
final Function(String) onSelectEmoji;
final ConstructIdentifier cId;
final List<String> emojis;
const EmojiEditOverlay({
super.key,
required this.onSelectEmoji,
required this.cId,
required this.emojis,
});
@override
Widget build(BuildContext context) {
return Material(
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
child: Container(
padding: const EdgeInsets.all(8),
height: 70,
width: 200,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
boxShadow: [
BoxShadow(
color: Theme.of(context).colorScheme.onSurface.withAlpha(50),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
alignment: Alignment.center,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: emojis
.map(
(emoji) => EmojiChoiceItem(
emoji: emoji,
onSelectEmoji: onSelectEmoji,
),
)
.toList(),
),
),
),
);
}
}
class EmojiChoiceItem extends StatefulWidget {
final String emoji;
final Function(String) onSelectEmoji;
const EmojiChoiceItem({
super.key,
required this.emoji,
required this.onSelectEmoji,
});
@override
EmojiChoiceItemState createState() => EmojiChoiceItemState();
}
class EmojiChoiceItemState extends State<EmojiChoiceItem> {
bool _isHovered = false;
@override
Widget build(BuildContext context) {
return MouseRegion(
onEnter: (_) => setState(() => _isHovered = true),
onExit: (_) => setState(() => _isHovered = false),
child: GestureDetector(
onTap: () {
debugPrint('Selected emoji: ${widget.emoji}');
if (!mounted) {
return;
}
widget.onSelectEmoji(widget.emoji);
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: _isHovered
? Theme.of(context).colorScheme.primary.withAlpha(50)
: Colors.transparent,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
),
child: Text(
widget.emoji,
style: Theme.of(context).textTheme.headlineSmall,
),
),
),
);
}
}