Merge pull request 'Prevent Layout Errors Caused By Pathological Unicode' (#631) from unicode into trunk
continuous-integration/drone/push Build is pending Details

Reviewed-on: #631
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
This commit is contained in:
Sarah Jamie Lewis 2023-02-17 00:06:01 +00:00
commit 107281e20a
20 changed files with 190 additions and 157 deletions

View File

@ -138,17 +138,6 @@ steps:
- cd ..
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@build.openprivacy.ca:/home/buildfiles/buildfiles/
- name: notify-email
image: drillster/drone-email
pull: if-not-exists
settings:
host: build.openprivacy.ca
port: 25
skip_verify: true
from: drone@openprivacy.ca
when:
status: [ failure ]
- name: notify-gogs
image: openpriv/drone-gogs
pull: if-not-exists

View File

@ -159,7 +159,6 @@ class CwtchFfi implements Cwtch {
cwtchDir = envVars['CWTCH_HOME'] ?? path.join(envVars['HOME']!, ".cwtch");
}
if (await File("linux/Tor/tor").exists()) {
bundledTor = "linux/Tor/tor";
} else if (await File("lib/Tor/tor").exists()) {
@ -788,7 +787,6 @@ class CwtchFfi implements Cwtch {
@override
String? defaultDownloadPath() {
Map<String, String> envVars = Platform.environment;
String nominalPath = path.join(envVars[Platform.isWindows ? 'UserProfile' : 'HOME']!, "Downloads");
if (Directory(nominalPath).existsSync() == false) {

View File

@ -71,7 +71,6 @@ class FlwtchState extends State<Flwtch> with WindowListener {
@override
initState() {
globalSettings = Settings(Locale("en", ''), CwtchDark());
globalErrorHandler = ErrorHandler();
globalTorStatus = TorStatus();
@ -204,7 +203,8 @@ class FlwtchState extends State<Flwtch> with WindowListener {
print("Exiting...");
exit(0);
}
};
}
;
}
// Invoked via notificationClickChannel by MyBroadcastReceiver in MainActivity.kt

View File

@ -69,5 +69,4 @@ class ProfileServerListState extends ChangeNotifier {
}
List<RemoteServerInfoState> get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
}

View File

@ -68,7 +68,6 @@ class ServerListState extends ChangeNotifier {
}
List<ServerInfoState> get servers => _servers.sublist(0); //todo: copy?? dont want caller able to bypass changenotifier
}
class ServerInfoState extends ChangeNotifier {

View File

@ -154,7 +154,6 @@ class NixNotificationManager implements NotificationsManager {
}
NotificationsManager newDesktopNotificationsManager(Future<void> Function(String profileOnion, int convoId) notificationSelectConvo) {
// We don't want notifications in Dev Mode
if (EnvironmentConfig.TEST_MODE) {
return NullNotificationsManager();

View File

@ -294,47 +294,50 @@ class SelectableLinkify extends StatelessWidget {
linkifiers: linkifiers,
);
return SelectableText.rich(
buildTextSpan(
elements,
style: Theme.of(context).textTheme.bodyText2?.merge(style),
codeStyle: Theme.of(context).textTheme.bodyText2?.merge(codeStyle),
onOpen: onOpen,
context: context,
linkStyle: Theme.of(context)
.textTheme
.bodyText2
?.merge(style)
.copyWith(
color: Colors.blueAccent,
decoration: TextDecoration.underline,
)
.merge(linkStyle),
),
textAlign: textAlign,
textDirection: textDirection,
minLines: minLines,
maxLines: maxLines,
focusNode: focusNode,
strutStyle: strutStyle,
showCursor: showCursor,
textScaleFactor: textScaleFactor,
autofocus: autofocus,
toolbarOptions: toolbarOptions,
cursorWidth: cursorWidth,
cursorRadius: cursorRadius,
cursorColor: cursorColor,
dragStartBehavior: dragStartBehavior,
enableInteractiveSelection: enableInteractiveSelection,
onTap: onTap,
scrollPhysics: scrollPhysics,
textWidthBasis: textWidthBasis,
textHeightBehavior: textHeightBehavior,
cursorHeight: cursorHeight,
selectionControls: selectionControls,
onSelectionChanged: onSelectionChanged,
style: style,
);
return Container(
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: SelectableText.rich(
buildTextSpan(
elements,
style: Theme.of(context).textTheme.bodyText2?.merge(style),
codeStyle: Theme.of(context).textTheme.bodyText2?.merge(codeStyle),
onOpen: onOpen,
context: context,
linkStyle: Theme.of(context)
.textTheme
.bodyText2
?.merge(style)
.copyWith(
color: Colors.blueAccent,
decoration: TextDecoration.underline,
)
.merge(linkStyle),
),
textAlign: textAlign,
textDirection: textDirection,
minLines: minLines,
maxLines: maxLines,
focusNode: focusNode,
strutStyle: strutStyle,
showCursor: showCursor,
textScaleFactor: textScaleFactor,
autofocus: autofocus,
toolbarOptions: toolbarOptions,
cursorWidth: cursorWidth,
cursorRadius: cursorRadius,
cursorColor: cursorColor,
dragStartBehavior: dragStartBehavior,
enableInteractiveSelection: enableInteractiveSelection,
onTap: onTap,
scrollPhysics: scrollPhysics,
textWidthBasis: textWidthBasis,
textHeightBehavior: textHeightBehavior,
cursorHeight: cursorHeight,
selectionControls: selectionControls,
onSelectionChanged: onSelectionChanged,
style: style,
));
}
}

View File

@ -128,7 +128,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
items: AppLocalizations.supportedLocales.map<DropdownMenuItem<String>>((Locale value) {
return DropdownMenuItem<String>(
value: value.toString(),
child: Text( key: Key("dropdownLanguage" + value.languageCode), getLanguageFull(context, value.languageCode, value.countryCode)),
child: Text(key: Key("dropdownLanguage" + value.languageCode), getLanguageFull(context, value.languageCode, value.countryCode)),
);
}).toList()))),
SwitchListTile(

View File

@ -46,7 +46,11 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(Provider.of<ContactInfoState>(context).nickname + " " + AppLocalizations.of(context)!.conversationSettings),
title: Container(
height: 24,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: Text(Provider.of<ContactInfoState>(context).nickname + " " + AppLocalizations.of(context)!.conversationSettings)),
),
body: _buildSettingsList(),
);

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:ui';
import 'package:crypto/crypto.dart';
import 'package:cwtch/cwtch/cwtch.dart';
@ -208,10 +209,15 @@ class _MessageViewState extends State<MessageView> {
width: 10,
),
Expanded(
child: Text(
Provider.of<ContactInfoState>(context).nickname,
overflow: TextOverflow.ellipsis,
))
child: Container(
height: 24,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: Text(
Provider.of<ContactInfoState>(context).nickname,
overflow: TextOverflow.clip,
maxLines: 1,
)))
]),
actions: appBarButtons,
),
@ -574,7 +580,7 @@ class _MessageViewState extends State<MessageView> {
keyboardType: TextInputType.multiline,
enableIMEPersonalizedLearning: false,
minLines: 1,
maxLength: (isGroup ? GroupMessageLengthMax : P2PMessageLengthMax) - numberOfBytesMoreThanChar,
maxLength: max(1, (isGroup ? GroupMessageLengthMax : P2PMessageLengthMax) - numberOfBytesMoreThanChar),
maxLengthEnforcement: MaxLengthEnforcement.enforced,
maxLines: 3,
onFieldSubmitted: _sendMessage,

View File

@ -47,7 +47,7 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
}
return Scaffold(
appBar: AppBar(
title: Text(handle + " " + AppLocalizations.of(context)!.conversationSettings),
title: Container(height: 24, clipBehavior: Clip.hardEdge, decoration: BoxDecoration(), child: Text(handle + " " + AppLocalizations.of(context)!.conversationSettings)),
),
body: _buildSettingsList(),
);

