remove unused files

This commit is contained in:
ggurdin 2025-06-10 11:50:07 -04:00
parent 51490605fb
commit 524b8978f5
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
4 changed files with 10 additions and 633 deletions

View file

@ -166,28 +166,23 @@ class HtmlMessage extends StatelessWidget {
(token) => token.text.offset == offset && token.text.length == length,
);
/// Wrap token spans in token tags so styling / functions can be applied
dom.Node _tokenizeHtml(
dom.Node element,
String fullHtml,
List<PangeaToken> remainingTokens,
) {
String _addTokenTags() {
final regex = RegExp(r'(<[^>]+>)');
final matches = regex.allMatches(fullHtml);
final matches = regex.allMatches(html);
final List<String> result = <String>[];
int lastEnd = 0;
for (final match in matches) {
if (match.start > lastEnd) {
result.add(fullHtml.substring(lastEnd, match.start)); // Text before tag
result.add(html.substring(lastEnd, match.start)); // Text before tag
}
result.add(match.group(0)!); // The tag itself
lastEnd = match.end;
}
if (lastEnd < fullHtml.length) {
result.add(fullHtml.substring(lastEnd)); // Remaining text after last tag
if (lastEnd < html.length) {
result.add(html.substring(lastEnd)); // Remaining text after last tag
}
for (final PangeaToken token in tokens ?? []) {
@ -217,7 +212,7 @@ class HtmlMessage extends StatelessWidget {
]);
}
return dom.Element.html('<span>${result.join()}</span>');
return result.join();
}
// Pangea#
@ -834,10 +829,7 @@ class HtmlMessage extends StatelessWidget {
// maxLines: limitHeight ? 64 : null,Add commentMore actions
// overflow: TextOverflow.fade,
// );
dom.Node parsed = parser.parse(html).body ?? dom.Element.html('');
if (tokens != null) {
parsed = _tokenizeHtml(parsed, html, List.from(tokens!));
}
final parsed = parser.parse(_addTokenTags()).body ?? dom.Element.html('');
return SelectionArea(
child: GestureDetector(
onTap: () {

View file

@ -6,8 +6,6 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_selection_repo.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_token_text.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../../utils/matrix_sdk_extensions/matrix_locals.dart';
@ -88,26 +86,9 @@ class ChatListItemSubtitle extends StatelessWidget {
if (snapshot.hasData) {
final messageEventAndTokens = snapshot.data as MessageEventAndTokens;
final pangeaMessageEvent = messageEventAndTokens.event;
final tokens = messageEventAndTokens.tokens;
final analyticsEntry = tokens != null
? PracticeSelectionRepo.get(
messageEventAndTokens.event.messageDisplayLangCode,
tokens,
)
: null;
return MessageTextWidget(
pangeaMessageEvent: pangeaMessageEvent,
existingStyle: style,
messageAnalyticsEntry: analyticsEntry,
isSelected: null,
onClick: null,
softWrap: false,
maxLines: pangeaMessageEvent.room.notificationCount >= 1 ? 2 : 1,
overflow: TextOverflow.ellipsis,
isMessage: false,
textScaler: MediaQuery.textScalerOf(context),
return Text(
pangeaMessageEvent.messageDisplayText,
style: style,
);
}

View file

@ -1,177 +0,0 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_selection.dart';
class TokenPosition {
/// Start index of the full substring in the message
final int start;
/// End index of the full substring in the message
final int end;
/// Start index of the token in the message
final int tokenStart;
/// End index of the token in the message
final int tokenEnd;
final bool hideContent;
final PangeaToken? token;
final bool isHighlighted;
final bool selected;
const TokenPosition({
required this.start,
required this.end,
required this.tokenStart,
required this.tokenEnd,
required this.hideContent,
required this.selected,
required this.isHighlighted,
this.token,
});
}
class MessageTextUtil {
static final Map<String, List<TokenPosition>> _tokenPositionsCache = {};
static List<TokenPosition>? getTokenPositions(
PangeaMessageEvent pangeaMessageEvent, {
PracticeSelection? messageAnalyticsEntry,
bool Function(PangeaToken)? isSelected,
bool Function(PangeaToken)? isHighlighted,
}) {
try {
if (pangeaMessageEvent.messageDisplayRepresentation?.tokens == null) {
return null;
}
final cacheKey = pangeaMessageEvent.event
.getDisplayEvent(pangeaMessageEvent.timeline)
.eventId;
if (_tokenPositionsCache.containsKey(cacheKey)) {
return _tokenPositionsCache[cacheKey]!
.map(
(t) => TokenPosition(
start: t.start,
end: t.end,
tokenStart: t.tokenStart,
tokenEnd: t.tokenEnd,
hideContent: t.hideContent,
selected: t.token != null
? isSelected?.call(t.token!) ?? false
: false,
isHighlighted: t.token != null
? isHighlighted?.call(t.token!) ?? false
: false,
token: t.token,
),
)
.toList();
}
// Convert the entire message into a list of characters
final Characters messageCharacters =
pangeaMessageEvent.messageDisplayText.characters;
// When building token positions, use grapheme cluster indices
final List<TokenPosition> tokenPositions = [];
int globalIndex = 0;
final tokens = pangeaMessageEvent.messageDisplayRepresentation!.tokens!;
int pointer = 0;
while (pointer < tokens.length) {
PangeaToken token = tokens[pointer];
final start = token.start;
final end = token.end;
// Calculate the number of grapheme clusters up to the start and end positions
final int startIndex = messageCharacters.take(start).length;
int endIndex = messageCharacters.take(end).length;
final hasHiddenContent =
messageAnalyticsEntry?.hasHiddenWordActivity ?? false;
// if this is white space, add position without token
if (globalIndex < startIndex) {
tokenPositions.add(
TokenPosition(
start: globalIndex,
end: startIndex,
tokenStart: globalIndex,
tokenEnd: startIndex,
hideContent: false,
selected: (isSelected?.call(token) ?? false) && !hasHiddenContent,
isHighlighted: isHighlighted?.call(token) ?? false,
),
);
}
// group tokens with punctuation before and after so punctuation doesn't cause newline
int nextTokenPointer = pointer + 1;
while (nextTokenPointer < tokens.length) {
final nextToken = tokens[nextTokenPointer];
if (token.pos == 'PUNCT' && token.end == nextToken.start) {
token = nextToken;
nextTokenPointer++;
endIndex = messageCharacters.take(nextToken.end).length;
continue;
}
break;
}
while (nextTokenPointer < tokens.length) {
final nextToken = tokens[nextTokenPointer];
if (nextToken.pos == 'PUNCT' && token.end == nextToken.start) {
nextTokenPointer++;
endIndex = messageCharacters.take(nextToken.end).length;
continue;
}
break;
}
final hideContent =
messageAnalyticsEntry?.isTokenInHiddenWordActivity(token) ?? false;
tokenPositions.add(
TokenPosition(
start: startIndex,
end: endIndex,
tokenStart: messageCharacters.take(token.start).length,
tokenEnd: messageCharacters.take(token.end).length,
token: token,
hideContent: hideContent,
selected: (isSelected?.call(token) ?? false) &&
!hideContent &&
!hasHiddenContent,
isHighlighted: isHighlighted?.call(token) ?? false,
),
);
globalIndex = endIndex;
pointer = nextTokenPointer;
continue;
}
_tokenPositionsCache[cacheKey] = tokenPositions;
return tokenPositions;
} catch (err, s) {
ErrorHandler.logError(
e: err,
s: s,
data: {
'pangeaMessageEvent': pangeaMessageEvent,
'messageAnalyticsEntry': messageAnalyticsEntry,
'isSelected': isSelected,
},
);
return null;
}
}
}

View file

@ -1,419 +0,0 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:fluffychat/pangea/common/utils/any_state_holder.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/events/utils/message_text_util.dart';
import 'package:fluffychat/pangea/message_token_text/message_token_button.dart';
import 'package:fluffychat/pangea/practice_activities/practice_selection.dart';
import 'package:fluffychat/pangea/practice_activities/practice_selection_repo.dart';
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/utils/token_rendering_util.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/matrix.dart';
/// Question - does this need to be stateful or does this work?
/// Need to test.
///
class MessageTokenText extends StatelessWidget {
final PangeaMessageEvent _pangeaMessageEvent;
final TextStyle _style;
final bool Function(PangeaToken)? _isSelected;
final void Function(PangeaToken)? _onClick;
final bool Function(PangeaToken)? _isHighlighted;
final MessageOverlayController? _overlayController;
final bool _isTransitionAnimation;
final ReadingAssistanceMode? readingAssistanceMode;
const MessageTokenText({
super.key,
required PangeaMessageEvent pangeaMessageEvent,
required List<PangeaToken>? tokens,
required TextStyle style,
required void Function(PangeaToken)? onClick,
bool Function(PangeaToken)? isSelected,
bool Function(PangeaToken)? isHighlighted,
MessageMode? messageMode,
MessageOverlayController? overlayController,
bool isTransitionAnimation = false,
this.readingAssistanceMode,
}) : _onClick = onClick,
_isSelected = isSelected,
_style = style,
_pangeaMessageEvent = pangeaMessageEvent,
_isHighlighted = isHighlighted,
_overlayController = overlayController,
_isTransitionAnimation = isTransitionAnimation;
List<PangeaToken>? get _tokens =>
_pangeaMessageEvent.messageDisplayRepresentation?.tokens;
PracticeSelection? get messageAnalyticsEntry => _tokens != null
? PracticeSelectionRepo.get(
_pangeaMessageEvent.messageDisplayLangCode,
_tokens!,
)
: null;
void callOnClick(TokenPosition tokenPosition) {
_onClick != null && tokenPosition.token != null
? _onClick!(tokenPosition.token!)
: null;
}
@override
Widget build(BuildContext context) {
if (_tokens == null) {
return Text(
_pangeaMessageEvent.messageDisplayText,
style: _style,
textScaler: TextScaler.noScaling,
);
}
return MessageTextWidget(
pangeaMessageEvent: _pangeaMessageEvent,
existingStyle: _style,
messageAnalyticsEntry: messageAnalyticsEntry,
isSelected: _isSelected,
onClick: callOnClick,
isHighlighted: _isHighlighted,
overlayController: _overlayController,
isTransitionAnimation: _isTransitionAnimation,
readingAssistanceMode: readingAssistanceMode,
);
}
}
class MessageTextWidget extends StatelessWidget {
final PangeaMessageEvent pangeaMessageEvent;
final TextStyle existingStyle;
final PracticeSelection? messageAnalyticsEntry;
final bool Function(PangeaToken)? isSelected;
final void Function(TokenPosition tokenPosition)? onClick;
final bool Function(PangeaToken)? isHighlighted;
final bool? softWrap;
final int? maxLines;
final TextOverflow? overflow;
final MessageOverlayController? overlayController;
final bool isTransitionAnimation;
final bool isMessage;
final ReadingAssistanceMode? readingAssistanceMode;
final TextScaler? textScaler;
const MessageTextWidget({
super.key,
required this.pangeaMessageEvent,
required this.existingStyle,
this.messageAnalyticsEntry,
this.isSelected,
this.onClick,
this.softWrap,
this.maxLines,
this.overflow,
this.isHighlighted,
this.overlayController,
this.isTransitionAnimation = false,
this.isMessage = true,
this.readingAssistanceMode,
this.textScaler,
});
@override
Widget build(BuildContext context) {
final renderer = TokenRenderingUtil(
pangeaMessageEvent: pangeaMessageEvent,
readingAssistanceMode: readingAssistanceMode,
existingStyle: existingStyle,
overlayController: overlayController,
isTransitionAnimation: isTransitionAnimation,
);
final Characters messageCharacters =
pangeaMessageEvent.messageDisplayText.characters;
final tokenPositions = MessageTextUtil.getTokenPositions(
pangeaMessageEvent,
messageAnalyticsEntry: messageAnalyticsEntry,
isSelected: isSelected,
isHighlighted: isHighlighted,
);
if (tokenPositions == null) {
return Text(
pangeaMessageEvent.messageDisplayText,
style: renderer.style(context),
softWrap: softWrap,
maxLines: maxLines,
overflow: overflow,
textScaler: textScaler,
);
}
final theme = Theme.of(context);
final ownMessage =
pangeaMessageEvent.senderId == Matrix.of(context).client.userID;
final linkColor = theme.brightness == Brightness.light
? theme.colorScheme.primary
: ownMessage
? theme.colorScheme.onPrimary
: theme.colorScheme.onSurface;
return RichText(
softWrap: softWrap ?? true,
maxLines: maxLines,
overflow: overflow ?? TextOverflow.clip,
textScaler: textScaler ?? TextScaler.noScaling,
text: TextSpan(
children:
tokenPositions.mapIndexed((int i, TokenPosition tokenPosition) {
final substring = messageCharacters
.skip(tokenPosition.start)
.take(tokenPosition.end - tokenPosition.start)
.toString();
if (tokenPosition.token?.pos == 'SPACE') {
return const TextSpan(text: '\n');
}
if (tokenPosition.token != null) {
// if the tokenPosition is a combination of the token and preceding / following punctuation
// split them so that only the token itself is highlighted when clicked
String start = '';
String middle = '';
String end = '';
final startSplitIndex =
tokenPosition.tokenStart - tokenPosition.start;
final endSplitIndex = tokenPosition.tokenEnd - tokenPosition.start;
start = substring.characters.take(startSplitIndex).toString();
end = substring.characters.skip(endSplitIndex).toString();
middle = substring.characters
.skip(startSplitIndex)
.take(endSplitIndex - startSplitIndex)
.toString();
final token = tokenPosition.token!;
final tokenWidth = renderer.tokenTextWidthForContainer(
context,
token.text.content,
);
return WidgetSpan(
child: CompositedTransformTarget(
link: renderer.assignTokenKey
? MatrixState.pAnyState
.layerLinkAndKey(token.text.uniqueKey)
.link
: LayerLinkAndKey(token.hashCode.toString()).link,
child: Column(
key: renderer.assignTokenKey
? MatrixState.pAnyState
.layerLinkAndKey(token.text.uniqueKey)
.key
: null,
children: [
if (renderer.showCenterStyling)
MessageTokenButton(
token: token,
overlayController: overlayController,
textStyle: renderer.style(context),
width: tokenWidth,
animateIn: isTransitionAnimation,
practiceTargetForToken: overlayController
?.toolbarMode.associatedActivityType !=
null
? overlayController?.practiceSelection
?.activities(
overlayController!
.toolbarMode.associatedActivityType!,
)
.firstWhereOrNull(
(a) => a.tokens.contains(token),
)
: null,
),
MouseRegion(
cursor: SystemMouseCursors.click,
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: onClick != null
? () => onClick?.call(tokenPosition)
: null,
child: RichText(
text: TextSpan(
children: [
if (start.isNotEmpty)
LinkifySpan(
text: start,
style: renderer.style(
context,
color: renderer.backgroundColor(
context,
tokenPosition.selected,
),
),
linkStyle: TextStyle(
decoration: TextDecoration.underline,
color: linkColor,
),
onOpen: (url) =>
UrlLauncher(context, url.url).launchUrl(),
),
// tokenPosition.hideContent
// ? WidgetSpan(
// alignment: PlaceholderAlignment.middle,
// child: GestureDetector(
// onTap: onClick != null
// ? () => onClick?.call(tokenPosition)
// : null,
// child: HiddenText(
// text: middle,
// style: style(context),
// ),
// ),
// )
// :
LinkifySpan(
text: middle,
style: renderer.style(
context,
color: renderer.backgroundColor(
context,
tokenPosition.selected,
),
),
linkStyle: TextStyle(
decoration: TextDecoration.underline,
color: linkColor,
),
onOpen: (url) =>
UrlLauncher(context, url.url).launchUrl(),
),
if (end.isNotEmpty)
LinkifySpan(
text: end,
style: renderer.style(
context,
color: renderer.backgroundColor(
context,
tokenPosition.selected,
),
),
linkStyle: TextStyle(
decoration: TextDecoration.underline,
color: linkColor,
),
onOpen: (url) =>
UrlLauncher(context, url.url).launchUrl(),
),
],
),
),
),
),
// AnimatedContainer(
// duration: const Duration(
// milliseconds: AppConfig.overlayAnimationDuration,
// ),
// height: overlayController != null && isTransitionAnimation
// ? 4
// : 0,
// width: tokenWidth,
// child: Container(
// color: backgroundColor(context, tokenPosition),
// ),
// ),
],
),
),
);
} else {
// if ((i > 0 || i < tokenPositions.length - 1) &&
// tokenPositions[i + 1].hideContent &&
// tokenPositions[i - 1].hideContent) {
// return WidgetSpan(
// child: GestureDetector(
// onTap: onClick != null
// ? () => onClick?.call(tokenPosition)
// : null,
// child: HiddenText(
// text: substring,
// style: style(context),
// ),
// ),
// );
// }
return LinkifySpan(
text: substring,
style: renderer.style(context),
options: const LinkifyOptions(humanize: false),
linkStyle: TextStyle(
decoration: TextDecoration.underline,
color: Theme.of(context).colorScheme.primary,
),
onOpen: (url) => UrlLauncher(context, url.url).launchUrl(),
);
}
}).toList(),
),
);
}
}
// class HiddenText extends StatelessWidget {
// final String text;
// final TextStyle style;
// const HiddenText({
// super.key,
// required this.text,
// required this.style,
// });
// @override
// Widget build(BuildContext context) {
// final TextPainter textPainter = TextPainter(
// text: TextSpan(text: text, style: style),
// textDirection: TextDirection.ltr,
// )..layout();
// final textWidth = textPainter.size.width;
// final textHeight = textPainter.size.height;
// textPainter.dispose();
// return SizedBox(
// height: textHeight,
// child: Stack(
// children: [
// Container(
// width: textWidth,
// height: textHeight,
// color: Colors.transparent,
// ),
// Positioned(
// bottom: 0,
// child: Container(
// width: textWidth,
// height: 1,
// color: style.color,
// ),
// ),
// ],
// ),
// );
// }
// }