Small UI Fixes / Font Styles / Abstractions
This commit is contained in:
parent
7f50036968
commit
22bf5cfe92
|
@ -360,8 +360,7 @@ class MainActivity: FlutterActivity() {
|
|||
"PeerWithOnion" -> {
|
||||
val profile: String = call.argument("ProfileOnion") ?: ""
|
||||
val onion: String = call.argument("onion") ?: ""
|
||||
result.success(Cwtch.peerWithOnion(profile, onion))
|
||||
return
|
||||
Cwtch.peerWithOnion(profile, onion)
|
||||
}
|
||||
|
||||
|
||||
|
@ -493,14 +492,17 @@ class MainActivity: FlutterActivity() {
|
|||
}
|
||||
"GetProfileAttribute" -> {
|
||||
val profile: String = call.argument("ProfileOnion") ?: ""
|
||||
val key: String = call.argument("Key") ?: ""
|
||||
Data.Builder().putString("result", Cwtch.getProfileAttribute(profile, key)).build()
|
||||
val key: String = call.argument("key") ?: ""
|
||||
var resultjson = Cwtch.getProfileAttribute(profile, key);
|
||||
return result.success(resultjson)
|
||||
}
|
||||
"GetConversationAttribute" -> {
|
||||
val profile: String = call.argument("ProfileOnion") ?: ""
|
||||
val conversation: Int = call.argument("conversation") ?: 0
|
||||
val key: String = call.argument("Key") ?: ""
|
||||
Data.Builder().putString("result", Cwtch.getConversationAttribute(profile, conversation.toLong(), key)).build()
|
||||
val key: String = call.argument("key") ?: ""
|
||||
var resultjson = Cwtch.getConversationAttribute(profile, conversation.toLong(), key);
|
||||
result.success(resultjson)
|
||||
return
|
||||
}
|
||||
"SetConversationAttribute" -> {
|
||||
val profile: String = call.argument("ProfileOnion") ?: ""
|
||||
|
@ -512,7 +514,8 @@ class MainActivity: FlutterActivity() {
|
|||
"ImportProfile" -> {
|
||||
val file: String = call.argument("file") ?: ""
|
||||
val pass: String = call.argument("pass") ?: ""
|
||||
Data.Builder().putString("result", Cwtch.importProfile(file, pass)).build()
|
||||
result.success(Cwtch.importProfile(file, pass))
|
||||
return
|
||||
}
|
||||
"ReconnectCwtchForeground" -> {
|
||||
Cwtch.reconnectCwtchForeground()
|
||||
|
|
|
@ -1,16 +1,20 @@
|
|||
import 'package:cwtch/themes/opaque.dart';
|
||||
import 'package:cwtch/third_party/linkify/linkify.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import '../settings.dart';
|
||||
|
||||
void modalOpenLink(BuildContext ctx, LinkableElement link) {
|
||||
showModalBottomSheet<void>(
|
||||
context: ctx,
|
||||
builder: (BuildContext bcontext) {
|
||||
return Container(
|
||||
height: 200, // bespoke value courtesy of the [TextField] docs
|
||||
height: 200,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(30.0),
|
||||
|
@ -18,17 +22,24 @@ void modalOpenLink(BuildContext ctx, LinkableElement link) {
|
|||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(AppLocalizations.of(bcontext)!.clickableLinksWarning),
|
||||
Text(
|
||||
AppLocalizations.of(bcontext)!.clickableLinksWarning,
|
||||
style: Provider.of<Settings>(bcontext).scaleFonts(defaultTextStyle),
|
||||
),
|
||||
Flex(direction: Axis.horizontal, mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinksCopy, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinksCopy),
|
||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinksCopy,
|
||||
style: Provider.of<Settings>(bcontext).scaleFonts(defaultTextButtonStyle), semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinksCopy),
|
||||
onPressed: () {
|
||||
Clipboard.setData(new ClipboardData(text: link.url));
|
||||
|
||||
final snackBar = SnackBar(
|
||||
content: Text(AppLocalizations.of(bcontext)!.copiedToClipboardNotification),
|
||||
content: Text(
|
||||
AppLocalizations.of(bcontext)!.copiedToClipboardNotification,
|
||||
style: Provider.of<Settings>(bcontext).scaleFonts(defaultTextButtonStyle),
|
||||
),
|
||||
);
|
||||
|
||||
Navigator.pop(bcontext);
|
||||
|
@ -39,15 +50,14 @@ void modalOpenLink(BuildContext ctx, LinkableElement link) {
|
|||
Container(
|
||||
margin: EdgeInsets.symmetric(vertical: 20, horizontal: 10),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinkOpen, semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinkOpen),
|
||||
child: Text(AppLocalizations.of(bcontext)!.clickableLinkOpen,
|
||||
style: Provider.of<Settings>(bcontext).scaleFonts(defaultTextButtonStyle), semanticsLabel: AppLocalizations.of(bcontext)!.clickableLinkOpen),
|
||||
onPressed: () async {
|
||||
if (await canLaunch(link.url)) {
|
||||
await launch(link.url);
|
||||
if (await canLaunchUrlString(link.url)) {
|
||||
await launchUrlString(link.url);
|
||||
Navigator.pop(bcontext);
|
||||
} else {
|
||||
final snackBar = SnackBar(
|
||||
content: Text(AppLocalizations.of(bcontext)!.clickableLinkError),
|
||||
);
|
||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(bcontext)!.clickableLinkError, style: Provider.of<Settings>(bcontext).scaleFonts(defaultTextButtonStyle)));
|
||||
ScaffoldMessenger.of(bcontext).showSnackBar(snackBar);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -97,11 +97,11 @@ abstract class Cwtch {
|
|||
Future<dynamic> ImportBundle(String profile, String bundle);
|
||||
// ignore: non_constant_identifier_names
|
||||
void SetProfileAttribute(String profile, String key, String val);
|
||||
String? GetProfileAttribute(String profile, String key);
|
||||
Future<String?> GetProfileAttribute(String profile, String key);
|
||||
// ignore: non_constant_identifier_names
|
||||
void SetConversationAttribute(String profile, int conversation, String key, String val);
|
||||
// ignore: non_constant_identifier_names
|
||||
String? GetConversationAttribute(String profile, int identifier, String s);
|
||||
Future<String?> GetConversationAttribute(String profile, int identifier, String s);
|
||||
// ignore: non_constant_identifier_names
|
||||
void SetMessageAttribute(String profile, int conversation, int channel, int message, String key, String val);
|
||||
// ignore: non_constant_identifier_names
|
||||
|
|
|
@ -59,6 +59,8 @@ class CwtchNotifier {
|
|||
}
|
||||
|
||||
void handleMessage(String type, dynamic data) {
|
||||
EnvironmentConfig.debugLog("Handing Message $type ${data.toString()}");
|
||||
|
||||
//EnvironmentConfig.debugLog("NewEvent $type $data");
|
||||
switch (type) {
|
||||
case "CwtchStarted":
|
||||
|
@ -68,25 +70,24 @@ class CwtchNotifier {
|
|||
appState.SetAppError(data["Error"]);
|
||||
break;
|
||||
case "NewPeer":
|
||||
// empty events can be caused by the testing framework
|
||||
if (data["Online"] == null) {
|
||||
break;
|
||||
}
|
||||
// EnvironmentConfig.debugLog("NewPeer $data");
|
||||
// if tag != v1-defaultPassword then it is either encrypted OR it is an unencrypted account created during pre-beta...
|
||||
profileCN.add(data["Identity"], data["name"], data["picture"], data["defaultPicture"], data["ContactsJson"], data["ServerList"], data["Online"] == "true", data["autostart"] == "true",
|
||||
data["tag"] != "v1-defaultPassword");
|
||||
|
||||
// Update Profile Attributes
|
||||
profileCN.getProfile(data["Identity"])?.setAttribute(0, flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-1"));
|
||||
profileCN.getProfile(data["Identity"])?.setAttribute(1, flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-2"));
|
||||
profileCN.getProfile(data["Identity"])?.setAttribute(2, flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-3"));
|
||||
profileCN.getProfile(data["Identity"])?.setAvailabilityStatus(flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-status") ?? "");
|
||||
EnvironmentConfig.debugLog("Looking up Profile Attributes ${data["Identity"]} ${profileCN.getProfile(data["Identity"])}");
|
||||
flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-1").then((value) => profileCN.getProfile(data["Identity"])?.setAttribute(0, value));
|
||||
flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-2").then((value) => profileCN.getProfile(data["Identity"])?.setAttribute(1, value));
|
||||
flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-attribute-3").then((value) => profileCN.getProfile(data["Identity"])?.setAttribute(2, value));
|
||||
flwtchState.cwtch.GetProfileAttribute(data["Identity"], "profile.profile-status").then((value) => profileCN.getProfile(data["Identity"])?.setAvailabilityStatus(value ?? ""));
|
||||
|
||||
EnvironmentConfig.debugLog("Looking up Profile Information for Contact...");
|
||||
profileCN.getProfile(data["Identity"])?.contactList.contacts.forEach((contact) {
|
||||
contact.setAttribute(0, flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-1"));
|
||||
contact.setAttribute(1, flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-2"));
|
||||
contact.setAttribute(2, flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-3"));
|
||||
contact.setAvailabilityStatus(flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-status") ?? "");
|
||||
flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-1").then((value) => contact.setAttribute(0, value));
|
||||
flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-2").then((value) => contact.setAttribute(1, value));
|
||||
flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-attribute-3").then((value) => contact.setAttribute(2, value));
|
||||
flwtchState.cwtch.GetConversationAttribute(data["Identity"], contact.identifier, "public.profile.profile-status").then((value) => contact.setAvailabilityStatus(value ?? ""));
|
||||
});
|
||||
|
||||
break;
|
||||
|
@ -392,6 +393,7 @@ class CwtchNotifier {
|
|||
String fileKey = data['Data'];
|
||||
var contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]);
|
||||
if (contact != null) {
|
||||
EnvironmentConfig.debugLog("waiting for download from $contact");
|
||||
profileCN.getProfile(data["ProfileOnion"])?.waitForDownloadComplete(contact.identifier, fileKey);
|
||||
}
|
||||
} else if (data['Path'] == "profile.profile-attribute-1" || data['Path'] == "profile.profile-attribute-2" || data['Path'] == "profile.profile-attribute-3") {
|
||||
|
|
|
@ -967,7 +967,7 @@ class CwtchFfi implements Cwtch {
|
|||
}
|
||||
|
||||
@override
|
||||
String? GetProfileAttribute(String profile, String key) {
|
||||
Future<String?> GetProfileAttribute(String profile, String key) {
|
||||
var getProfileAttributeC = library.lookup<NativeFunction<get_json_blob_from_str_str_function>>("c_GetProfileAttribute");
|
||||
// ignore: non_constant_identifier_names
|
||||
final GetProfileAttribute = getProfileAttributeC.asFunction<GetJsonBlobFromStrStrFn>();
|
||||
|
@ -982,17 +982,17 @@ class CwtchFfi implements Cwtch {
|
|||
try {
|
||||
dynamic attributeResult = json.decode(jsonMessage);
|
||||
if (attributeResult["Exists"]) {
|
||||
return attributeResult["Value"];
|
||||
return Future.value(attributeResult["Value"]);
|
||||
}
|
||||
} catch (e) {
|
||||
EnvironmentConfig.debugLog("error getting profile attribute: $e");
|
||||
}
|
||||
|
||||
return null;
|
||||
return Future.value(null);
|
||||
}
|
||||
|
||||
@override
|
||||
String? GetConversationAttribute(String profile, int conversation, String key) {
|
||||
Future<String?> GetConversationAttribute(String profile, int conversation, String key) {
|
||||
var getConversationAttributeC = library.lookup<NativeFunction<get_json_blob_from_str_int_string_function>>("c_GetConversationAttribute");
|
||||
// ignore: non_constant_identifier_names
|
||||
final GetConversationAttribute = getConversationAttributeC.asFunction<GetJsonBlobFromStrIntStringFn>();
|
||||
|
@ -1007,13 +1007,13 @@ class CwtchFfi implements Cwtch {
|
|||
try {
|
||||
dynamic attributeResult = json.decode(jsonMessage);
|
||||
if (attributeResult["Exists"]) {
|
||||
return attributeResult["Value"];
|
||||
return Future.value(attributeResult["Value"]);
|
||||
}
|
||||
} catch (e) {
|
||||
EnvironmentConfig.debugLog("error getting profile attribute: $e");
|
||||
}
|
||||
|
||||
return null;
|
||||
return Future.value(null);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -390,21 +390,25 @@ class CwtchGomobile implements Cwtch {
|
|||
}
|
||||
|
||||
@override
|
||||
String? GetProfileAttribute(String profile, String key) {
|
||||
dynamic attributeResult = cwtchPlatform.invokeMethod("GetProfileAttribute", {"ProfileOnion": profile, "key": key});
|
||||
if (attributeResult["Exists"]) {
|
||||
return attributeResult["Value"];
|
||||
}
|
||||
return null;
|
||||
Future<String?> GetProfileAttribute(String profile, String key) async {
|
||||
return await cwtchPlatform.invokeMethod("GetProfileAttribute", {"ProfileOnion": profile, "key": key}).then((dynamic json) {
|
||||
var value = jsonDecode(json);
|
||||
if (value["Exists"]) {
|
||||
return value["Value"];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
String? GetConversationAttribute(String profile, int conversation, String key) {
|
||||
dynamic attributeResult = cwtchPlatform.invokeMethod("GetProfileAttribute", {"ProfileOnion": profile, "conversation": conversation, "key": key});
|
||||
if (attributeResult["Exists"]) {
|
||||
return attributeResult["Value"];
|
||||
}
|
||||
return null;
|
||||
Future<String?> GetConversationAttribute(String profile, int conversation, String key) async {
|
||||
return await cwtchPlatform.invokeMethod("GetConversationAttribute", {"ProfileOnion": profile, "conversation": conversation, "key": key}).then((dynamic json) {
|
||||
var value = jsonDecode(json);
|
||||
if (value["Exists"]) {
|
||||
return value["Value"];
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -87,7 +87,6 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
|||
print("initState: running...");
|
||||
windowManager.addListener(this);
|
||||
|
||||
|
||||
print("initState: registering notification, shutdown handlers...");
|
||||
profs = ProfileListState();
|
||||
notificationClickChannel.setMethodCallHandler(_externalNotificationClicked);
|
||||
|
@ -307,8 +306,7 @@ class FlwtchState extends State<Flwtch> with WindowListener {
|
|||
globalAppState.focus = false;
|
||||
}
|
||||
|
||||
void onWindowClose() {
|
||||
}
|
||||
void onWindowClose() {}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
|
|
|
@ -264,6 +264,15 @@ class ContactInfoState extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
bool canSend() {
|
||||
if (this.isGroup == true) {
|
||||
// We now have an out of sync warning so we will mark these as online...
|
||||
return this.status == "Synced";
|
||||
} else {
|
||||
return this.isOnline();
|
||||
}
|
||||
}
|
||||
|
||||
ConversationNotificationPolicy get notificationsPolicy => _notificationPolicy;
|
||||
|
||||
set notificationsPolicy(ConversationNotificationPolicy newVal) {
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// A "MessageDraft" structure that stores information about in-progress message drafts.
|
||||
/// MessageDraft stores text, quoted replies, and attached images.
|
||||
/// Only one draft is stored per conversation.
|
||||
class MessageDraft extends ChangeNotifier {
|
||||
String? _messageText;
|
||||
QuotedReference? _quotedReference;
|
||||
|
||||
TextEditingController ctrlCompose = TextEditingController();
|
||||
|
||||
static MessageDraft empty() {
|
||||
return MessageDraft();
|
||||
}
|
||||
|
||||
bool isEmpty() {
|
||||
return (this._messageText == null && this._quotedReference == null) || (this._messageText != null && this._messageText!.isEmpty);
|
||||
return (this._quotedReference == null) || (this.messageText.isEmpty);
|
||||
}
|
||||
|
||||
String? get messageText => _messageText;
|
||||
String get messageText => ctrlCompose.text;
|
||||
|
||||
set messageText(String? text) {
|
||||
this._messageText = text;
|
||||
set messageText(String text) {
|
||||
this.ctrlCompose.text = text;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -35,6 +37,18 @@ class MessageDraft extends ChangeNotifier {
|
|||
this._quotedReference = null;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void clearDraft() {
|
||||
this._quotedReference = null;
|
||||
this.ctrlCompose.clear();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
ctrlCompose.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// A QuotedReference encapsulates the state of replied-to message.
|
||||
|
|
|
@ -23,8 +23,7 @@ abstract class NotificationsManager {
|
|||
// NullNotificationsManager ignores all notification requests
|
||||
class NullNotificationsManager implements NotificationsManager {
|
||||
@override
|
||||
Future<void> notify(
|
||||
String message, String profile, int conversationId) async {}
|
||||
Future<void> notify(String message, String profile, int conversationId) async {}
|
||||
}
|
||||
|
||||
// Windows Notification Manager uses https://pub.dev/packages/desktoasts to implement
|
||||
|
@ -34,7 +33,6 @@ class WindowsNotificationManager implements NotificationsManager {
|
|||
bool initialized = false;
|
||||
late Future<void> Function(String, int) notificationSelectConvo;
|
||||
|
||||
|
||||
WindowsNotificationManager(Future<void> Function(String, int) notificationSelectConvo) {
|
||||
this.notificationSelectConvo = notificationSelectConvo;
|
||||
scheduleMicrotask(() async {
|
||||
|
@ -83,28 +81,26 @@ class WindowsNotificationManager implements NotificationsManager {
|
|||
*
|
||||
WinToast.instance().clear();
|
||||
await WinToast.instance().showToast(
|
||||
toast: Toast(
|
||||
duration: ToastDuration.short,
|
||||
children: [
|
||||
ToastChildAudio(source: ToastAudioSource.im),
|
||||
ToastChildVisual(
|
||||
binding: ToastVisualBinding(children: [
|
||||
ToastVisualBindingChildText(
|
||||
text: message,
|
||||
id: 1,
|
||||
),
|
||||
])),
|
||||
ToastChildActions(children: [
|
||||
ToastAction(
|
||||
content: "Open",
|
||||
arguments: jsonEncode(NotificationPayload(profile, conversationId)),
|
||||
),
|
||||
ToastAction(
|
||||
content: "Close",
|
||||
arguments: "close",
|
||||
),
|
||||
]),
|
||||
]));
|
||||
toast: Toast(duration: ToastDuration.short, children: [
|
||||
ToastChildAudio(source: ToastAudioSource.im),
|
||||
ToastChildVisual(
|
||||
binding: ToastVisualBinding(children: [
|
||||
ToastVisualBindingChildText(
|
||||
text: message,
|
||||
id: 1,
|
||||
),
|
||||
])),
|
||||
ToastChildActions(children: [
|
||||
ToastAction(
|
||||
content: "Open",
|
||||
arguments: jsonEncode(NotificationPayload(profile, conversationId)),
|
||||
),
|
||||
ToastAction(
|
||||
content: "Close",
|
||||
arguments: "close",
|
||||
),
|
||||
]),
|
||||
]));
|
||||
active = false;
|
||||
}*/
|
||||
}
|
||||
|
@ -142,8 +138,7 @@ class NixNotificationManager implements NotificationsManager {
|
|||
Future<String> detectLinuxAssetsPath() async {
|
||||
var devStat = FileStat.stat("assets");
|
||||
var localStat = FileStat.stat("data/flutter_assets");
|
||||
var homeStat = FileStat.stat((Platform.environment["HOME"] ?? "") +
|
||||
"/.local/share/cwtch/data/flutter_assets");
|
||||
var homeStat = FileStat.stat((Platform.environment["HOME"] ?? "") + "/.local/share/cwtch/data/flutter_assets");
|
||||
var rootStat = FileStat.stat("/usr/share/cwtch/data/flutter_assets");
|
||||
|
||||
if ((await devStat).type == FileSystemEntityType.directory) {
|
||||
|
@ -151,16 +146,14 @@ class NixNotificationManager implements NotificationsManager {
|
|||
} else if ((await localStat).type == FileSystemEntityType.directory) {
|
||||
return path.join(Directory.current.path, "data/flutter_assets/");
|
||||
} else if ((await homeStat).type == FileSystemEntityType.directory) {
|
||||
return (Platform.environment["HOME"] ?? "") +
|
||||
"/.local/share/cwtch/data/flutter_assets/";
|
||||
return (Platform.environment["HOME"] ?? "") + "/.local/share/cwtch/data/flutter_assets/";
|
||||
} else if ((await rootStat).type == FileSystemEntityType.directory) {
|
||||
return "/usr/share/cwtch/data/flutter_assets/";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
NixNotificationManager(
|
||||
Future<void> Function(String, int) notificationSelectConvo) {
|
||||
NixNotificationManager(Future<void> Function(String, int) notificationSelectConvo) {
|
||||
this.notificationSelectConvo = notificationSelectConvo;
|
||||
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||
|
||||
|
@ -171,26 +164,14 @@ class NixNotificationManager implements NotificationsManager {
|
|||
linuxAssetsPath = "";
|
||||
}
|
||||
|
||||
var linuxIcon =
|
||||
FilePathLinuxIcon(path.join(linuxAssetsPath, 'assets/knott.png'));
|
||||
var linuxIcon = FilePathLinuxIcon(path.join(linuxAssetsPath, 'assets/knott.png'));
|
||||
|
||||
final LinuxInitializationSettings initializationSettingsLinux =
|
||||
LinuxInitializationSettings(
|
||||
defaultActionName: 'Open notification',
|
||||
defaultIcon: linuxIcon,
|
||||
defaultSuppressSound: true);
|
||||
final LinuxInitializationSettings initializationSettingsLinux = LinuxInitializationSettings(defaultActionName: 'Open notification', defaultIcon: linuxIcon, defaultSuppressSound: true);
|
||||
|
||||
final InitializationSettings initializationSettings =
|
||||
InitializationSettings(
|
||||
android: null,
|
||||
iOS: null,
|
||||
macOS: DarwinInitializationSettings(defaultPresentSound: false),
|
||||
linux: initializationSettingsLinux);
|
||||
InitializationSettings(android: null, iOS: null, macOS: DarwinInitializationSettings(defaultPresentSound: false), linux: initializationSettingsLinux);
|
||||
|
||||
flutterLocalNotificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<
|
||||
MacOSFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermissions(
|
||||
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<MacOSFlutterLocalNotificationsPlugin>()?.requestPermissions(
|
||||
alert: true,
|
||||
badge: false,
|
||||
sound: false,
|
||||
|
@ -202,8 +183,7 @@ class NixNotificationManager implements NotificationsManager {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> notify(
|
||||
String message, String profile, int conversationId) async {
|
||||
Future<void> notify(String message, String profile, int conversationId) async {
|
||||
if (!globalAppState.focus) {
|
||||
// Warning: Only use title field on Linux, body field will render links as clickable
|
||||
await flutterLocalNotificationsPlugin.show(
|
||||
|
@ -211,11 +191,7 @@ class NixNotificationManager implements NotificationsManager {
|
|||
message,
|
||||
'',
|
||||
NotificationDetails(
|
||||
linux: LinuxNotificationDetails(
|
||||
suppressSound: true,
|
||||
category: LinuxNotificationCategory.imReceived,
|
||||
icon: FilePathLinuxIcon(
|
||||
path.join(linuxAssetsPath, 'assets/knott.png')))),
|
||||
linux: LinuxNotificationDetails(suppressSound: true, category: LinuxNotificationCategory.imReceived, icon: FilePathLinuxIcon(path.join(linuxAssetsPath, 'assets/knott.png')))),
|
||||
payload: jsonEncode(NotificationPayload(profile, conversationId)));
|
||||
}
|
||||
}
|
||||
|
@ -230,9 +206,7 @@ class NixNotificationManager implements NotificationsManager {
|
|||
}
|
||||
}
|
||||
|
||||
NotificationsManager newDesktopNotificationsManager(
|
||||
Future<void> Function(String profileOnion, int convoId)
|
||||
notificationSelectConvo) {
|
||||
NotificationsManager newDesktopNotificationsManager(Future<void> Function(String profileOnion, int convoId) notificationSelectConvo) {
|
||||
// We don't want notifications in Dev Mode
|
||||
if (EnvironmentConfig.TEST_MODE) {
|
||||
return NullNotificationsManager();
|
||||
|
@ -242,22 +216,19 @@ NotificationsManager newDesktopNotificationsManager(
|
|||
try {
|
||||
return NixNotificationManager(notificationSelectConvo);
|
||||
} catch (e) {
|
||||
EnvironmentConfig.debugLog(
|
||||
"Failed to create LinuxNotificationManager. Switching off notifications.");
|
||||
EnvironmentConfig.debugLog("Failed to create LinuxNotificationManager. Switching off notifications.");
|
||||
}
|
||||
} else if (Platform.isMacOS) {
|
||||
try {
|
||||
return NixNotificationManager(notificationSelectConvo);
|
||||
} catch (e) {
|
||||
EnvironmentConfig.debugLog(
|
||||
"Failed to create NixNotificationManager. Switching off notifications.");
|
||||
EnvironmentConfig.debugLog("Failed to create NixNotificationManager. Switching off notifications.");
|
||||
}
|
||||
} else if (Platform.isWindows) {
|
||||
try {
|
||||
return WindowsNotificationManager(notificationSelectConvo);
|
||||
} catch (e) {
|
||||
EnvironmentConfig.debugLog(
|
||||
"Failed to create Windows desktoasts notification manager");
|
||||
EnvironmentConfig.debugLog("Failed to create Windows desktoasts notification manager");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,6 +192,11 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
double get fontScaling => _fontScaling;
|
||||
|
||||
// a convenience function to scale fonts dynamically...
|
||||
TextStyle scaleFonts(TextStyle input) {
|
||||
return input.copyWith(fontSize: (input.fontSize ?? 12) * this.fontScaling);
|
||||
}
|
||||
|
||||
/// Switch the Locale of the App
|
||||
switchLocale(Locale newLocale) {
|
||||
locale = newLocale;
|
||||
|
|
|
@ -19,6 +19,12 @@ import 'neon2.dart';
|
|||
const mode_light = "light";
|
||||
const mode_dark = "dark";
|
||||
|
||||
final TextStyle defaultSmallTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.normal, fontSize: 10);
|
||||
final TextStyle defaultMessageTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.normal, fontSize: 12);
|
||||
final TextStyle defaultFormLabelTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: 20);
|
||||
final TextStyle defaultTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w500, fontSize: 12);
|
||||
final TextStyle defaultTextButtonStyle = defaultTextStyle.copyWith(fontWeight: FontWeight.bold);
|
||||
|
||||
final themes = {
|
||||
cwtch_theme: {mode_light: CwtchLight(), mode_dark: CwtchDark()},
|
||||
ghost_theme: {mode_light: GhostLight(), mode_dark: GhostDark()},
|
||||
|
|
|
@ -44,7 +44,6 @@ class MessageView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _MessageViewState extends State<MessageView> {
|
||||
final ctrlrCompose = TextEditingController();
|
||||
final focusNode = FocusNode();
|
||||
int selectedContact = -1;
|
||||
ItemPositionsListener scrollListener = ItemPositionsListener.create();
|
||||
|
@ -68,7 +67,6 @@ class _MessageViewState extends State<MessageView> {
|
|||
showDown = false;
|
||||
}
|
||||
});
|
||||
ctrlrCompose.text = Provider.of<ContactInfoState>(context, listen: false).messageDraft.messageText ?? "";
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -88,7 +86,6 @@ class _MessageViewState extends State<MessageView> {
|
|||
@override
|
||||
void dispose() {
|
||||
focusNode.dispose();
|
||||
ctrlrCompose.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -321,22 +318,22 @@ class _MessageViewState extends State<MessageView> {
|
|||
|
||||
void _sendMessage([String? ignoredParam]) {
|
||||
// Do this after we trim to preserve enter-behaviour...
|
||||
bool isOffline = Provider.of<ContactInfoState>(context, listen: false).isOnline() == false;
|
||||
bool cannotSend = Provider.of<ContactInfoState>(context, listen: false).canSend() == false;
|
||||
bool performingAntiSpam = Provider.of<ContactInfoState>(context, listen: false).antispamTickets == 0;
|
||||
bool isGroup = Provider.of<ContactInfoState>(context, listen: false).isGroup;
|
||||
if (isOffline || (isGroup && performingAntiSpam)) {
|
||||
if (cannotSend || (isGroup && performingAntiSpam)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trim message
|
||||
final messageWithoutNewLine = ctrlrCompose.value.text.trimRight();
|
||||
ctrlrCompose.value = TextEditingValue(text: messageWithoutNewLine, selection: TextSelection.fromPosition(TextPosition(offset: messageWithoutNewLine.length)));
|
||||
var messageText = Provider.of<ContactInfoState>(context, listen: false).messageDraft.messageText ?? "";
|
||||
final messageWithoutNewLine = messageText.trimRight();
|
||||
|
||||
// peers and groups currently have different length constraints (servers can store less)...
|
||||
var actualMessageLength = ctrlrCompose.value.text.length;
|
||||
var actualMessageLength = messageText.length;
|
||||
var lengthOk = (isGroup && actualMessageLength < GroupMessageLengthMax) || actualMessageLength <= P2PMessageLengthMax;
|
||||
|
||||
if (ctrlrCompose.value.text.isNotEmpty && lengthOk) {
|
||||
if (messageWithoutNewLine.isNotEmpty && lengthOk) {
|
||||
if (Provider.of<AppState>(context, listen: false).selectedConversation != null && Provider.of<ContactInfoState>(context, listen: false).messageDraft.getQuotedMessage() != null) {
|
||||
var conversationId = Provider.of<AppState>(context, listen: false).selectedConversation!;
|
||||
MessageCache? cache = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(conversationId)?.messageCache;
|
||||
|
@ -347,7 +344,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
var bytes1 = utf8.encode(data!.metadata.senderHandle + data.wrapper);
|
||||
var digest1 = sha256.convert(bytes1);
|
||||
var contentHash = base64Encode(digest1.bytes);
|
||||
var quotedMessage = jsonEncode(QuotedMessageStructure(contentHash, ctrlrCompose.value.text));
|
||||
var quotedMessage = jsonEncode(QuotedMessageStructure(contentHash, messageWithoutNewLine));
|
||||
ChatMessage cm = new ChatMessage(o: QuotedMessageOverlay, d: quotedMessage);
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
|
@ -359,7 +356,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.clearQuotedReference();
|
||||
});
|
||||
} else {
|
||||
ChatMessage cm = new ChatMessage(o: TextMessageOverlay, d: ctrlrCompose.value.text);
|
||||
ChatMessage cm = new ChatMessage(o: TextMessageOverlay, d: messageWithoutNewLine);
|
||||
Provider.of<FlwtchState>(context, listen: false)
|
||||
.cwtch
|
||||
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, jsonEncode(cm))
|
||||
|
@ -391,8 +388,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
|
||||
// At this point we have decided to send the text to the backend, failure is still possible
|
||||
// but it will show as an error-ed message, as such the draft can be purged.
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft = MessageDraft.empty();
|
||||
ctrlrCompose.clear();
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.clearDraft();
|
||||
|
||||
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
|
||||
var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
|
||||
|
@ -424,7 +420,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
var wdgMessage = Padding(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: SelectableLinkify(
|
||||
text: ctrlrCompose.text + '\n',
|
||||
text: Provider.of<ContactInfoState>(context).messageDraft.messageText + '\n',
|
||||
options: LinkifyOptions(messageFormatting: true, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
|
||||
linkifiers: [UrlLinkifier()],
|
||||
onOpen: showClickableLinks ? null : null,
|
||||
|
@ -468,7 +464,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
margin: EdgeInsets.all(2),
|
||||
|
||||
// 164 minimum height + 16px for every line of text so the entire message is displayed when previewed.
|
||||
height: 164 + ((ctrlrCompose.text.split("\n").length - 1) * 16),
|
||||
height: 164 + ((Provider.of<ContactInfoState>(context).messageDraft.messageText.split("\n").length - 1) * 16),
|
||||
child: Column(
|
||||
children: [
|
||||
Row(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [preview]),
|
||||
|
@ -483,11 +479,11 @@ class _MessageViewState extends State<MessageView> {
|
|||
}
|
||||
|
||||
Widget _buildComposeBox(BuildContext context) {
|
||||
bool isOffline = Provider.of<ContactInfoState>(context).isOnline() == false;
|
||||
bool cannotSend = Provider.of<ContactInfoState>(context).canSend() == false;
|
||||
bool isGroup = Provider.of<ContactInfoState>(context).isGroup;
|
||||
var showToolbar = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
||||
var charLength = ctrlrCompose.value.text.characters.length;
|
||||
var expectedLength = ctrlrCompose.value.text.length;
|
||||
var charLength = Provider.of<ContactInfoState>(context).messageDraft.messageText.characters.length;
|
||||
var expectedLength = Provider.of<ContactInfoState>(context).messageDraft.messageText.length;
|
||||
var numberOfBytesMoreThanChar = (expectedLength - charLength);
|
||||
|
||||
var bold = IconButton(
|
||||
|
@ -495,12 +491,14 @@ class _MessageViewState extends State<MessageView> {
|
|||
tooltip: AppLocalizations.of(context)!.tooltipBoldText,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
|
||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
||||
var selection = ctrlrCompose.selection;
|
||||
var start = ctrlrCompose.selection.start;
|
||||
var end = ctrlrCompose.selection.end;
|
||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "**" + selected + "**");
|
||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 2, extentOffset: selection.start + 2);
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -509,12 +507,14 @@ class _MessageViewState extends State<MessageView> {
|
|||
tooltip: AppLocalizations.of(context)!.tooltipItalicize,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
|
||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
||||
var selection = ctrlrCompose.selection;
|
||||
var start = ctrlrCompose.selection.start;
|
||||
var end = ctrlrCompose.selection.end;
|
||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "*" + selected + "*");
|
||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -523,12 +523,14 @@ class _MessageViewState extends State<MessageView> {
|
|||
tooltip: AppLocalizations.of(context)!.tooltipCode,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
|
||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
||||
var selection = ctrlrCompose.selection;
|
||||
var start = ctrlrCompose.selection.start;
|
||||
var end = ctrlrCompose.selection.end;
|
||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "`" + selected + "`");
|
||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -537,12 +539,14 @@ class _MessageViewState extends State<MessageView> {
|
|||
tooltip: AppLocalizations.of(context)!.tooltipSuperscript,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
|
||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
||||
var selection = ctrlrCompose.selection;
|
||||
var start = ctrlrCompose.selection.start;
|
||||
var end = ctrlrCompose.selection.end;
|
||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "^" + selected + "^");
|
||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -551,12 +555,14 @@ class _MessageViewState extends State<MessageView> {
|
|||
tooltip: AppLocalizations.of(context)!.tooltipSubscript,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
|
||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
||||
var selection = ctrlrCompose.selection;
|
||||
var start = ctrlrCompose.selection.start;
|
||||
var end = ctrlrCompose.selection.end;
|
||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "_" + selected + "_");
|
||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -565,12 +571,14 @@ class _MessageViewState extends State<MessageView> {
|
|||
tooltip: AppLocalizations.of(context)!.tooltipStrikethrough,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
|
||||
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
|
||||
var selection = ctrlrCompose.selection;
|
||||
var start = ctrlrCompose.selection.start;
|
||||
var end = ctrlrCompose.selection.end;
|
||||
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "~~" + selected + "~~");
|
||||
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 2, extentOffset: selection.start + 2);
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -600,7 +608,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
padding: EdgeInsets.all(8),
|
||||
child: TextFormField(
|
||||
key: Key('txtCompose'),
|
||||
controller: ctrlrCompose,
|
||||
controller: Provider.of<ContactInfoState>(context).messageDraft.ctrlCompose,
|
||||
focusNode: focusNode,
|
||||
autofocus: !Platform.isAndroid,
|
||||
textInputAction: TextInputAction.newline,
|
||||
|
@ -615,18 +623,17 @@ class _MessageViewState extends State<MessageView> {
|
|||
style: TextStyle(
|
||||
fontFamily: "Inter",
|
||||
fontSize: 12.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontWeight: FontWeight.w300,
|
||||
),
|
||||
enabled: true, // always allow editing...
|
||||
|
||||
onChanged: (String x) {
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.messageText = x;
|
||||
setState(() {
|
||||
// we need to force a rerender here to update the max length count
|
||||
});
|
||||
},
|
||||
decoration: InputDecoration(
|
||||
hintText: isOffline ? "" : AppLocalizations.of(context)!.placeholderEnterMessage,
|
||||
hintText: AppLocalizations.of(context)!.placeholderEnterMessage,
|
||||
hintStyle: TextStyle(fontFamily: "Inter", fontSize: 10.0 * Provider.of<Settings>(context).fontScaling, color: Provider.of<Settings>(context).theme.sendHintTextColor),
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
|
@ -635,13 +642,13 @@ class _MessageViewState extends State<MessageView> {
|
|||
key: Key("btnSend"),
|
||||
style: ElevatedButton.styleFrom(padding: EdgeInsets.all(0.0), shape: new RoundedRectangleBorder(borderRadius: new BorderRadius.circular(45.0))),
|
||||
child: Tooltip(
|
||||
message: isOffline
|
||||
message: cannotSend
|
||||
? (isGroup ? AppLocalizations.of(context)!.serverNotSynced : AppLocalizations.of(context)!.peerOfflineMessage)
|
||||
: (isGroup && Provider.of<ContactInfoState>(context, listen: false).antispamTickets == 0)
|
||||
? AppLocalizations.of(context)!.acquiringTicketsFromServer
|
||||
: AppLocalizations.of(context)!.sendMessage,
|
||||
child: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.defaultButtonTextColor)),
|
||||
onPressed: isOffline || (isGroup && Provider.of<ContactInfoState>(context, listen: false).antispamTickets == 0) ? null : _sendMessage,
|
||||
onPressed: cannotSend || (isGroup && Provider.of<ContactInfoState>(context, listen: false).antispamTickets == 0) ? null : _sendMessage,
|
||||
))),
|
||||
)));
|
||||
|
||||
|
@ -727,7 +734,8 @@ class _MessageViewState extends State<MessageView> {
|
|||
if (event is RawKeyUpEvent) {
|
||||
if ((data.logicalKey == LogicalKeyboardKey.enter && !event.isShiftPressed) || data.logicalKey == LogicalKeyboardKey.numpadEnter && !event.isShiftPressed) {
|
||||
// Don't send when inserting a new line that is not at the end of the message
|
||||
if (ctrlrCompose.selection.baseOffset != ctrlrCompose.text.length) {
|
||||
if (Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose.selection.baseOffset !=
|
||||
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose.text.length) {
|
||||
return;
|
||||
}
|
||||
_sendMessage();
|
||||
|
@ -735,8 +743,6 @@ class _MessageViewState extends State<MessageView> {
|
|||
}
|
||||
}
|
||||
|
||||
void placeHolder() => {};
|
||||
|
||||
// explicitly passing BuildContext ctx here is important, change at risk to own health
|
||||
// otherwise some Providers will become inaccessible to subwidgets...?
|
||||
// https://stackoverflow.com/a/63818697
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cwtch/themes/opaque.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../settings.dart';
|
||||
|
@ -18,7 +19,7 @@ class _CwtchLabelState extends State<CwtchLabel> {
|
|||
return Consumer<Settings>(builder: (context, theme, child) {
|
||||
return Text(
|
||||
widget.label,
|
||||
style: TextStyle(fontSize: 20, color: theme.current().mainTextColor),
|
||||
style: Provider.of<Settings>(context).scaleFonts(defaultFormLabelTextStyle),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,7 +7,9 @@ import 'package:cwtch/models/contact.dart';
|
|||
import 'package:cwtch/models/filedownloadprogress.dart';
|
||||
import 'package:cwtch/models/message.dart';
|
||||
import 'package:cwtch/models/profile.dart';
|
||||
import 'package:cwtch/themes/opaque.dart';
|
||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||
import 'package:cwtch/widgets/messageBubbleWidgetHelpers.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -147,18 +149,10 @@ class FileBubbleState extends State<FileBubble> {
|
|||
var wdgSender = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: Container(
|
||||
height: 14 * Provider.of<Settings>(context).fontScaling,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(),
|
||||
child: SelectableText(senderDisplayStr + '\u202F',
|
||||
style: TextStyle(
|
||||
fontSize: 9.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: "Inter",
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor))));
|
||||
height: 14 * Provider.of<Settings>(context).fontScaling, clipBehavior: Clip.hardEdge, decoration: BoxDecoration(), child: compileSenderWidget(context, fromMe, senderDisplayStr)));
|
||||
var isPreview = false;
|
||||
var wdgMessage = !showFileSharing
|
||||
? Text(AppLocalizations.of(context)!.messageEnableFileSharing)
|
||||
? Text(AppLocalizations.of(context)!.messageEnableFileSharing, style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle))
|
||||
: fromMe
|
||||
? senderFileChrome(AppLocalizations.of(context)!.messageFileSent, widget.nameSuggestion, widget.rootHash, widget.fileSize)
|
||||
: (fileChrome(AppLocalizations.of(context)!.messageFileOffered + ":", widget.nameSuggestion, widget.rootHash, widget.fileSize,
|
||||
|
@ -182,11 +176,13 @@ class FileBubbleState extends State<FileBubble> {
|
|||
},
|
||||
)));
|
||||
} else {
|
||||
wdgDecorations = Visibility(visible: widget.interactive, child: Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F'));
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive, child: Text(AppLocalizations.of(context)!.fileSavedTo + ': ' + path + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle)));
|
||||
}
|
||||
} else if (downloadActive) {
|
||||
if (!downloadGotManifest) {
|
||||
wdgDecorations = Visibility(visible: widget.interactive, child: Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F'));
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive, child: Text(AppLocalizations.of(context)!.retrievingManifestMessage + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle)));
|
||||
} else {
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive,
|
||||
|
@ -199,19 +195,19 @@ class FileBubbleState extends State<FileBubble> {
|
|||
// in this case, the download was done in a previous application launch,
|
||||
// so we probably have to request an info lookup
|
||||
if (!downloadInterrupted) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F');
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.fileCheckingStatus + '...' + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle));
|
||||
// We should have already requested this...
|
||||
} else {
|
||||
var path = Provider.of<ProfileInfoState>(context).downloadFinalPath(widget.fileKey()) ?? "";
|
||||
wdgDecorations = Visibility(
|
||||
visible: widget.interactive,
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F'),
|
||||
ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton))
|
||||
Text(AppLocalizations.of(context)!.fileInterrupted + ': ' + path + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle)),
|
||||
ElevatedButton(onPressed: _btnResume, child: Text(AppLocalizations.of(context)!.verfiyResumeButton, style: Provider.of<Settings>(context).scaleFonts(defaultTextButtonStyle)))
|
||||
]));
|
||||
}
|
||||
} else if (!senderIsContact) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.msgAddToAccept);
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.msgAddToAccept, style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle));
|
||||
} else if (!widget.isAuto || Provider.of<MessageMetadata>(context).attributes["file-missing"] == "false") {
|
||||
//Note: we need this second case to account for scenarios where a user deletes the downloaded file, we won't automatically
|
||||
// fetch it again, so we need to offer the user the ability to restart..
|
||||
|
@ -220,7 +216,10 @@ class FileBubbleState extends State<FileBubble> {
|
|||
child: Center(
|
||||
widthFactor: 1,
|
||||
child: Wrap(children: [
|
||||
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F'), onPressed: _btnAccept)),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.downloadFileButton + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)),
|
||||
])));
|
||||
} else {
|
||||
wdgDecorations = Container();
|
||||
|
@ -278,7 +277,7 @@ class FileBubbleState extends State<FileBubble> {
|
|||
}
|
||||
} else {
|
||||
try {
|
||||
selectedFileName = await FilePicker.platform.saveFile(
|
||||
selectedFileName = await FilePicker.platform.saveFile(
|
||||
fileName: widget.nameSuggestion,
|
||||
lockParentWindow: true,
|
||||
);
|
||||
|
@ -308,52 +307,35 @@ class FileBubbleState extends State<FileBubble> {
|
|||
|
||||
// Construct an file chrome for the sender
|
||||
Widget senderFileChrome(String chrome, String fileName, String rootHash, int fileSize) {
|
||||
var settings = Provider.of<Settings>(context);
|
||||
return ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
title: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.start, children: [
|
||||
SelectableText(
|
||||
chrome + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Inter",
|
||||
fontSize: 12 * Provider.of<Settings>(context).fontScaling,
|
||||
),
|
||||
style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromMeTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
),
|
||||
SelectableText(
|
||||
fileName + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontFamily: "Inter",
|
||||
fontSize: 12 * Provider.of<Settings>(context).fontScaling,
|
||||
),
|
||||
style:
|
||||
settings.scaleFonts(defaultMessageTextStyle.copyWith(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold, color: Provider.of<Settings>(context).theme.messageFromMeTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.parent,
|
||||
maxLines: 2,
|
||||
),
|
||||
SelectableText(
|
||||
prettyBytes(fileSize) + '\u202F' + '\n',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
fontSize: 10 * Provider.of<Settings>(context).fontScaling,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
style: settings.scaleFonts(defaultSmallTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromMeTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
)
|
||||
]),
|
||||
subtitle: SelectableText(
|
||||
'sha512: ' + rootHash + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
fontSize: 10 * Provider.of<Settings>(context).fontScaling,
|
||||
fontFamily: "RobotoMono",
|
||||
),
|
||||
style: settings.scaleFonts(defaultSmallTextStyle.copyWith(fontFamily: "RobotoMono", color: Provider.of<Settings>(context).theme.messageFromMeTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 4,
|
||||
textWidthBasis: TextWidthBasis.parent,
|
||||
|
@ -363,50 +345,35 @@ class FileBubbleState extends State<FileBubble> {
|
|||
|
||||
// Construct an file chrome
|
||||
Widget fileChrome(String chrome, String fileName, String rootHash, int fileSize, String speed) {
|
||||
var settings = Provider.of<Settings>(context);
|
||||
return ListTile(
|
||||
visualDensity: VisualDensity.compact,
|
||||
title: Wrap(direction: Axis.horizontal, alignment: WrapAlignment.start, children: [
|
||||
SelectableText(
|
||||
chrome + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
fontSize: 12 * Provider.of<Settings>(context).fontScaling,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
),
|
||||
SelectableText(
|
||||
fileName + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
fontFamily: "Inter",
|
||||
),
|
||||
style: settings
|
||||
.scaleFonts(defaultMessageTextStyle.copyWith(overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold, color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.parent,
|
||||
maxLines: 2,
|
||||
),
|
||||
SelectableText(
|
||||
AppLocalizations.of(context)!.labelFilesize + ': ' + prettyBytes(fileSize) + '\u202F' + '\n',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
fontFamily: "Inter",
|
||||
fontSize: 10 * Provider.of<Settings>(context).fontScaling,
|
||||
),
|
||||
style: settings.scaleFonts(defaultSmallTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
)
|
||||
]),
|
||||
subtitle: SelectableText(
|
||||
'sha512: ' + rootHash + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
fontSize: 10 * Provider.of<Settings>(context).fontScaling,
|
||||
fontFamily: "RobotoMono",
|
||||
),
|
||||
style: settings.scaleFonts(defaultSmallTextStyle.copyWith(fontFamily: "RobotoMono", color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 4,
|
||||
textWidthBasis: TextWidthBasis.parent,
|
||||
|
@ -416,11 +383,7 @@ class FileBubbleState extends State<FileBubble> {
|
|||
visible: speed != "0 B/s",
|
||||
child: SelectableText(
|
||||
speed + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
fontFamily: "Inter",
|
||||
fontSize: 10 * Provider.of<Settings>(context).fontScaling,
|
||||
),
|
||||
style: settings.scaleFonts(defaultSmallTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 1,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:cwtch/cwtch_icons_icons.dart';
|
|||
import 'package:cwtch/models/contact.dart';
|
||||
import 'package:cwtch/models/message.dart';
|
||||
import 'package:cwtch/models/profile.dart';
|
||||
import 'package:cwtch/themes/opaque.dart';
|
||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -13,6 +14,7 @@ import 'package:intl/intl.dart';
|
|||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import '../settings.dart';
|
||||
import 'messageBubbleWidgetHelpers.dart';
|
||||
import 'messagebubbledecorations.dart';
|
||||
|
||||
// Like MessageBubble but for displaying chat overlay 100/101 invitations
|
||||
|
@ -54,15 +56,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
|||
}
|
||||
}
|
||||
|
||||
var wdgSender = Center(
|
||||
widthFactor: 1,
|
||||
child: SelectableText(senderDisplayStr + '\u202F',
|
||||
style: TextStyle(
|
||||
fontSize: 9.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: "Inter",
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor)));
|
||||
|
||||
var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr);
|
||||
// If we receive an invite for ourselves, treat it as a bug. The UI no longer allows this so it could have only come from
|
||||
// some kind of malfeasance.
|
||||
var selfInvite = widget.inviteNick == Provider.of<ProfileInfoState>(context).onion;
|
||||
|
@ -71,7 +65,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
|||
}
|
||||
|
||||
var wdgMessage = isGroup && !showGroupInvite
|
||||
? Text(AppLocalizations.of(context)!.groupInviteSettingsWarning)
|
||||
? Text(AppLocalizations.of(context)!.groupInviteSettingsWarning, style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle))
|
||||
: fromMe
|
||||
? senderInviteChrome(
|
||||
AppLocalizations.of(context)!.sendAnInvitation, isGroup ? Provider.of<ProfileInfoState>(context).contactList.findContact(widget.inviteTarget)!.nickname : widget.inviteTarget)
|
||||
|
@ -83,15 +77,21 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
|||
} else if (fromMe) {
|
||||
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, messageDate: messageDate);
|
||||
} else if (isAccepted) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F');
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle));
|
||||
} else if (this.rejected) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F');
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle));
|
||||
} else {
|
||||
wdgDecorations = Center(
|
||||
widthFactor: 1,
|
||||
child: Wrap(children: [
|
||||
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.rejectGroupBtn + '\u202F'), onPressed: _btnReject)),
|
||||
Padding(padding: EdgeInsets.all(5), child: ElevatedButton(child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F'), onPressed: _btnAccept)),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.rejectGroupBtn + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnReject)),
|
||||
Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)),
|
||||
]));
|
||||
}
|
||||
|
||||
|
@ -149,21 +149,19 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
|||
|
||||
// Construct an invite chrome for the sender
|
||||
Widget senderInviteChrome(String chrome, String targetName) {
|
||||
var settings = Provider.of<Settings>(context);
|
||||
|
||||
return Wrap(children: [
|
||||
SelectableText(
|
||||
chrome + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
),
|
||||
style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromMeTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
),
|
||||
SelectableText(
|
||||
targetName + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
),
|
||||
style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromMeTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
|
@ -173,19 +171,19 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
|||
|
||||
// Construct an invite chrome
|
||||
Widget inviteChrome(String chrome, String targetName, String targetId) {
|
||||
var settings = Provider.of<Settings>(context);
|
||||
|
||||
return Wrap(children: [
|
||||
SelectableText(
|
||||
chrome + '\u202F',
|
||||
style: TextStyle(
|
||||
color: Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
),
|
||||
style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
maxLines: 2,
|
||||
),
|
||||
SelectableText(
|
||||
targetName + '\u202F',
|
||||
style: TextStyle(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor),
|
||||
style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../controllers/open_link_modal.dart';
|
||||
import '../settings.dart';
|
||||
import '../themes/opaque.dart';
|
||||
import '../third_party/linkify/flutter_linkify.dart';
|
||||
|
||||
Widget compileSenderWidget(BuildContext context, bool fromMe, String senderDisplayStr) {
|
||||
return Container(
|
||||
height: 14 * Provider.of<Settings>(context).fontScaling,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(),
|
||||
child: SelectableText(senderDisplayStr,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 9.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: "Inter",
|
||||
overflow: TextOverflow.clip,
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
)));
|
||||
}
|
||||
|
||||
Widget compileMessageContentWidget(BuildContext context, bool fromMe, String content, FocusNode focus, bool formatMessages, bool showClickableLinks) {
|
||||
return SelectableLinkify(
|
||||
text: content + '\u202F',
|
||||
// TODO: onOpen breaks the "selectable" functionality. Maybe something to do with gesture handler?
|
||||
options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
|
||||
linkifiers: [UrlLinkifier()],
|
||||
onOpen: showClickableLinks
|
||||
? (link) {
|
||||
modalOpenLink(context, link);
|
||||
}
|
||||
: null,
|
||||
//key: Key(myKey),
|
||||
focusNode: focus,
|
||||
style: Provider.of<Settings>(context)
|
||||
.scaleFonts(defaultMessageTextStyle.copyWith(color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
linkStyle: Provider.of<Settings>(context)
|
||||
.scaleFonts(defaultMessageTextStyle.copyWith(color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
|
||||
codeStyle: Provider.of<Settings>(context).scaleFonts(defaultMessageTextStyle.copyWith(
|
||||
fontFamily: "RobotoMono",
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor : Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
backgroundColor: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor)),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
);
|
||||
}
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
import 'package:cwtch/controllers/open_link_modal.dart';
|
||||
import 'package:cwtch/models/contact.dart';
|
||||
import 'package:cwtch/models/message.dart';
|
||||
import 'package:cwtch/themes/opaque.dart';
|
||||
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
|
||||
import 'package:cwtch/models/profile.dart';
|
||||
import 'package:cwtch/widgets/malformedbubble.dart';
|
||||
|
@ -10,6 +11,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../settings.dart';
|
||||
import 'messageBubbleWidgetHelpers.dart';
|
||||
import 'messagebubbledecorations.dart';
|
||||
|
||||
class MessageBubble extends StatefulWidget {
|
||||
|
@ -42,56 +44,9 @@ class MessageBubbleState extends State<MessageBubble> {
|
|||
senderDisplayStr = Provider.of<MessageMetadata>(context).senderHandle;
|
||||
}
|
||||
}
|
||||
var wdgSender = Container(
|
||||
height: 14 * Provider.of<Settings>(context).fontScaling,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(),
|
||||
child: SelectableText(senderDisplayStr,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 9.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: "Inter",
|
||||
overflow: TextOverflow.clip,
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
)));
|
||||
|
||||
var wdgMessage = SelectableLinkify(
|
||||
text: widget.content + '\u202F',
|
||||
// TODO: onOpen breaks the "selectable" functionality. Maybe something to do with gesture handler?
|
||||
options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
|
||||
linkifiers: [UrlLinkifier()],
|
||||
onOpen: showClickableLinks
|
||||
? (link) {
|
||||
modalOpenLink(context, link);
|
||||
}
|
||||
: null,
|
||||
//key: Key(myKey),
|
||||
focusNode: _focus,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Inter",
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
),
|
||||
linkStyle: TextStyle(
|
||||
fontSize: 12.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Inter",
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor),
|
||||
codeStyle: TextStyle(
|
||||
fontSize: 12.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Inter",
|
||||
// note: these colors are flipped
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor : Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
backgroundColor: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
);
|
||||
|
||||
var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr);
|
||||
var wdgMessage = compileMessageContentWidget(context, fromMe, widget.content, _focus, formatMessages, showClickableLinks);
|
||||
var wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, messageDate: messageDate);
|
||||
|
||||
var error = Provider.of<MessageMetadata>(context).error;
|
||||
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:cwtch/models/appstate.dart';
|
|||
import 'package:cwtch/models/contact.dart';
|
||||
import 'package:cwtch/models/message.dart';
|
||||
import 'package:cwtch/models/profile.dart';
|
||||
import 'package:cwtch/themes/opaque.dart';
|
||||
import 'package:cwtch/third_party/base32/base32.dart';
|
||||
import 'package:cwtch/views/contactsview.dart';
|
||||
import 'package:cwtch/widgets/staticmessagebubble.dart';
|
||||
|
@ -307,7 +308,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
|
|||
bottomRight: Radius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Padding(padding: EdgeInsets.all(9.0), child: Text(AppLocalizations.of(context)!.newMessagesLabel)));
|
||||
child: Padding(padding: EdgeInsets.all(9.0), child: Text(AppLocalizations.of(context)!.newMessagesLabel, style: Provider.of<Settings>(context).scaleFonts(defaultTextButtonStyle))));
|
||||
}
|
||||
|
||||
void _runAnimation(Offset pixelsPerSecond, Size size) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import '../settings.dart';
|
||||
import 'messageBubbleWidgetHelpers.dart';
|
||||
import 'messagebubbledecorations.dart';
|
||||
|
||||
class QuotedMessageBubble extends StatefulWidget {
|
||||
|
@ -43,53 +44,11 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
|||
}
|
||||
}
|
||||
|
||||
var wdgSender = Container(
|
||||
height: 14 * Provider.of<Settings>(context).fontScaling,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(),
|
||||
child: SelectableText(senderDisplayStr,
|
||||
style: TextStyle(
|
||||
fontSize: 9.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: "Inter",
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor)));
|
||||
var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr);
|
||||
|
||||
var showClickableLinks = Provider.of<Settings>(context).isExperimentEnabled(ClickableLinksExperiment);
|
||||
var formatMessages = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
|
||||
|
||||
var wdgMessage = SelectableLinkify(
|
||||
text: widget.body + '\u202F',
|
||||
// TODO: onOpen breaks the "selectable" functionality. Maybe something to do with gesture handler?
|
||||
options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
|
||||
linkifiers: [UrlLinkifier()],
|
||||
onOpen: showClickableLinks
|
||||
? (link) {
|
||||
modalOpenLink(context, link);
|
||||
}
|
||||
: null,
|
||||
//key: Key(myKey),
|
||||
focusNode: _focus,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Inter",
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor,
|
||||
),
|
||||
linkStyle: TextStyle(
|
||||
fontSize: 12.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "Inter",
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeTextColor : Provider.of<Settings>(context).theme.messageFromOtherTextColor),
|
||||
codeStyle: TextStyle(
|
||||
fontSize: 12.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: "RobotoMono",
|
||||
// note: these colors are flipped
|
||||
color: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherTextColor : Provider.of<Settings>(context).theme.messageFromMeTextColor,
|
||||
backgroundColor: fromMe ? Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor : Provider.of<Settings>(context).theme.messageFromMeBackgroundColor),
|
||||
textAlign: TextAlign.left,
|
||||
textWidthBasis: TextWidthBasis.longestLine,
|
||||
);
|
||||
var wdgMessage = compileMessageContentWidget(context, fromMe, widget.body, _focus, formatMessages, showClickableLinks);
|
||||
|
||||
var wdgQuote = FutureBuilder(
|
||||
future: widget.quotedMessage,
|
||||
|
@ -118,7 +77,7 @@ class QuotedMessageBubbleState extends State<QuotedMessageBubble> {
|
|||
|
||||
var wdgReplyingTo = SelectableText(
|
||||
AppLocalizations.of(context)!.replyingTo.replaceAll("%1", qMessageSender),
|
||||
style: TextStyle(fontSize: 10, color: qTextColor.withOpacity(0.8)),
|
||||
style: Provider.of<Settings>(context).scaleFonts(TextStyle(fontSize: 10, color: qTextColor.withOpacity(0.8))),
|
||||
);
|
||||
// Swap the background color for quoted tweets..
|
||||
return MouseRegion(
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../settings.dart';
|
||||
import 'messageBubbleWidgetHelpers.dart';
|
||||
import 'messagebubbledecorations.dart';
|
||||
|
||||
class StaticMessageBubble extends StatefulWidget {
|
||||
|
@ -40,12 +41,7 @@ class StaticMessageBubbleState extends State<StaticMessageBubble> {
|
|||
senderDisplayStr = widget.profile.nickname;
|
||||
}
|
||||
|
||||
var wdgSender = SelectableText(senderDisplayStr,
|
||||
style: TextStyle(
|
||||
fontSize: 9.0 * Provider.of<Settings>(context).fontScaling,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: "Inter",
|
||||
color: fromMe ? widget.settings.theme.messageFromMeTextColor : widget.settings.theme.messageFromOtherTextColor));
|
||||
var wdgSender = compileSenderWidget(context, fromMe, senderDisplayStr);
|
||||
|
||||
var wdgDecorations = MessageBubbleDecoration(ackd: widget.metadata.ackd, errored: widget.metadata.error, fromMe: fromMe, messageDate: messageDate);
|
||||
|
||||
|
|
Loading…
Reference in New Issue