This commit is contained in:
otsmr 2025-03-27 15:14:31 +01:00
parent ad674dc77f
commit 672a8cff01
6 changed files with 95 additions and 13 deletions

View file

@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:logging/logging.dart';
import 'package:url_launcher/url_launcher.dart';
class BetterText extends StatelessWidget {
final String text;
const BetterText({super.key, required this.text});
@override
Widget build(BuildContext context) {
// Regular expression to find URLs and domains
final RegExp urlRegExp = RegExp(
r'(?:(?:https?://|www\.)[^\s]+|(?:[a-zA-Z0-9-]+\.[a-zA-Z]{2,}))',
caseSensitive: false,
multiLine: false,
);
// Split the text into parts based on the URLs and domains
final List<TextSpan> spans = [];
final Iterable<RegExpMatch> matches = urlRegExp.allMatches(text);
int lastMatchEnd = 0;
for (final match in matches) {
// Add the text before the URL/domain
if (match.start > lastMatchEnd) {
spans.add(TextSpan(text: text.substring(lastMatchEnd, match.start)));
}
// Add the URL/domain as a clickable TextSpan
final String? url = match.group(0);
spans.add(TextSpan(
text: url,
style: TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()
..onTap = () async {
final lUrl =
Uri.parse(url!.startsWith('http') ? url : 'http://$url');
try {
await launchUrl(lUrl);
} catch (e) {
Logger("launchUrl").shout("Could not launch $e");
}
},
));
lastMatchEnd = match.end;
}
// Add any remaining text after the last URL/domain
if (lastMatchEnd < text.length) {
spans.add(TextSpan(text: text.substring(lastMatchEnd)));
}
return SelectableText.rich(
TextSpan(
children: spans,
),
style: TextStyle(
color: Colors.white, // Set text color for contrast
fontSize: 17,
),
);
// child: SelectableText(
// content.text,
// style: TextStyle(
// color: Colors.white, // Set text color for contrast
// fontSize: 17,
// ),
// textAlign: TextAlign.left, // Center the text
// ),
// RichText
}
}

View file

@ -19,7 +19,7 @@
"onboardingTryForFree": "Kostenlos testen", "onboardingTryForFree": "Kostenlos testen",
"registerUsernameSlogan": "Bitte wähle einen Benutzernamen, damit dich andere finden können!", "registerUsernameSlogan": "Bitte wähle einen Benutzernamen, damit dich andere finden können!",
"registerUsernameDecoration": "Benutzername", "registerUsernameDecoration": "Benutzername",
"registerUsernameLimits": "Der Benutzername muss 4 bis 12 Zeichen lang sein und darf nur aus Buchstaben (a-z) und Zahlen (0-9) bestehen.", "registerUsernameLimits": "Der Benutzername muss 3 bis 12 Zeichen lang sein und darf nur aus Buchstaben (a-z) und Zahlen (0-9) bestehen.",
"registerSubmitButton": "Jetzt registrieren!", "registerSubmitButton": "Jetzt registrieren!",
"newMessageTitle": "Neue Nachricht", "newMessageTitle": "Neue Nachricht",
"chatsTapToSend": "Klicke, um dein erstes Bild zu teilen.", "chatsTapToSend": "Klicke, um dein erstes Bild zu teilen.",

View file

@ -19,7 +19,7 @@
"onboardingTryForFree": "Try for free", "onboardingTryForFree": "Try for free",
"registerUsernameSlogan": "Please select a username so others can find you!", "registerUsernameSlogan": "Please select a username so others can find you!",
"registerUsernameDecoration": "Username", "registerUsernameDecoration": "Username",
"registerUsernameLimits": "Username must be 4 to 12 characters long, consisting only of letters (a-z) and numbers (0-9).", "registerUsernameLimits": "Username must be 3 to 12 characters long, consisting only of letters (a-z) and numbers (0-9).",
"registerSubmitButton": "Register now!", "registerSubmitButton": "Register now!",
"newMessageTitle": "New message", "newMessageTitle": "New message",
"chatsTapToSend": "Click to send your first image", "chatsTapToSend": "Click to send your first image",

View file

@ -19,9 +19,14 @@ Future initMediaStorage() async {
} }
Future<Box> getMediaStorage() async { Future<Box> getMediaStorage() async {
await initMediaStorage();
final storage = getSecureStorage(); final storage = getSecureStorage();
var containsEncryptionKey =
await storage.containsKey(key: 'hive_encryption_key');
if (!containsEncryptionKey) {
await initMediaStorage();
}
var encryptionKey = var encryptionKey =
base64Url.decode((await storage.read(key: 'hive_encryption_key'))!); base64Url.decode((await storage.read(key: 'hive_encryption_key'))!);

View file

@ -6,6 +6,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/globals.dart'; import 'package:twonly/globals.dart';
import 'package:twonly/src/components/animate_icon.dart'; import 'package:twonly/src/components/animate_icon.dart';
import 'package:twonly/src/components/better_text.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/components/message_send_state_icon.dart'; import 'package:twonly/src/components/message_send_state_icon.dart';
import 'package:twonly/src/components/verified_shield.dart'; import 'package:twonly/src/components/verified_shield.dart';
@ -62,14 +63,7 @@ class ChatListEntry extends StatelessWidget {
83, 68, 137, 255), // Set the background color 83, 68, 137, 255), // Set the background color
borderRadius: BorderRadius.circular(12.0), // Set border radius borderRadius: BorderRadius.circular(12.0), // Set border radius
), ),
child: SelectableText( child: BetterText(text: content.text),
content.text,
style: TextStyle(
color: Colors.white, // Set text color for contrast
fontSize: 17,
),
textAlign: TextAlign.left, // Center the text
),
); );
} }
} else if (content is MediaMessageContent && !content.isVideo) { } else if (content is MediaMessageContent && !content.isVideo) {

View file

@ -105,9 +105,15 @@ class _RegisterViewState extends State<RegisterView> {
const SizedBox(height: 15), const SizedBox(height: 15),
TextField( TextField(
controller: usernameController, controller: usernameController,
onChanged: (value) {
usernameController.text = value.toLowerCase();
usernameController.selection = TextSelection.fromPosition(
TextPosition(offset: usernameController.text.length),
);
},
inputFormatters: [ inputFormatters: [
LengthLimitingTextInputFormatter(12), LengthLimitingTextInputFormatter(12),
FilteringTextInputFormatter.allow(RegExp(r'[a-z0-9]')), FilteringTextInputFormatter.allow(RegExp(r'[a-z0-9A-Z]')),
], ],
style: TextStyle(fontSize: 17), style: TextStyle(fontSize: 17),
decoration: getInputDecoration( decoration: getInputDecoration(