inital work for selecting definitions

pull/1011/head
ggurdin 2 years ago
parent a9e5082bec
commit 0f3e1c7e16

@ -1,9 +1,9 @@
{
"dart.previewLsp": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true,
"source.sortMembers": false
"source.fixAll": "explicit",
"source.organizeImports": "explicit",
"source.sortMembers": "never"
},
"editor.formatOnSave": true
}

@ -3956,5 +3956,6 @@
"inNoSpaces": "You are not a member of any classes or exchanges",
"successfullySubscribed": "You have successfully subscribed!",
"clickToManageSubscription": "Click here to manage your subscription.",
"emptyInviteWarning": "Add this chat to a class or exchange to invite other users."
"emptyInviteWarning": "Add this chat to a class or exchange to invite other users.",
"showDefinition": "Show Definition"
}

@ -1,9 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:swipe_to_action/swipe_to_action.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/enum/use_type.dart';
import 'package:fluffychat/pangea/models/language_model.dart';
@ -12,6 +6,11 @@ import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
import 'package:swipe_to_action/swipe_to_action.dart';
import '../../../config/app_config.dart';
import 'message_content.dart';
import 'message_reactions.dart';

@ -1,9 +1,3 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/pages/chat/events/video_player.dart';
import 'package:fluffychat/pangea/models/language_model.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
@ -12,6 +6,11 @@ import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
import 'package:fluffychat/utils/date_time_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:matrix/matrix.dart';
import '../../../config/app_config.dart';
import '../../../utils/platform_infos.dart';
import '../../../utils/url_launcher.dart';

