Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RTL support (Bidi) #1239

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion lib/pages/chat/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import 'package:fluffychat/utils/error_reporter.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/event_extension.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/text_direction.dart';
import 'package:fluffychat/widgets/app_lock.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../utils/account_bundles.dart';
Expand Down Expand Up @@ -1058,7 +1059,8 @@ class ChatController extends State<ChatPageWithRoom>
message: room
.getState(EventTypes.RoomTombstone)!
.parsedTombstoneContent
.body,
.body
.bidiFormatted,
okLabel: L10n.of(context)!.ok,
cancelLabel: L10n.of(context)!.cancel,
)) {
Expand Down
3 changes: 2 additions & 1 deletion lib/pages/chat/chat_app_bar_list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_linkify/flutter_linkify.dart';

import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/text_direction.dart';
import 'package:fluffychat/utils/url_launcher.dart';

class ChatAppBarListTile extends StatelessWidget {
Expand Down Expand Up @@ -33,7 +34,7 @@ class ChatAppBarListTile extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: Linkify(
text: title,
text: title.bidiFormatted,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now every time I want to add a text somewhere, I need to think about adding the .bidiFormatted suffix? I would prefer a solution, which does not add more complexity

options: const LinkifyOptions(humanize: false),
maxLines: 1,
overflow: TextOverflow.ellipsis,
Expand Down
162 changes: 84 additions & 78 deletions lib/pages/chat/events/html_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:linkify/linkify.dart';
import 'package:matrix/matrix.dart';

import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/text_direction.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import '../../../utils/url_launcher.dart';
Expand Down Expand Up @@ -73,87 +74,92 @@ class HtmlMessage extends StatelessWidget {
padding: HtmlPaddings.only(left: 6, bottom: 0),
);

final direction = html.textDirectionHtml ?? Directionality.of(context);

final element = _linkifyHtml(HtmlParser.parseHTML(html));

// 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),
),
'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, fontSize, linkColor),
CodeExtension(fontSize: fontSize),
MatrixMathExtension(
style: TextStyle(fontSize: fontSize, color: textColor),
),
const TableHtmlExtension(),
SpoilerExtension(textColor: textColor),
const ImageExtension(),
FontColorExtension(),
FallbackTextExtension(fontSize: fontSize),
],
onLinkTap: (url, _, element) => UrlLauncher(
context,
url,
element?.text,
).launchUrl(),
onlyRenderTheseTags: const {
...allowedHtmlTags,
// Needed to make it work properly
'body',
'html',
},
shrinkWrap: true,
return Directionality(
textDirection: direction,
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, fontSize, linkColor),
CodeExtension(fontSize: fontSize),
MatrixMathExtension(
style: TextStyle(fontSize: fontSize, color: textColor),
),
const TableHtmlExtension(),
SpoilerExtension(textColor: textColor),
const ImageExtension(),
FontColorExtension(),
FallbackTextExtension(fontSize: fontSize),
],
onLinkTap: (url, _, element) => UrlLauncher(
context,
url,
element?.text,
).launchUrl(),
onlyRenderTheseTags: const {
...allowedHtmlTags,
// Needed to make it work properly
'body',
'html',
},
shrinkWrap: true,
),
);
}

