mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 09:28:41 +00:00
fix #63
This commit is contained in:
parent
ad674dc77f
commit
672a8cff01
6 changed files with 95 additions and 13 deletions
77
lib/src/components/better_text.dart
Normal file
77
lib/src/components/better_text.dart
Normal 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
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
"onboardingTryForFree": "Kostenlos testen",
|
||||
"registerUsernameSlogan": "Bitte wähle einen Benutzernamen, damit dich andere finden können!",
|
||||
"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!",
|
||||
"newMessageTitle": "Neue Nachricht",
|
||||
"chatsTapToSend": "Klicke, um dein erstes Bild zu teilen.",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
"onboardingTryForFree": "Try for free",
|
||||
"registerUsernameSlogan": "Please select a username so others can find you!",
|
||||
"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!",
|
||||
"newMessageTitle": "New message",
|
||||
"chatsTapToSend": "Click to send your first image",
|
||||
|
|
|
|||
|
|
@ -19,9 +19,14 @@ Future initMediaStorage() async {
|
|||
}
|
||||
|
||||
Future<Box> getMediaStorage() async {
|
||||
await initMediaStorage();
|
||||
|
||||
final storage = getSecureStorage();
|
||||
|
||||
var containsEncryptionKey =
|
||||
await storage.containsKey(key: 'hive_encryption_key');
|
||||
if (!containsEncryptionKey) {
|
||||
await initMediaStorage();
|
||||
}
|
||||
|
||||
var encryptionKey =
|
||||
base64Url.decode((await storage.read(key: 'hive_encryption_key'))!);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/globals.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/message_send_state_icon.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
|
||||
borderRadius: BorderRadius.circular(12.0), // Set border radius
|
||||
),
|
||||
child: SelectableText(
|
||||
content.text,
|
||||
style: TextStyle(
|
||||
color: Colors.white, // Set text color for contrast
|
||||
fontSize: 17,
|
||||
),
|
||||
textAlign: TextAlign.left, // Center the text
|
||||
),
|
||||
child: BetterText(text: content.text),
|
||||
);
|
||||
}
|
||||
} else if (content is MediaMessageContent && !content.isVideo) {
|
||||
|
|
|
|||
|
|
@ -105,9 +105,15 @@ class _RegisterViewState extends State<RegisterView> {
|
|||
const SizedBox(height: 15),
|
||||
TextField(
|
||||
controller: usernameController,
|
||||
onChanged: (value) {
|
||||
usernameController.text = value.toLowerCase();
|
||||
usernameController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: usernameController.text.length),
|
||||
);
|
||||
},
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(12),
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[a-z0-9]')),
|
||||
FilteringTextInputFormatter.allow(RegExp(r'[a-z0-9A-Z]')),
|
||||
],
|
||||
style: TextStyle(fontSize: 17),
|
||||
decoration: getInputDecoration(
|
||||
|
|
|
|||
Loading…
Reference in a new issue