@ -1,20 +1,18 @@
import 'dart:developer';
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/models/span_card_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/overlay.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
import 'package:fluffychat/pangea/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/models/span_card_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import '../constants/model_keys.dart';
import '../utils/overlay.dart';
import '../widgets/igc/span_card.dart';
import '../widgets/igc/word_data_card.dart';
import 'language_detection_model.dart';
// import 'package:language_tool/language_tool.dart';
@ -150,35 +148,30 @@ class IGCTextData {
}
}
int tokenIndexByOffset(
cursorOffset,
) =>
tokens.indexWhere(
int tokenIndexByOffset(cursorOffset) => tokens.indexWhere(
(token) =>
token.text.offset <= cursorOffset &&
cursorOffset <= token.text.offset + token.text.length,
token.text.offset <= cursorOffset && cursorOffset <= token.end,
);
List<int> getMatchIndicesForToken(PangeaToken token) =>
matchIndicesByOffset(token.text.offset);
List<int> matchIndicesByOffset(int offset) {
final List<int> matchesForOffset = [];
for (final (index, match) in matches.indexed) {
if (match.isOffsetInMatchSpan(offset)) {
matchesForOffset.add(index);
}
}
return matchesForOffset;
}
int getTopMatchIndexForOffset(int offset) {
final List<int> matchesForToken = matchIndicesByOffset(offset);
if (matchesForToken.isEmpty) return -1;
for (final matchIndex in matchesForToken) {
final int matchIndex = matchesForToken.indexWhere((matchIndex) {
final match = matches[matchIndex];
if (enableIT) {
if (match.isITStart || match.isl1SpanMatch) {
return matchIndex;
}
}
if (enableIGC) {
if (match.isGrammarMatch) {
return matchIndex;
}
}
}
return -1;
return (enableIT && (match.isITStart || match.isl1SpanMatch)) ||
(enableIGC && match.isGrammarMatch);
});
if (matchIndex == -1) return -1;
return matchesForToken[matchIndex];
}
PangeaMatch? getTopMatchForToken(PangeaToken token) {
@ -187,23 +180,8 @@ class IGCTextData {
return matches[topMatchIndex];
}
List<int> matchIndicesByOffset(int offset) {
final List<int> matchesForOffset = [];
for (final (index, match) in matches.indexed) {
if (match.isOffsetInMatchSpan(offset)) {
matchesForOffset.add(index);
}
}
return matchesForOffset;
}
int getAfterTokenSpacingByIndex(
int tokenIndex,
) {
final int endOfToken =
tokens[tokenIndex].text.offset + tokens[tokenIndex].text.length;
int getAfterTokenSpacingByIndex(int tokenIndex) {
final int endOfToken = tokens[tokenIndex].end;
if (tokenIndex + 1 < tokens.length) {
final spaceBetween = tokens[tokenIndex + 1].text.offset - endOfToken;
@ -218,7 +196,7 @@ class IGCTextData {
),
);
ErrorHandler.logError(
m: "wierd token lengths for ${tokens[tokenIndex].text.content} and ${tokens[tokenIndex + 1].text.content}",
m: "weird token lengths for ${tokens[tokenIndex].text.content} and ${tokens[tokenIndex + 1].text.content}",
);
return 0;
}
@ -234,20 +212,42 @@ class IGCTextData {
decorationThickness: 5,
);
static const _hasDefinitionStyle = TextStyle(
decoration: TextDecoration.underline,
decorationColor: Color.fromARGB(148, 83, 97, 255),
decorationThickness: 4,
List<MatchToken> getMatchTokens() {
final List<MatchToken> matchTokens = [];
int? endTokenIndex;
PangeaMatch? topMatch;
for (final (i, token) in tokens.indexed) {
if (endTokenIndex != null) {
if (i <= endTokenIndex) {
matchTokens.add(
MatchToken(
token: token,
match: topMatch,
),
);
static TextStyle hasDefinitionStyle(TextStyle? existingStyle) =>
existingStyle?.merge(_hasDefinitionStyle) ?? _hasDefinitionStyle;
continue;
}
endTokenIndex = null;
}
topMatch = getTopMatchForToken(token);
if (topMatch != null) {
endTokenIndex = tokens.indexWhere((e) => e.end >= topMatch!.end, i);
}
matchTokens.add(
MatchToken(
token: token,
match: topMatch,
),
);
}
return matchTokens;
}
//PTODO - handle multitoken spans
List<TextSpan> constructTokenSpan({
required BuildContext context,
TextStyle? defaultStyle,
required SpanCardModel? spanCardModel,
required bool showTokens,
required bool handleClick,
required String transformTargetId,
required Room room,
@ -263,45 +263,43 @@ class IGCTextData {
];
}
// or could make big strings for non-match text and therefore make less textspans.
// would that be more performant?
tokens.asMap().forEach(
(index, token) {
final PangeaMatch? topTokenMatch = getTopMatchForToken(
tokens[index],
);
// if (index == 3) {
// debugPrint(
// "constructing span with topTokenMatch: ${topTokenMatch?.match.rule.id}");
// }
final Widget cardToShow = spanCardModel != null && topTokenMatch != null
? SpanCard(
scm: spanCardModel,
)
: WordDataCard(
fullText: originalInput,
fullTextLang: detections.first.langCode,
word: token.text.content,
wordLang: detections.first.langCode,
hasInfo: token.hasInfo,
room: room,
final List<MatchToken> matchTokens = getMatchTokens();
for (int tokenIndex = 0; tokenIndex < matchTokens.length; tokenIndex++) {
final MatchToken matchToken = matchTokens[tokenIndex];
final Widget? cardToShow =
matchToken.match != null && spanCardModel != null
? SpanCard(scm: spanCardModel)
: null;
int nextTokenIndex = matchTokens.indexWhere(
(e) => matchToken.match != null
? e.match != matchToken.match
: e.match != null,
tokenIndex,
);
final TextStyle tokenStyle = topTokenMatch != null
? topTokenMatch.textStyle(defaultStyle)
: hasDefinitionStyle(defaultStyle);
if (nextTokenIndex < 0) {
nextTokenIndex = matchTokens.length;
}
final String matchText = originalInput.substring(
matchTokens[tokenIndex].token.text.offset,
matchTokens[nextTokenIndex - 1].token.end,
);
items.add(
TextSpan(
text: token.text.content,
style: tokenStyle,
recognizer: handleClick
text: matchText,
style: matchTokens[tokenIndex].match?.textStyle(defaultStyle) ??
defaultStyle,
recognizer: handleClick && cardToShow != null
? (TapGestureRecognizer()
..onTapDown = (details) => OverlayUtil.showPositionedCard(
context: context,
cardToShow: cardToShow,
cardSize: topTokenMatch?.isITStart ?? false
cardSize:
matchTokens[tokenIndex].match?.isITStart ?? false
? const Size(350, 220)
: const Size(350, 400),
transformTargetId: transformTargetId,
@ -310,26 +308,32 @@ class IGCTextData {
),
);
final int charBetween = getAfterTokenSpacingByIndex(
index,
final String beforeNextToken = originalInput.substring(
matchTokens[nextTokenIndex - 1].token.end,
nextTokenIndex < matchTokens.length
? matchTokens[nextTokenIndex].token.text.offset
: originalInput.length,
);
if (charBetween > 0) {
if (beforeNextToken.isNotEmpty) {
items.add(
TextSpan(
text: " " * charBetween,
style: topTokenMatch != null &&
token.text.offset + token.text.length + charBetween <=
topTokenMatch.match.offset +
topTokenMatch.match.length
? tokenStyle
: defaultStyle,
text: beforeNextToken,
style: defaultStyle,
),
);
}
},
);
tokenIndex = nextTokenIndex - 1;
}
return items;
}
}
class MatchToken {
final PangeaToken token;
final PangeaMatch? match;
MatchToken({required this.token, this.match});
}

@ -1,10 +1,10 @@
import 'dart:developer';
import 'package:fluffychat/pangea/enum/span_data_type.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/enum/span_data_type.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import '../constants/match_rule_ids.dart';
import 'igc_text_data_model.dart';
import 'span_data.dart';
@ -127,4 +127,9 @@ class PangeaMatch {
IGCTextData.underlineStyle(underlineColor);
PangeaMatch get copyWith => PangeaMatch.fromJson(toJson());
int get beginning => match.offset < 0 ? 0 : match.offset;
int get end => match.offset + match.length > match.fullText.length
? match.fullText.length
: match.offset + match.length;
}

@ -1,7 +1,6 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import '../constants/model_keys.dart';
@ -65,6 +64,8 @@ class PangeaToken {
_hasInfoKey: hasInfo,
_lemmaKey: lemmas.map((e) => e.toJson()).toList(),
};
int get end => text.offset + text.length;
}
class PangeaTokenText {

@ -1,7 +1,6 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import '../models/widget_measurement.dart';
@ -38,6 +37,12 @@ class PangeaAnyState {
_layerLinkAndKeys.remove(transformTargetId);
}
void openOverlay(OverlayEntry entry, BuildContext context) {
closeOverlay();
overlay = entry;
Overlay.of(context).insert(overlay!);
}
void closeOverlay() {
if (overlay != null) {
overlay!.remove();

@ -1,48 +1,83 @@
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/pangea/utils/any_state_holder.dart';
import 'package:fluffychat/pangea/widgets/common_widgets/overlay_container.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import '../../config/themes.dart';
import '../../widgets/matrix.dart';
import 'error_handler.dart';
class OverlayUtil {
static showPositionedCard({
static showOverlay({
required BuildContext context,
required Widget cardToShow,
required Size cardSize,
required Widget child,
required Size size,
required String transformTargetId,
Offset? offset,
backDropToDismiss = true,
Color? borderColor,
}) {
try {
MatrixState.pAnyState.closeOverlay();
final LayerLinkAndKey layerLinkAndKey =
MatrixState.pAnyState.layerLinkAndKey(transformTargetId);
final Offset cardOffset = _calculateCardOffset(
cardSize: cardSize,
transformTargetKey: layerLinkAndKey.key,
);
MatrixState.pAnyState.overlay = OverlayEntry(
final OverlayEntry entry = OverlayEntry(
builder: (context) => Stack(
children: [
// GestureDetector to detect when dismissed by clicking outside
Positioned.fill(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
MatrixState.pAnyState.closeOverlay();
},
),
),
if (backDropToDismiss) const TransparentBackdrop(),
Positioned(
width: cardSize.width,
height: cardSize.height,
width: size.width,
height: size.height,
child: CompositedTransformFollower(
link: layerLinkAndKey.link,
showWhenUnlinked: false,
offset: cardOffset,
child: Material(
offset: offset ?? Offset.zero,
child: child,
),
),
],
),
);
MatrixState.pAnyState.openOverlay(entry, context);
} catch (err, stack) {
debugger(when: kDebugMode);
ErrorHandler.logError(e: err, s: stack);
}
}
static showPositionedCard({
required BuildContext context,
required Widget cardToShow,
required Size cardSize,
required String transformTargetId,
backDropToDismiss = true,
Color? borderColor,
}) {
try {
final LayerLinkAndKey layerLinkAndKey =
MatrixState.pAnyState.layerLinkAndKey(transformTargetId);
final Offset cardOffset = _calculateCardOffset(
cardSize: cardSize,
transformTargetKey: layerLinkAndKey.key,
);
final Widget child = Material(
borderOnForeground: false,
color: Colors.transparent,
clipBehavior: Clip.antiAlias,
@ -50,15 +85,17 @@ class OverlayUtil {
cardToShow: cardToShow,
borderColor: borderColor,
),
),
),
),
],
),
);
Overlay.of(layerLinkAndKey.key.currentContext!)
.insert(MatrixState.pAnyState.overlay!);
showOverlay(
context: context,
child: child,
size: cardSize,
transformTargetId: transformTargetId,
offset: cardOffset,
backDropToDismiss: backDropToDismiss,
borderColor: borderColor,
);
} catch (err, stack) {
debugger(when: kDebugMode);
ErrorHandler.logError(e: err, s: stack);
@ -132,6 +169,8 @@ class OverlayUtil {
return Offset(dx, dy);
}
static bool get isOverlayOpen => MatrixState.pAnyState.overlay != null;
}
class TransparentBackdrop extends StatelessWidget {

@ -0,0 +1,130 @@
import 'dart:async';
import 'package:fluffychat/pangea/utils/any_state_holder.dart';
import 'package:fluffychat/pangea/utils/overlay.dart';
import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
class ShowDefintionUtil {
final String messageText;
final String langCode;
final String targetId;
final FocusNode focusNode = FocusNode();
final Room room;
TextSelection? textSelection;
bool inCooldown = false;
ShowDefintionUtil({
required this.targetId,
required this.room,
required this.langCode,
required this.messageText,
});
void onTextSelection(
TextSelection selection,
SelectionChangedCause? cause,
BuildContext context,
) {
selection.isCollapsed
? clearTextSelection()
: setTextSelection(
selection,
cause,
context,
);
}
void setTextSelection(
TextSelection selection,
SelectionChangedCause? cause,
BuildContext context,
) {
textSelection = selection;
if (BrowserContextMenu.enabled && kIsWeb) {
BrowserContextMenu.disableContextMenu();
}
if (kIsWeb && cause != SelectionChangedCause.tap) {
handleToolbar(context);
}
}
void clearTextSelection() {
textSelection = null;
if (kIsWeb && !BrowserContextMenu.enabled) {
BrowserContextMenu.enableContextMenu();
}
}
void handleToolbar(BuildContext context) async {
if (inCooldown || OverlayUtil.isOverlayOpen || !kIsWeb) return;
inCooldown = true;
Timer(const Duration(milliseconds: 750), () => inCooldown = false);
await Future.delayed(const Duration(milliseconds: 750));
showToolbar(context);
}
void showDefinition(BuildContext context) {
final String? fullText = textSelection?.textInside(messageText);
if (fullText == null) return;
OverlayUtil.showPositionedCard(
context: context,
cardToShow: WordDataCard(
word: fullText,
wordLang: langCode,
fullText: messageText,
fullTextLang: langCode,
hasInfo: false,
room: room,
),
cardSize: const Size(300, 300),
transformTargetId: targetId,
backDropToDismiss: false,
);
}
// web toolbar
Future<dynamic> showToolbar(BuildContext context) async {
final LayerLinkAndKey layerLinkAndKey =
MatrixState.pAnyState.layerLinkAndKey(targetId);
final RenderBox? targetRenderBox =
(layerLinkAndKey.key.currentContext!.findRenderObject() as RenderBox?);
final Size? transformTargetSize = targetRenderBox?.size;
Offset? transformTargetOffset;
if (transformTargetSize != null) {
transformTargetOffset = Offset(
(transformTargetSize.width / 2) - 65,
transformTargetSize.height * -1,
);
}
OverlayUtil.showOverlay(
context: context,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
minimumSize: Size.zero,
padding: EdgeInsets.zero,
),
onPressed: () {
showDefinition(context);
},
child: Text(
L10n.of(context)!.showDefinition,
style: const TextStyle(
fontSize: 14,
),
),
),
size: const Size(130, 45),
transformTargetId: targetId,
offset: transformTargetOffset,
);
}
}

@ -1,11 +1,7 @@
import 'dart:async';
import 'dart:developer';
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/constants/language_keys.dart';
@ -13,12 +9,17 @@ import 'package:fluffychat/pangea/controllers/pangea_controller.dart';
import 'package:fluffychat/pangea/models/language_model.dart';
import 'package:fluffychat/pangea/models/pangea_message_event.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/show_defintion_util.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import '../../models/igc_text_data_model.dart';
import '../../models/language_detection_model.dart';
import '../../models/pangea_match_model.dart';
import '../../models/pangea_representation_event.dart';
import '../../utils/bot_style.dart';
import '../../utils/instructions.dart';
class PangeaRichText extends StatefulWidget {
@ -51,6 +52,7 @@ class PangeaRichTextState extends State<PangeaRichText> {
bool _fetchingTokens = false;
double get blur => _fetchingRepresentation && widget.immersionMode ? 5 : 0;
List<TextSpan> textSpan = [];
ShowDefintionUtil? messageToolbar;
@override
void initState() {
@ -68,6 +70,14 @@ class PangeaRichTextState extends State<PangeaRichText> {
Widget build(BuildContext context) {
//TODO - take out of build function of every message
// if (areLanguagesSet) {
messageToolbar = ShowDefintionUtil(
targetId: widget.pangeaMessageEvent.eventId,
room: widget.pangeaMessageEvent.room,
langCode: widget.selectedDisplayLang?.langCode ??
userL2LangCode ??
LanguageKeys.unknownLanguage,
messageText: textSpan.map((x) => x.text).join(),
);
if (!widget.selected &&
widget.selectedDisplayLang != null &&
@ -85,12 +95,10 @@ class PangeaRichTextState extends State<PangeaRichText> {
);
}
final Widget richText = RichText(
text: TextSpan(
final TextSpan richTextSpan = TextSpan(
children: [
...textSpan,
if (widget.selected && (_fetchingRepresentation || _fetchingTokens))
// if (widget.selected)
const WidgetSpan(
child: Padding(
padding: EdgeInsets.only(left: 5.0),
@ -105,8 +113,32 @@ class PangeaRichTextState extends State<PangeaRichText> {
),
),
],
);
final Widget richText = widget.selected
? SelectableText.rich(
richTextSpan,
onSelectionChanged: (selection, cause) => kIsWeb
? messageToolbar?.onTextSelection(selection, cause, context)
: null,
focusNode: messageToolbar?.focusNode,
contextMenuBuilder: (context, selection) {
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: selection.contextMenuAnchors,
buttonItems: [
...selection.contextMenuButtonItems,
ContextMenuButtonItem(
label: L10n.of(context)!.showDefinition,
onPressed: () {
messageToolbar?.showDefinition(context);
messageToolbar?.focusNode.unfocus();
},
),
],
);
},
)
: RichText(text: richTextSpan);
return blur > 0
? ImageFiltered(
@ -190,10 +222,9 @@ class PangeaRichTextState extends State<PangeaRichText> {
userL1: userL1LangCode ?? LanguageKeys.unknownLanguage,
).constructTokenSpan(
context: context,
defaultStyle: textStyle(repEvent, context),
defaultStyle: widget.existingStyle,
handleClick: true,
spanCardModel: null,
showTokens: widget.definitions,
transformTargetId: widget.pangeaMessageEvent.eventId,
room: widget.pangeaMessageEvent.room,
);
@ -213,20 +244,10 @@ class PangeaRichTextState extends State<PangeaRichText> {
[
TextSpan(
text: repEvent.text,
style: textStyle(repEvent, context),
style: widget.existingStyle,
),
];
TextStyle? textStyle(RepresentationEvent repEvent, BuildContext context) =>
// !repEvent.botAuthored
true
? widget.existingStyle
: BotStyle.text(
context,
existingStyle: widget.existingStyle,
setColor: false,
);
bool get areLanguagesSet =>
userL2LangCode != null && userL2LangCode != LanguageKeys.unknownLanguage;
@ -258,4 +279,75 @@ class PangeaRichTextState extends State<PangeaRichText> {
Future<void> onSentenceRewrite(String sentenceRewrite) async {
debugPrint("PTODO implement onSentenceRewrite");
}
// void onTextSelection(
// TextSelection selection,
// SelectionChangedCause? _,
// ) =>
// selection.isCollapsed
// ? clearTextSelection()
// : setTextSelection(selection);
// void setTextSelection(TextSelection selection) {
// textSelection = selection;
// if (BrowserContextMenu.enabled && kIsWeb) {
// BrowserContextMenu.disableContextMenu();
// }
// kIsWeb ? showToolbar() : showDefinition();
// }
// void clearTextSelection() {
// textSelection = null;
// if (kIsWeb && !BrowserContextMenu.enabled) {
// BrowserContextMenu.enableContextMenu();
// }
// }
// void showToolbar() async {
// if (toolbarShowing || !kIsWeb) return;
// toolbarShowing = true;
// await Future.delayed(const Duration(seconds: 2));
// final toolbarFuture = MessageToolbar.showToolbar(
// context,
// widget.pangeaMessageEvent.eventId,
// _focusNode.offset,
// );
// final resp = await toolbarFuture;
// toolbarShowing = false;
// switch (resp) {
// case null:
// break;
// case 1:
// showDefinition();
// break;
// default:
// break;
// }
// }
// void showDefinition() {
// final String messageText = textSpan.map((x) => x.text).join();
// final String fullText = textSelection!.textInside(messageText);
// final String langCode = widget.selectedDisplayLang?.langCode ??
// userL2LangCode ??
// LanguageKeys.unknownLanguage;
// OverlayUtil.showPositionedCard(
// context: context,
// cardToShow: WordDataCard(
// word: fullText,
// wordLang: langCode,
// fullText: messageText,
// fullTextLang: langCode,
// hasInfo: false,
// room: widget.pangeaMessageEvent.room,
// ),
// cardSize: const Size(300, 300),
// transformTargetId: widget.pangeaMessageEvent.eventId,
// backDropToDismiss: false,
// );
// }
}

@ -1,13 +1,11 @@
import 'dart:developer';
import 'package:fluffychat/pangea/widgets/igc/span_card.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/widgets/igc/span_card.dart';
import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart';
import '../../choreographer/controllers/choreographer.dart';
import '../../enum/edit_type.dart';
import '../../models/pangea_token_model.dart';
import '../../models/span_card_model.dart';
import '../../models/widget_measurement.dart';
import '../../utils/overlay.dart';
@ -53,12 +51,11 @@ class PangeaTextController extends TextEditingController {
if (tokenIndex == -1) return;
final PangeaToken token = choreographer.igc.igcTextData!.tokens[tokenIndex];
final int matchIndex =
choreographer.igc.igcTextData!.getTopMatchIndexForOffset(
selection.baseOffset,
);
final Widget cardToShow = matchIndex != -1
final Widget? cardToShow = matchIndex != -1
? SpanCard(
scm: SpanCardModel(
// igcTextData: choreographer.igc.igcTextData!,
@ -80,18 +77,9 @@ class PangeaTextController extends TextEditingController {
),
roomId: choreographer.roomId,
)
: WordDataCard(
fullText: text,
fullTextLang:
choreographer.igc.igcTextData!.detections.first.langCode,
word: token.text.content,
//Note: this assumes that the token must be in the target language
//since it didn't have a match
wordLang: choreographer.itController.targetLangCode,
hasInfo: token.hasInfo,
room: choreographer.chatController.room,
);
: null;
if (cardToShow != null) {
OverlayUtil.showPositionedCard(
context: context,
cardSize: matchIndex != -1 &&
@ -102,6 +90,7 @@ class PangeaTextController extends TextEditingController {
transformTargetId: choreographer.inputTransformTargetKey,
);
}
}
@override
TextSpan buildTextSpan({
@ -139,7 +128,6 @@ class PangeaTextController extends TextEditingController {
...choreographer.igc.igcTextData!.constructTokenSpan(
context: context,
defaultStyle: style,
showTokens: choreographer.definitionsEnabled,
spanCardModel: null,
handleClick: false,
transformTargetId: choreographer.inputTransformTargetKey,

@ -765,7 +765,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"bn": [
@ -1539,7 +1540,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"bo": [
@ -2313,7 +2315,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"ca": [
@ -3082,7 +3085,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"cs": [
@ -3851,7 +3855,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"de": [
@ -4620,7 +4625,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"el": [
@ -5394,7 +5400,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"eo": [
@ -6163,7 +6170,12 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"es": [
"showDefinition"
],
"et": [
@ -6932,7 +6944,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"eu": [
@ -7701,7 +7714,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"fa": [
@ -8470,7 +8484,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"fi": [
@ -9239,7 +9254,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"fr": [
@ -10008,7 +10024,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"ga": [
@ -10777,7 +10794,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"gl": [
@ -11546,7 +11564,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"he": [
@ -12315,7 +12334,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"hi": [
@ -13089,7 +13109,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"hr": [
@ -13858,7 +13879,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"hu": [
@ -14627,7 +14649,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"id": [
@ -15396,7 +15419,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"ie": [
@ -16167,7 +16191,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"it": [
@ -16936,7 +16961,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"ja": [
@ -17705,7 +17731,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"ko": [
@ -18474,7 +18501,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"lt": [
@ -19243,7 +19271,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"lv": [
@ -20017,7 +20046,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"nb": [
@ -20786,7 +20816,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"nl": [
@ -21555,7 +21586,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"pl": [
@ -22324,7 +22356,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"pt": [
@ -23098,7 +23131,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"pt_BR": [
@ -23867,7 +23901,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"pt_PT": [
@ -24636,7 +24671,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"ro": [
@ -25405,7 +25441,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"ru": [
@ -26174,7 +26211,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"sk": [
@ -26944,7 +26982,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"sl": [
@ -27716,7 +27755,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"sr": [
@ -28485,7 +28525,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"sv": [
@ -29254,7 +29295,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"ta": [
@ -30028,7 +30070,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"th": [
@ -30802,7 +30845,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"tr": [
@ -31571,7 +31615,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"uk": [
@ -32340,7 +32385,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"vi": [
@ -33112,7 +33158,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"zh": [
@ -33881,7 +33928,8 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
],
"zh_Hant": [
@ -34650,6 +34698,7 @@
"activateTrial",
"successfullySubscribed",
"clickToManageSubscription",
"emptyInviteWarning"
"emptyInviteWarning",
"showDefinition"
]
}

@ -138,6 +138,7 @@ flutter:
generate: true
uses-material-design: true
assets:
- .env
- assets/
# #Pangea
- assets/pangea/

Loading…
Cancel
Save