inital work for selecting definitions
This commit is contained in:
parent
a9e5082bec
commit
0f3e1c7e16
14 changed files with 588 additions and 275 deletions
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
|
@ -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: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 '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 '../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 List<MatchToken> matchTokens = getMatchTokens();
|
||||
|
||||
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,
|
||||
);
|
||||
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;
|
||||
|
||||
final TextStyle tokenStyle = topTokenMatch != null
|
||||
? topTokenMatch.textStyle(defaultStyle)
|
||||
: hasDefinitionStyle(defaultStyle);
|
||||
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: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 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.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,17 +1,65 @@
|
|||
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 showOverlay({
|
||||
required BuildContext context,
|
||||
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 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: size.width,
|
||||
height: size.height,
|
||||
child: CompositedTransformFollower(
|
||||
link: layerLinkAndKey.link,
|
||||
showWhenUnlinked: false,
|
||||
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,
|
||||
|
|
@ -21,8 +69,6 @@ class OverlayUtil {
|
|||
Color? borderColor,
|
||||
}) {
|
||||
try {
|
||||
MatrixState.pAnyState.closeOverlay();
|
||||
|
||||
final LayerLinkAndKey layerLinkAndKey =
|
||||
MatrixState.pAnyState.layerLinkAndKey(transformTargetId);
|
||||
|
||||
|
|
@ -31,34 +77,25 @@ class OverlayUtil {
|
|||
transformTargetKey: layerLinkAndKey.key,
|
||||
);
|
||||
|
||||
MatrixState.pAnyState.overlay = OverlayEntry(
|
||||
builder: (context) => Stack(
|
||||
children: [
|
||||
if (backDropToDismiss) const TransparentBackdrop(),
|
||||
Positioned(
|
||||
width: cardSize.width,
|
||||
height: cardSize.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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
final Widget child = Material(
|
||||
borderOnForeground: false,
|
||||
color: Colors.transparent,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: OverlayContainer(
|
||||
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 {
|
||||
|
|
|
|||
130
lib/pangea/utils/show_defintion_util.dart
Normal file
130
lib/pangea/utils/show_defintion_util.dart
Normal file
|
|
@ -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,29 +95,51 @@ 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 TextSpan richTextSpan = TextSpan(
|
||||
children: [
|
||||
...textSpan,
|
||||
if (widget.selected && (_fetchingRepresentation || _fetchingTokens))
|
||||
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.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(
|
||||
imageFilter: ImageFilter.blur(sigmaX: blur, sigmaY: blur),
|
||||
|
|
@ -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,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,
|
||||
);
|
||||
: 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,
|
||||
);
|
||||
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"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ flutter:
|
|||
generate: true
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- .env
|
||||
- assets/
|
||||
# #Pangea
|
||||
- assets/pangea/
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue