merge conflict

pull/1011/head
ggurdin 2 years ago
commit c16fff76a9

@ -3881,5 +3881,6 @@
"conversationTopic": "Conversation topic",
"enableModeration": "Enable moderation",
"enableModerationDesc": "Enable automatic moderation to review messages before they are sent",
"conversationLanguageLevel": "What is the language level of this conversation?"
"conversationLanguageLevel": "What is the language level of this conversation?",
"showDefinition": "Show Definition"
}

@ -3,8 +3,6 @@
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.im.fluffychat.app</string>
</array>
<array/>
</dict>
</plist>

@ -21,6 +21,6 @@
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
<string>12.0</string>
</dict>
</plist>

@ -457,7 +457,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@ -546,7 +546,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@ -595,7 +595,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;

@ -27,8 +27,6 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
@ -38,8 +36,8 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@ -61,8 +59,6 @@
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"

@ -63,10 +63,10 @@ abstract class AppConfig {
static const bool enableSentry = true;
static const String sentryDns =
'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143';
//#Pangea
// #Pangea
static bool renderHtml = false;
// static bool renderHtml = true;
//Pangea#
// Pangea#
static bool hideRedactedEvents = false;
static bool hideUnknownEvents = true;
static bool hideUnimportantStateEvents = true;

@ -23,7 +23,6 @@ import 'package:fluffychat/pangea/models/message_data_models.dart';
import 'package:fluffychat/pangea/models/student_analytics_summary_model.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/utils/firebase_analytics.dart';
import 'package:fluffychat/pangea/utils/instructions.dart';
import 'package:fluffychat/pangea/utils/report_message.dart';
import 'package:fluffychat/pangea/widgets/igc/pangea_text_controller.dart';
import 'package:fluffychat/utils/adaptive_bottom_sheet.dart';
@ -1282,11 +1281,6 @@ class ChatController extends State<ChatPageWithRoom>
if (choreographer.itController.isOpen) {
return;
}
pangeaController.instructions.show(
context,
InstructionsEnum.understandingMessages,
event.eventId,
);
// Pangea#
if (!event.redacted) {
if (selectedEvents.contains(event)) {

@ -1,6 +1,10 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/utils/show_defintion_util.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_highlighter/flutter_highlighter.dart';
import 'package:flutter_highlighter/themes/shades-of-purple.dart';
import 'package:flutter_html/flutter_html.dart';
@ -10,21 +14,24 @@ import 'package:html/dom.dart' as dom;
import 'package:linkify/linkify.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import '../../../utils/url_launcher.dart';
class HtmlMessage extends StatelessWidget {
final String html;
final Room room;
final Color textColor;
// #Pangea
final ShowDefintionUtil? messageToolbar;
// Pangea#
const HtmlMessage({
super.key,
required this.html,
required this.room,
this.textColor = Colors.black,
// #Pangea
this.messageToolbar,
// Pangea#
});
dom.Node _linkifyHtml(dom.Node element) {
@ -92,84 +99,108 @@ class HtmlMessage extends StatelessWidget {
final element = _linkifyHtml(HtmlParser.parseHTML(renderHtml));
// there is no need to pre-validate the html, as we validate it while rendering
return Html.fromElement(
documentElement: element as dom.Element,
style: {
'*': Style(
color: textColor,
margin: Margins.all(0),
fontSize: FontSize(fontSize),
),
'a': Style(color: linkColor, textDecorationColor: linkColor),
'h1': Style(
fontSize: FontSize(fontSize * 2),
lineHeight: LineHeight.number(1.5),
fontWeight: FontWeight.w600,
),
'h2': Style(
fontSize: FontSize(fontSize * 1.75),
lineHeight: LineHeight.number(1.5),
fontWeight: FontWeight.w500,
),
'h3': Style(
fontSize: FontSize(fontSize * 1.5),
lineHeight: LineHeight.number(1.5),
),
'h4': Style(
fontSize: FontSize(fontSize * 1.25),
lineHeight: LineHeight.number(1.5),
),
'h5': Style(
fontSize: FontSize(fontSize * 1.25),
lineHeight: LineHeight.number(1.5),
),
'h6': Style(
fontSize: FontSize(fontSize),
lineHeight: LineHeight.number(1.5),
),
'blockquote': blockquoteStyle,
'tg-forward': blockquoteStyle,
'hr': Style(
border: Border.all(color: textColor, width: 0.5),
),
'table': Style(
border: Border.all(color: textColor, width: 0.5),
// #Pangea
return MouseRegion(
onHover: messageToolbar?.onMouseRegionUpdate,
child: SelectionArea(
onSelectionChanged: (SelectedContent? selection) =>
messageToolbar?.onTextSelection(
selectedContent: selection,
context: context,
),
'tr': Style(
border: Border.all(color: textColor, width: 0.5),
),
'td': Style(
border: Border.all(color: textColor, width: 0.5),
padding: HtmlPaddings.all(2),
),
'th': Style(
border: Border.all(color: textColor, width: 0.5),
),
},
extensions: [
RoomPillExtension(context, room),
CodeExtension(fontSize: fontSize),
MatrixMathExtension(
style: TextStyle(fontSize: fontSize, color: textColor),
focusNode: messageToolbar?.focusNode,
contextMenuBuilder: (context, state) =>
messageToolbar?.contextMenuOverride(
context: context,
contentSelection: state,
) ??
const SizedBox(),
// Pangea#
child: Html.fromElement(
documentElement: element as dom.Element,
style: {
'*': Style(
color: textColor,
margin: Margins.all(0),
fontSize: FontSize(fontSize),
),
'a': Style(color: linkColor, textDecorationColor: linkColor),
'h1': Style(
fontSize: FontSize(fontSize * 2),
lineHeight: LineHeight.number(1.5),
fontWeight: FontWeight.w600,
),
'h2': Style(
fontSize: FontSize(fontSize * 1.75),
lineHeight: LineHeight.number(1.5),
fontWeight: FontWeight.w500,
),
'h3': Style(
fontSize: FontSize(fontSize * 1.5),
lineHeight: LineHeight.number(1.5),
),
'h4': Style(
fontSize: FontSize(fontSize * 1.25),
lineHeight: LineHeight.number(1.5),
),
'h5': Style(
fontSize: FontSize(fontSize * 1.25),
lineHeight: LineHeight.number(1.5),
),
'h6': Style(
fontSize: FontSize(fontSize),
lineHeight: LineHeight.number(1.5),
),
'blockquote': blockquoteStyle,
'tg-forward': blockquoteStyle,
'hr': Style(
border: Border.all(color: textColor, width: 0.5),
),
'table': Style(
border: Border.all(color: textColor, width: 0.5),
),
'tr': Style(
border: Border.all(color: textColor, width: 0.5),
),
'td': Style(
border: Border.all(color: textColor, width: 0.5),
padding: HtmlPaddings.all(2),
),
'th': Style(
border: Border.all(color: textColor, width: 0.5),
),
},
extensions: [
RoomPillExtension(context, room),
CodeExtension(fontSize: fontSize),
MatrixMathExtension(
style: TextStyle(fontSize: fontSize, color: textColor),
),
const TableHtmlExtension(),
SpoilerExtension(textColor: textColor),
const ImageExtension(),
FontColorExtension(),
],
onLinkTap: (url, _, element) => UrlLauncher(
context,
url,
element?.text,
).launchUrl(),
onlyRenderTheseTags: const {
...allowedHtmlTags,
// Needed to make it work properly
'body',
'html',
},
shrinkWrap: true,
),
const TableHtmlExtension(),
SpoilerExtension(textColor: textColor),
const ImageExtension(),
FontColorExtension(),
],
onLinkTap: (url, _, element) => UrlLauncher(
context,
url,
element?.text,
).launchUrl(),
onlyRenderTheseTags: const {
...allowedHtmlTags,
// Needed to make it work properly
'body',
'html',
},
shrinkWrap: true,
),
);
// ),
// ],
// ),
// ),
// );
}
/// Keep in sync with: https://spec.matrix.org/v1.6/client-server-api/#mroommessage-msgtypes

@ -1,23 +1,24 @@
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/html_message.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';
import 'package:fluffychat/pangea/utils/show_defintion_util.dart';
import 'package:fluffychat/pangea/widgets/igc/pangea_rich_text.dart';
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:fluffychat/widgets/matrix.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';
import 'audio_player.dart';
import 'cute_events.dart';
import 'html_message.dart';
import 'image_bubble.dart';
import 'map_bubble.dart';
import 'message_download_content.dart';
@ -37,9 +38,10 @@ class MessageContent extends StatelessWidget {
final LanguageModel? selectedDisplayLang;
final bool immersionMode;
final bool definitions;
ShowDefintionUtil? messageToolbar;
// Pangea#
const MessageContent(
MessageContent(
this.event, {
this.onInfoTab,
super.key,
@ -122,6 +124,18 @@ class MessageContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
// #Pangea
messageToolbar = ShowDefintionUtil(
targetId: pangeaMessageEvent.eventId,
room: pangeaMessageEvent.room,
langCode: selectedDisplayLang?.langCode ??
MatrixState.pangeaController.languageController.activeL2Code(
roomID: pangeaMessageEvent.room.id,
) ??
LanguageModel.unknown.langCode,
messageText: "",
);
// Pangea#
final fontSize = AppConfig.messageFontSize * AppConfig.fontSizeFactor;
final buttonTextColor = textColor;
switch (event.type) {
@ -168,16 +182,25 @@ class MessageContent extends StatelessWidget {
case MessageTypes.Notice:
case MessageTypes.Emote:
if (AppConfig.renderHtml &&
!event.redacted &&
event.isRichMessage) {
!event.redacted &&
event.isRichMessage
// #Pangea
&&
!pangeaMessageEvent.showRichText
// Pangea#
) {
var html = event.formattedText;
if (event.messageType == MessageTypes.Emote) {
html = '* $html';
}
// #Pangea
messageToolbar?.messageText = html;
// Pangea#
return HtmlMessage(
html: html,
textColor: textColor,
room: event.room,
messageToolbar: messageToolbar,
);
}
// else we fall through to the normal message rendering
@ -264,55 +287,83 @@ class MessageContent extends StatelessWidget {
height: 1.3,
);
if (pangeaMessageEvent.showRichText) {
return PangeaRichText(
existingStyle: messageTextStyle,
selected: selected,
pangeaMessageEvent: pangeaMessageEvent,
immersionMode: immersionMode,
definitions: definitions,
selectedDisplayLang: selectedDisplayLang,
return MouseRegion(
onHover: messageToolbar?.onMouseRegionUpdate,
child: PangeaRichText(
style: messageTextStyle,
selected: selected,
pangeaMessageEvent: pangeaMessageEvent,
immersionMode: immersionMode,
definitions: definitions,
selectedDisplayLang: selectedDisplayLang,
messageToolbar: messageToolbar,
),
);
}
//Pangea#
return FutureBuilder<String>(
future: event.calcLocalizedBody(
MatrixLocals(L10n.of(context)!),
hideReply: true,
),
builder: (context, snapshot) {
// #Pangea
if (!snapshot.hasData) {
return Text(
event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
),
style: messageTextStyle,
);
}
return MouseRegion(
onHover: messageToolbar?.onMouseRegionUpdate,
child: FutureBuilder<String>(
// Pangea#
return Linkify(
text: snapshot.data ??
future: event.calcLocalizedBody(
MatrixLocals(L10n.of(context)!),
hideReply: true,
),
builder: (context, snapshot) {
// #Pangea
if (!snapshot.hasData) {
return Text(
event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
),
style: TextStyle(
color: textColor,
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration:
event.redacted ? TextDecoration.lineThrough : null,
),
options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle(
color: textColor.withAlpha(150),
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: TextDecoration.underline,
decorationColor: textColor.withAlpha(150),
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
);
},
style: messageTextStyle,
);
}
// return Linkify(
final String messageText = snapshot.data ??
event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
);
messageToolbar?.messageText = messageText;
return SelectableLinkify(
// Pangea#
text: messageText,
focusNode: messageToolbar?.focusNode,
contextMenuBuilder: (context, state) =>
messageToolbar?.contextMenuOverride(
context: context,
textSelection: state,
) ??
const SizedBox(),
// text: snapshot.data ??
// event.calcLocalizedBodyFallback(
// MatrixLocals(L10n.of(context)!),
// hideReply: true,
// ),
style: TextStyle(
color: textColor,
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration:
event.redacted ? TextDecoration.lineThrough : null,
),
options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle(
color: textColor.withAlpha(150),
fontSize: bigEmotes ? fontSize * 3 : fontSize,
decoration: TextDecoration.underline,
decorationColor: textColor.withAlpha(150),
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
onSelectionChanged: (selection, cause) =>
messageToolbar?.onTextSelection(
selectedText: selection,
cause: cause,
context: context,
),
);
},
),
);
}
case EventTypes.CallInvite:

@ -11,7 +11,10 @@ import 'package:fluffychat/pangea/utils/bot_name.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
// import markdown.dart
import 'package:html_unescape/html_unescape.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix/src/utils/markdown.dart';
import 'package:matrix/src/utils/space_child.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
@ -859,7 +862,7 @@ extension PangeaRoom on Room {
String? txid,
Event? inReplyTo,
String? editEventId,
bool parseMarkdown = false,
bool parseMarkdown = true,
bool parseCommands = false,
String msgtype = MessageTypes.Text,
String? threadRootEventId,
@ -889,17 +892,19 @@ extension PangeaRoom on Room {
ModelKey.tokensWritten: tokensWritten?.toJson(),
ModelKey.useType: useType?.string,
};
// if (parseMarkdown) {
// final html = markdown(event['body'],
// getEmotePacks: () => getImagePacksFlat(ImagePackUsage.emoticon),
// getMention: getMention);
// // if the decoded html is the same as the body, there is no need in sending a formatted message
// if (HtmlUnescape().convert(html.replaceAll(RegExp(r'<br />\n?'), '\n')) !=
// event['body']) {
// event['format'] = 'org.matrix.custom.html';
// event['formatted_body'] = html;
// }
// }
if (parseMarkdown) {
final html = markdown(
event['body'],
getEmotePacks: () => getImagePacksFlat(ImagePackUsage.emoticon),
getMention: getMention,
);
// if the decoded html is the same as the body, there is no need in sending a formatted message
if (HtmlUnescape().convert(html.replaceAll(RegExp(r'<br />\n?'), '\n')) !=
event['body']) {
event['format'] = 'org.matrix.custom.html';
event['formatted_body'] = html;
}
}
return sendEvent(
event,
txid: txid,

@ -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,
);
static TextStyle hasDefinitionStyle(TextStyle? existingStyle) =>
existingStyle?.merge(_hasDefinitionStyle) ?? _hasDefinitionStyle;
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,
),
);
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,73 +263,77 @@ 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 TextStyle tokenStyle = topTokenMatch != null
? topTokenMatch.textStyle(defaultStyle)
: hasDefinitionStyle(defaultStyle);
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,
);
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: matchText,
style: matchTokens[tokenIndex].match?.textStyle(defaultStyle) ??
defaultStyle,
recognizer: handleClick && cardToShow != null
? (TapGestureRecognizer()
..onTapDown = (details) => OverlayUtil.showPositionedCard(
context: context,
cardToShow: cardToShow,
cardSize:
matchTokens[tokenIndex].match?.isITStart ?? false
? const Size(350, 220)
: const Size(350, 400),
transformTargetId: transformTargetId,
))
: null,
),
);
final String beforeNextToken = originalInput.substring(
matchTokens[nextTokenIndex - 1].token.end,
nextTokenIndex < matchTokens.length
? matchTokens[nextTokenIndex].token.text.offset
: originalInput.length,
);
if (beforeNextToken.isNotEmpty) {
items.add(
TextSpan(
text: token.text.content,
style: tokenStyle,
recognizer: handleClick
? (TapGestureRecognizer()
..onTapDown = (details) => OverlayUtil.showPositionedCard(
context: context,
cardToShow: cardToShow,
cardSize: topTokenMatch?.isITStart ?? false
? const Size(350, 220)
: const Size(350, 400),
transformTargetId: transformTargetId,
))
: null,
text: beforeNextToken,
style: defaultStyle,
),
);
}
final int charBetween = getAfterTokenSpacingByIndex(
index,
);
if (charBetween > 0) {
items.add(
TextSpan(
text: " " * charBetween,
style: topTokenMatch != null &&
token.text.offset + token.text.length + charBetween <=
topTokenMatch.match.offset +
topTokenMatch.match.length
? tokenStyle
: 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,4 +1,5 @@
import 'package:collection/collection.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/constants/model_keys.dart';
import 'package:fluffychat/pangea/constants/pangea_message_types.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
@ -272,6 +273,10 @@ class PangeaMessageEvent {
//each match is turned into an activity that other students can access
//they're not told the answer but have to find it themselves
//the message has a blank piece which they fill in themselves
// replication of logic from message_content.dart
bool get isHtml =>
AppConfig.renderHtml && !_event.redacted && _event.isRichMessage;
}
class URLFinder {

@ -7,6 +7,7 @@ import 'package:fluffychat/pangea/repo/tokens_repo.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix/src/utils/markdown.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import '../../widgets/matrix.dart';
@ -161,4 +162,8 @@ class RepresentationEvent {
return _choreo;
}
String? formatBody() {
return markdown(content.text);
}
}

@ -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,5 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../../config/app_config.dart';
@ -96,7 +95,6 @@ class InstructionsController {
enum InstructionsEnum {
itInstructions,
clickMessage,
understandingMessages,
blurMeansTranslate,
}
@ -107,8 +105,6 @@ extension Copy on InstructionsEnum {
return L10n.of(context)!.itInstructionsTitle;
case InstructionsEnum.clickMessage:
return L10n.of(context)!.clickMessageTitle;
case InstructionsEnum.understandingMessages:
return L10n.of(context)!.understandingMessagesTitle;
case InstructionsEnum.blurMeansTranslate:
return L10n.of(context)!.blurMeansTranslateTitle;
}
@ -120,8 +116,6 @@ extension Copy on InstructionsEnum {
return L10n.of(context)!.itInstructionsBody;
case InstructionsEnum.clickMessage:
return L10n.of(context)!.clickMessageBody;
case InstructionsEnum.understandingMessages:
return L10n.of(context)!.understandingMessagesBody;
case InstructionsEnum.blurMeansTranslate:
return L10n.of(context)!.blurMeansTranslateBody;
}

@ -1,64 +1,101 @@
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(
borderOnForeground: false,
color: Colors.transparent,
clipBehavior: Clip.antiAlias,
child: OverlayContainer(
cardToShow: cardToShow,
borderColor: borderColor,
),
),
offset: offset ?? Offset.zero,
child: child,
),
),
],
),
);
Overlay.of(layerLinkAndKey.key.currentContext!)
.insert(MatrixState.pAnyState.overlay!);
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,
child: OverlayContainer(
cardToShow: cardToShow,
borderColor: borderColor,
),
);
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,159 @@
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/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';
class ShowDefintionUtil {
String messageText;
final String langCode;
final String targetId;
final FocusNode focusNode = FocusNode();
final Room room;
String? textSelection;
bool inCooldown = false;
double? dx;
double? dy;
ShowDefintionUtil({
required this.targetId,
required this.room,
required this.langCode,
required this.messageText,
});
void onTextSelection({
required BuildContext context,
TextSelection? selectedText,
SelectedContent? selectedContent,
SelectionChangedCause? cause,
}) {
if ((selectedText == null && selectedContent == null) ||
selectedText?.isCollapsed == true) {
clearTextSelection();
return;
}
textSelection = selectedText != null
? selectedText.textInside(messageText)
: selectedContent!.plainText;
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) {
if (textSelection == null) return;
OverlayUtil.showPositionedCard(
context: context,
cardToShow: WordDataCard(
word: textSelection!,
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 RenderObject? targetRenderBox =
layerLinkAndKey.key.currentContext!.findRenderObject();
final Offset transformTargetOffset =
(targetRenderBox as RenderBox).localToGlobal(Offset.zero);
if (dx != null && dx! > MediaQuery.of(context).size.width - 130) {
dx = MediaQuery.of(context).size.width - 130;
}
final double xOffset = dx != null ? dx! - transformTargetOffset.dx : 0;
final double yOffset =
dy != null ? dy! - transformTargetOffset.dy + 10 : 10;
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: Offset(xOffset, yOffset),
);
}
void onMouseRegionUpdate(PointerEvent event) {
dx = event.position.dx;
dy = event.position.dy;
}
Widget contextMenuOverride({
required BuildContext context,
EditableTextState? textSelection,
SelectableRegionState? contentSelection,
}) {
if (textSelection == null && contentSelection == null) {
return const SizedBox();
}
return AdaptiveTextSelectionToolbar.buttonItems(
anchors: textSelection?.contextMenuAnchors ??
contentSelection!.contextMenuAnchors,
buttonItems: [
if (textSelection != null) ...textSelection.contextMenuButtonItems,
if (contentSelection != null)
...contentSelection.contextMenuButtonItems,
ContextMenuButtonItem(
label: L10n.of(context)!.showDefinition,
onPressed: () {
showDefinition(context);
focusNode.unfocus();
},
),
],
);
}
}

@ -1,34 +1,33 @@
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/pages/chat/events/html_message.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/constants/language_keys.dart';
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 '../../models/igc_text_data_model.dart';
import '../../models/language_detection_model.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sentry_flutter/sentry_flutter.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 {
final PangeaMessageEvent pangeaMessageEvent;
final TextStyle? existingStyle;
final TextStyle? style;
final bool selected;
final LanguageModel? selectedDisplayLang;
final bool immersionMode;
final bool definitions;
final Choreographer? choreographer;
final ShowDefintionUtil? messageToolbar;
const PangeaRichText({
super.key,
@ -38,7 +37,8 @@ class PangeaRichText extends StatefulWidget {
required this.immersionMode,
required this.definitions,
this.choreographer,
this.existingStyle,
this.style,
this.messageToolbar,
});
@override
@ -48,20 +48,26 @@ class PangeaRichText extends StatefulWidget {
class PangeaRichTextState extends State<PangeaRichText> {
final PangeaController pangeaController = MatrixState.pangeaController;
bool _fetchingRepresentation = false;
bool _fetchingTokens = false;
double get blur => _fetchingRepresentation && widget.immersionMode ? 5 : 0;
List<TextSpan> textSpan = [];
String textSpan = "";
@override
void initState() {
super.initState();
setState(() => textSpan = getTextSpan(context));
updateTextSpan();
}
@override
void didUpdateWidget(PangeaRichText oldWidget) {
super.didUpdateWidget(oldWidget);
setState(() => textSpan = getTextSpan(context));
updateTextSpan();
}
void updateTextSpan() {
setState(() {
textSpan = getTextSpan(context);
widget.messageToolbar?.messageText = textSpan;
});
}
@override
@ -85,28 +91,48 @@ class PangeaRichTextState extends State<PangeaRichText> {
);
}
final Widget richText = RichText(
text: TextSpan(
children: [
...textSpan,
if (widget.selected && (_fetchingRepresentation || _fetchingTokens))
// if (widget.selected)
const WidgetSpan(
child: Padding(
padding: EdgeInsets.only(left: 5.0),
child: SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: AppConfig.secondaryColor,
final Widget richText = widget.pangeaMessageEvent.isHtml
? HtmlMessage(
html: textSpan,
room: widget.pangeaMessageEvent.room,
textColor: widget.style?.color ?? Colors.black,
messageToolbar: widget.messageToolbar,
)
: SelectableText.rich(
onSelectionChanged: (selection, cause) =>
widget.messageToolbar?.onTextSelection(
selectedText: selection,
cause: cause,
context: context,
),
focusNode: widget.messageToolbar?.focusNode,
contextMenuBuilder: (context, state) =>
widget.messageToolbar?.contextMenuOverride(
context: context,
textSelection: state,
) ??
const SizedBox(),
TextSpan(
text: textSpan,
style: widget.style,
children: [
if (widget.selected && (_fetchingRepresentation))
const WidgetSpan(
child: Padding(
padding: EdgeInsets.only(left: 5.0),
child: SizedBox(
height: 14,
width: 14,
child: CircularProgressIndicator(
strokeWidth: 2.0,
color: AppConfig.secondaryColor,
),
),
),
),
),
),
],
),
],
),
);
);
return blur > 0
? ImageFiltered(
@ -116,17 +142,17 @@ class PangeaRichTextState extends State<PangeaRichText> {
: richText;
}
List<TextSpan> getTextSpan(BuildContext context) {
String getTextSpan(BuildContext context) {
final String? displayLangCode =
widget.selected ? widget.selectedDisplayLang?.langCode : userL2LangCode;
if (displayLangCode == null || !widget.immersionMode) {
return simpleText(widget.pangeaMessageEvent.body);
return widget.pangeaMessageEvent.body;
}
if (widget.pangeaMessageEvent.eventId.contains("webdebug")) {
debugger(when: kDebugMode);
return simpleText(widget.pangeaMessageEvent.body);
return widget.pangeaMessageEvent.body;
}
final RepresentationEvent? repEvent =
@ -145,7 +171,7 @@ class PangeaRichTextState extends State<PangeaRichText> {
)
.onError((error, stackTrace) => ErrorHandler.logError())
.whenComplete(() => setState(() => _fetchingRepresentation = false));
return simpleText(widget.pangeaMessageEvent.body);
return widget.pangeaMessageEvent.body;
}
if (repEvent.event?.eventId.contains("web") ?? false) {
@ -158,75 +184,13 @@ class PangeaRichTextState extends State<PangeaRichText> {
"representationByLanguageGlobal returned RepEvent with event ID containing 'web' - ${repEvent.event?.eventId}",
),
);
// debugger(when: kDebugMode);
return textWithBotStyle(repEvent, context);
}
if (!widget.selected ||
displayLangCode != userL2LangCode ||
!widget.definitions) {
return textWithBotStyle(repEvent, context);
}
if (repEvent.tokens == null) {
setState(() => _fetchingTokens = true);
repEvent
.tokensGlobal(context)
.onError((error, stackTrace) => ErrorHandler.logError())
.whenComplete(() => setState(() => _fetchingTokens = false));
return textWithBotStyle(repEvent, context);
}
return IGCTextData(
originalInput: repEvent.text,
fullTextCorrection: repEvent.text,
matches: [],
detections: [LanguageDetection(langCode: displayLangCode)],
tokens: repEvent.tokens!,
enableIT: true,
enableIGC: true,
userL2: userL2LangCode ?? LanguageKeys.unknownLanguage,
userL1: userL1LangCode ?? LanguageKeys.unknownLanguage,
).constructTokenSpan(
context: context,
defaultStyle: textStyle(repEvent, context),
handleClick: true,
spanCardModel: null,
showTokens: widget.definitions,
transformTargetId: widget.pangeaMessageEvent.eventId,
room: widget.pangeaMessageEvent.room,
);
return widget.pangeaMessageEvent.isHtml
? repEvent.formatBody() ?? repEvent.text
: repEvent.text;
}
List<TextSpan> simpleText(String text) => [
TextSpan(
text: text,
style: widget.existingStyle,
),
];
List<TextSpan> textWithBotStyle(
RepresentationEvent repEvent,
BuildContext context,
) =>
[
TextSpan(
text: repEvent.text,
style: textStyle(repEvent, context),
),
];
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;

@ -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,27 +77,19 @@ 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,
);
OverlayUtil.showPositionedCard(
context: context,
cardSize: matchIndex != -1 &&
choreographer.igc.igcTextData!.matches[matchIndex].isITStart
? const Size(350, 220)
: const Size(350, 400),
cardToShow: cardToShow,
transformTargetId: choreographer.inputTransformTargetKey,
);
: null;
if (cardToShow != null) {
OverlayUtil.showPositionedCard(
context: context,
cardSize: matchIndex != -1 &&
choreographer.igc.igcTextData!.matches[matchIndex].isITStart
? const Size(350, 220)
: const Size(350, 400),
cardToShow: cardToShow,
transformTargetId: choreographer.inputTransformTargetKey,
);
}
}
@override
@ -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"
]
}

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

Loading…
Cancel
Save