Expand Down
19 changes: 12 additions & 7 deletions lib/pages/chat/events/message_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:fluffychat/pages/chat/events/video_player.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/utils/text_direction.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../../../config/app_config.dart';
Expand Down Expand Up @@ -86,9 +87,11 @@ class MessageContent extends StatelessWidget {
),
const Divider(),
Text(
event.calcLocalizedBodyFallback(
MatrixLocals(l10n),
),
event
.calcLocalizedBodyFallback(
MatrixLocals(l10n),
)
.bidiFormatted,
),
],
),
Expand Down Expand Up @@ -252,10 +255,12 @@ class MessageContent extends StatelessWidget {
event.numberEmotes > 0 &&
event.numberEmotes <= 10;
return Linkify(
text: event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
),
text: event
.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
hideReply: true,
)
.bidiFormatted,
style: TextStyle(
color: textColor,
fontSize: bigEmotes ? fontSize * 3 : fontSize,
Expand Down
13 changes: 8 additions & 5 deletions lib/pages/chat/events/reply_content.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';

import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/text_direction.dart';
import '../../../config/app_config.dart';

class ReplyContent extends StatelessWidget {
Expand Down Expand Up @@ -72,11 +73,13 @@ class ReplyContent extends StatelessWidget {
},
),
Text(
displayEvent.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
withSenderNamePrefix: false,
hideReply: true,
),
displayEvent
.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
withSenderNamePrefix: false,
hideReply: true,
)
.bidiFormatted,
overflow: TextOverflow.ellipsis,
maxLines: 1,
style: TextStyle(
Expand Down
9 changes: 6 additions & 3 deletions lib/pages/chat/events/state_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart';

import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:fluffychat/utils/text_direction.dart';
import '../../../config/app_config.dart';

class StateMessage extends StatelessWidget {
Expand All @@ -22,9 +23,11 @@ class StateMessage extends StatelessWidget {
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
),
child: Text(
event.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
),
event
.calcLocalizedBodyFallback(
MatrixLocals(L10n.of(context)!),
)
.bidiFormatted,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12 * AppConfig.fontSizeFactor,
Expand Down
76 changes: 40 additions & 36 deletions lib/pages/chat/input_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:slugify/slugify.dart';

import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/text_direction.dart';
import 'package:fluffychat/widgets/mxc_image.dart';
import '../../widgets/avatar.dart';
import '../../widgets/matrix.dart';
Expand Down Expand Up @@ -452,45 +453,48 @@ class InputBar extends StatelessWidget {
hideOnSelect: false,
debounceDuration: const Duration(milliseconds: 50),
// show suggestions after 50ms idle time (default is 300)
builder: (context, controller, focusNode) => TextField(
controller: controller,
focusNode: focusNode,
contentInsertionConfiguration: ContentInsertionConfiguration(
onContentInserted: (KeyboardInsertedContent content) {
final data = content.data;
if (data == null) return;
builder: (context, controller, focusNode) => AutoDirection(
text: controller.text,
child: TextField(
controller: controller,
focusNode: focusNode,
contentInsertionConfiguration: ContentInsertionConfiguration(
onContentInserted: (KeyboardInsertedContent content) {
final data = content.data;
if (data == null) return;

final file = MatrixFile(
mimeType: content.mimeType,
bytes: data,
name: content.uri.split('/').last,
);
room.sendFileEvent(
file,
shrinkImageMaxDimension: 1600,
);
final file = MatrixFile(
mimeType: content.mimeType,
bytes: data,
name: content.uri.split('/').last,
);
room.sendFileEvent(
file,
shrinkImageMaxDimension: 1600,
);
},
),
minLines: minLines,
maxLines: maxLines,
keyboardType: keyboardType!,
textInputAction: textInputAction,
autofocus: autofocus!,
inputFormatters: [
LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
],
onSubmitted: (text) {
// fix for library for now
// it sets the types for the callback incorrectly
onSubmitted!(text);
},
decoration: decoration!,
onChanged: (text) {
// fix for the library for now
// it sets the types for the callback incorrectly
onChanged!(text);
},
textCapitalization: TextCapitalization.sentences,
),
minLines: minLines,
maxLines: maxLines,
keyboardType: keyboardType!,
textInputAction: textInputAction,
autofocus: autofocus!,
inputFormatters: [
LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()),
],
onSubmitted: (text) {
// fix for library for now
// it sets the types for the callback incorrectly
onSubmitted!(text);
},
decoration: decoration!,
onChanged: (text) {
// fix for the library for now
// it sets the types for the callback incorrectly
onChanged!(text);
},
textCapitalization: TextCapitalization.sentences,
),
suggestionsCallback: getSuggestions,
itemBuilder: (c, s) =>
Expand Down
Loading