View File

@ -44,42 +44,47 @@ class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
@override
Widget build(BuildContext context) {
return Consumer<Settings>(builder: (context, theme, child) {
return TextFormField(
key: widget.testKey,
controller: widget.controller,
readOnly: widget.readonly,
showCursor: !widget.readonly,
focusNode: _focusNode,
enableIMEPersonalizedLearning: false,
onChanged: widget.onChanged,
decoration: InputDecoration(
labelText: widget.labelText,
labelStyle: TextStyle(color: theme.current().mainTextColor, backgroundColor: theme.current().textfieldBackgroundColor),
suffixIcon: IconButton(
onPressed: widget.onPressed,
icon: widget.icon,
splashRadius: Material.defaultSplashRadius / 2,
padding: EdgeInsets.fromLTRB(0.0, 4.0, 2.0, 2.0),
tooltip: widget.tooltip,
enableFeedback: true,
color: theme.current().mainTextColor,
highlightColor: theme.current().defaultButtonColor,
focusColor: theme.current().defaultButtonActiveColor,
splashColor: theme.current().defaultButtonActiveColor,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
filled: true,
fillColor: theme.current().textfieldBackgroundColor,
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorStyle: TextStyle(
color: theme.current().textfieldErrorColor,
fontWeight: FontWeight.bold,
),
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))),
);
return Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(),
child: TextFormField(
key: widget.testKey,
controller: widget.controller,
readOnly: widget.readonly,
showCursor: !widget.readonly,
focusNode: _focusNode,
enableIMEPersonalizedLearning: false,
onChanged: widget.onChanged,
maxLines: 1,
style: TextStyle(overflow: TextOverflow.clip),
decoration: InputDecoration(
labelText: widget.labelText,
labelStyle: TextStyle(color: theme.current().mainTextColor, backgroundColor: theme.current().textfieldBackgroundColor),
suffixIcon: IconButton(
onPressed: widget.onPressed,
icon: widget.icon,
splashRadius: Material.defaultSplashRadius / 2,
padding: EdgeInsets.fromLTRB(0.0, 4.0, 2.0, 2.0),
tooltip: widget.tooltip,
enableFeedback: true,
color: theme.current().mainTextColor,
highlightColor: theme.current().defaultButtonColor,
focusColor: theme.current().defaultButtonActiveColor,
splashColor: theme.current().defaultButtonActiveColor,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
filled: true,
fillColor: theme.current().textfieldBackgroundColor,
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorStyle: TextStyle(
color: theme.current().textfieldErrorColor,
fontWeight: FontWeight.bold,
),
contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))),
));
});
}
}

