diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart
index af380fc27..ddac6cbd1 100644
--- a/lib/pages/chat/events/html_message.dart
+++ b/lib/pages/chat/events/html_message.dart
@@ -231,7 +231,64 @@ class HtmlMessage extends StatelessWidget {
]);
}
- return result.join();
+ if (pangeaMessageEvent?.textDirection == TextDirection.rtl) {
+ for (int i = 0; i < result.length; i++) {
+ final tag = result[i];
+ if (blockHtmlTags.contains(tag.htmlTagName) ||
+ fullLineHtmlTag.contains(tag.htmlTagName)) {
+ if (i > 0 && result[i - 1] == ", ") {
+ result[i - 1] = "";
+ }
+ result[i] = ", ";
+ }
+ }
+ result.removeWhere((element) => element == "");
+ if (result[0] == ", ") result[0] = "";
+ if (result.last == ", ") result.last = "";
+ final inverted = _invertTags(result);
+ return inverted.join().trim();
+ }
+ return result.join().trim();
+ }
+
+ List _invertTags(List tags) {
+ final List<(String, int)> stack = [];
+ final List<(int, int)> invertedTags = [];
+ for (int i = 0; i < tags.length; i++) {
+ final tag = tags[i];
+ if (!tag.contains('<') || tag.contains("
+ element.$1.htmlTagName == tag.htmlTagName &&
+ !element.$1.contains(""),
+ );
+ }
+
+ if (match != -1) {
+ // If the tag is already in the stack, we remove it
+ final (matchTag, matchIndex) = stack.removeAt(match);
+ invertedTags.add((matchIndex, i));
+ } else {
+ // If the tag is not in the stack, we add it
+ stack.insert(0, (tag, i));
+ }
+ }
+
+ for (final (start, end) in invertedTags) {
+ final startTag = tags[start];
+ final endTag = tags[end];
+
+ tags[start] = endTag;
+ tags[end] = startTag;
+ }
+
+ final inverted = tags.reversed.toList();
+ return inverted;
}
// Pangea#
@@ -381,6 +438,7 @@ class HtmlMessage extends StatelessWidget {
? () => onClick?.call(token)
: null,
child: RichText(
+ textDirection: pangeaMessageEvent?.textDirection,
text: TextSpan(
children: [
LinkifySpan(
@@ -1006,3 +1064,8 @@ extension on String {
extension on dom.Element {
dom.Element get rootElement => parent?.rootElement ?? this;
}
+
+extension on String {
+ String get htmlTagName =>
+ replaceAll('<', '').replaceAll('>', '').replaceAll('/', '').split(' ')[0];
+}
diff --git a/lib/pangea/events/event_wrappers/pangea_message_event.dart b/lib/pangea/events/event_wrappers/pangea_message_event.dart
index f9ef56e2f..9df0b2e0d 100644
--- a/lib/pangea/events/event_wrappers/pangea_message_event.dart
+++ b/lib/pangea/events/event_wrappers/pangea_message_event.dart
@@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:developer';
+import 'dart:ui';
import 'package:flutter/foundation.dart';
@@ -18,6 +19,7 @@ import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/events/models/representation_content_model.dart';
import 'package:fluffychat/pangea/events/models/stt_translation_model.dart';
import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart';
+import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
import 'package:fluffychat/pangea/spaces/models/space_model.dart';
@@ -791,4 +793,9 @@ class PangeaMessageEvent {
tag: tag,
);
}
+
+ TextDirection get textDirection =>
+ PLanguageStore.rtlLanguageCodes.contains(messageDisplayLangCode)
+ ? TextDirection.rtl
+ : TextDirection.ltr;
}
diff --git a/lib/pangea/learning_settings/models/language_model.dart b/lib/pangea/learning_settings/models/language_model.dart
index 2c840475e..285577bfc 100644
--- a/lib/pangea/learning_settings/models/language_model.dart
+++ b/lib/pangea/learning_settings/models/language_model.dart
@@ -1,20 +1,25 @@
import 'package:flutter/material.dart';
+import 'package:collection/collection.dart';
+
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
import 'package:fluffychat/pangea/learning_settings/enums/l2_support_enum.dart';
+import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
class LanguageModel {
final String langCode;
final String displayName;
final String script;
final L2SupportEnum l2Support;
+ final TextDirection? _textDirection;
LanguageModel({
required this.langCode,
required this.displayName,
this.script = LanguageKeys.unknownLanguage,
this.l2Support = L2SupportEnum.na,
- });
+ TextDirection? textDirection,
+ }) : _textDirection = textDirection;
factory LanguageModel.fromJson(json) {
final String code = json['language_code'] ??
@@ -30,6 +35,11 @@ class LanguageModel {
? L2SupportEnum.na.fromStorageString(json['l2_support'])
: L2SupportEnum.na,
script: json['script'] ?? LanguageKeys.unknownLanguage,
+ textDirection: json['text_direction'] != null
+ ? TextDirection.values.firstWhereOrNull(
+ (e) => e.name == json['text_direction'],
+ )
+ : null,
);
}
@@ -38,6 +48,7 @@ class LanguageModel {
'language_name': displayName,
'script': script,
'l2_support': l2Support.storageString,
+ 'text_direction': textDirection.name,
};
bool get l2 => l2Support != L2SupportEnum.na;
@@ -65,6 +76,16 @@ class LanguageModel {
String get langCodeShort => langCode.split('-').first;
+ TextDirection get _defaultTextDirection {
+ return PLanguageStore.rtlLanguageCodes.contains(langCodeShort)
+ ? TextDirection.rtl
+ : TextDirection.ltr;
+ }
+
+ TextDirection get textDirection {
+ return _textDirection ?? _defaultTextDirection;
+ }
+
@override
bool operator ==(Object other) {
if (other is LanguageModel) {
diff --git a/lib/pangea/learning_settings/utils/p_language_store.dart b/lib/pangea/learning_settings/utils/p_language_store.dart
index 3d4a7ec54..60310f18b 100644
--- a/lib/pangea/learning_settings/utils/p_language_store.dart
+++ b/lib/pangea/learning_settings/utils/p_language_store.dart
@@ -106,4 +106,19 @@ class PLanguageStore {
}
return null;
}
+
+ static final List rtlLanguageCodes = [
+ 'ar',
+ 'arc',
+ 'dv',
+ 'fa',
+ 'ha',
+ 'he',
+ 'khw',
+ 'ks',
+ 'ku',
+ 'ps',
+ 'ur',
+ 'yi',
+ ];
}