View File

@ -64,17 +64,22 @@ class _ContactRowState extends State<ContactRow> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
contact.nickname, //(contact.isInvitation ? "invite " : "non-invite ") + (contact.isBlocked ? "blokt" : "nonblokt"),//
Container(
height: 24,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: Text(
contact.nickname, //(contact.isInvitation ? "invite " : "non-invite ") + (contact.isBlocked ? "blokt" : "nonblokt"),//
style: TextStyle(
fontSize: Provider.of<Settings>(context).theme.contactOnionTextSize(),
color: contact.isBlocked
? Provider.of<Settings>(context).theme.portraitBlockedTextColor
: Provider.of<Settings>(context).theme.mainTextColor), //Provider.of<FlwtchState>(context).biggerFont,
softWrap: true,
overflow: TextOverflow.visible,
),
style: TextStyle(
fontSize: Provider.of<Settings>(context).theme.contactOnionTextSize(),
color: contact.isBlocked
? Provider.of<Settings>(context).theme.portraitBlockedTextColor
: Provider.of<Settings>(context).theme.mainTextColor), //Provider.of<FlwtchState>(context).biggerFont,
softWrap: true,
overflow: TextOverflow.clip,
maxLines: 1,
)),
syncStatus ?? Container(),
Visibility(
visible: !Provider.of<Settings>(context).streamerMode,

View File

@ -144,8 +144,12 @@ class FileBubbleState extends State<FileBubble> {
return LayoutBuilder(builder: (bcontext, constraints) {
var wdgSender = Visibility(
visible: widget.interactive,
child: SelectableText(senderDisplayStr + '\u202F',
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor)));
child: Container(
height: 11,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: SelectableText(senderDisplayStr + '\u202F',
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor))));
var isPreview = false;
var wdgMessage = !showFileSharing
? Text(AppLocalizations.of(context)!.messageEnableFileSharing)

View File

@ -42,8 +42,17 @@ class MessageBubbleState extends State<MessageBubble> {
senderDisplayStr = Provider.of<MessageMetadata>(context).senderHandle;
}
}
var wdgSender = SelectableText(senderDisplayStr,
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor));
var wdgSender = Container(
height: 11,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: SelectableText(senderDisplayStr,
maxLines: 1,
style: TextStyle(
fontSize: 9.0,
overflow: TextOverflow.clip,
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
)));
var wdgMessage = SelectableLinkify(
text: widget.content + '\u202F',

View File

@ -44,13 +44,17 @@ class _ProfileRowState extends State<ProfileRow> {
Expanded(
child: Column(
children: [
Text(
profile.nickname,
semanticsLabel: profile.nickname,
style: Provider.of<FlwtchState>(context).biggerFont,
softWrap: true,
overflow: TextOverflow.ellipsis,
),
Container(
height: 24,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: Text(
profile.nickname,
semanticsLabel: profile.nickname,
style: Provider.of<FlwtchState>(context).biggerFont,
softWrap: true,
overflow: TextOverflow.ellipsis,
)),
Visibility(
visible: !Provider.of<Settings>(context).streamerMode,
child: ExcludeSemantics(

View File

@ -43,8 +43,12 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
}
}
var wdgSender = SelectableText(senderDisplayStr,
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor));
var wdgSender = Container(
height: 11,
clipBehavior: Clip.hardEdge,
decoration: BoxDecoration(),
child: SelectableText(senderDisplayStr,
style: TextStyle(fontSize: 9.0, color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor)));
var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
var formatMessages = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);

View File

@ -1,3 +1,4 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
@ -42,37 +43,41 @@ class _CwtchTextFieldState extends State<CwtchTextField> {
@override
Widget build(BuildContext context) {
return Consumer<Settings>(builder: (context, theme, child) {
return TextFormField(
key: widget.testKey,
controller: widget.controller,
validator: widget.validator,
onChanged: widget.onChanged,
autofocus: widget.autofocus,
autovalidateMode: AutovalidateMode.onUserInteraction,
textAlign: widget.number ? TextAlign.end : TextAlign.start,
keyboardType: widget.multiLine
? TextInputType.multiline
: widget.number
? TextInputType.number
: TextInputType.text,
inputFormatters: widget.number ? <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly] : null,
maxLines: widget.multiLine ? null : 1,
scrollController: _scrollController,
enableIMEPersonalizedLearning: false,
focusNode: _focusNode,
decoration: InputDecoration(
errorMaxLines: 2,
hintText: widget.hintText,
floatingLabelBehavior: FloatingLabelBehavior.never,
filled: true,
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorStyle: TextStyle(color: theme.current().textfieldErrorColor, fontWeight: FontWeight.bold, overflow: TextOverflow.visible),
fillColor: theme.current().textfieldBackgroundColor,
contentPadding: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))),
);
return Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(),
child: TextFormField(
key: widget.testKey,
controller: widget.controller,
validator: widget.validator,
onChanged: widget.onChanged,
autofocus: widget.autofocus,
autovalidateMode: AutovalidateMode.onUserInteraction,
textAlign: widget.number ? TextAlign.end : TextAlign.start,
keyboardType: widget.multiLine
? TextInputType.multiline
: widget.number
? TextInputType.number
: TextInputType.text,
inputFormatters: widget.number ? <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly] : null,
maxLines: widget.multiLine ? null : 1,
scrollController: _scrollController,
enableIMEPersonalizedLearning: false,
focusNode: _focusNode,
style: TextStyle(overflow: TextOverflow.clip),
decoration: InputDecoration(
errorMaxLines: 2,
hintText: widget.hintText,
floatingLabelBehavior: FloatingLabelBehavior.never,
filled: true,
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)),
focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)),
errorStyle: TextStyle(color: theme.current().textfieldErrorColor, fontWeight: FontWeight.bold, overflow: TextOverflow.visible),
fillColor: theme.current().textfieldBackgroundColor,
contentPadding: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))),
));
});
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB