Merge pull request 'Ongoing UI and Android Fixes on top of depenencies.' (#678) from ui-many-fixes into dep-upgrades-3.10.0
continuous-integration/drone/pr Build is passing Details

Reviewed-on: #678
This commit is contained in:
Sarah Jamie Lewis 2023-06-02 19:57:58 +00:00
commit b183966980
61 changed files with 1640 additions and 458 deletions

View File

@ -1 +1 @@
2023-05-09-13-30-v0.0.3-24-g5b2f3cf
2023-05-16-16-25-v0.0.4

View File

@ -84,6 +84,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
Log.i(TAG, "startCwtch success, starting coroutine AppbusEvent loop...")
val downloadIDs = mutableMapOf<String, Int>()
val downloadFinishedIDs = mutableMapOf<String, Int>()
var flags = PendingIntent.FLAG_UPDATE_CURRENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
flags = flags or PendingIntent.FLAG_IMMUTABLE
@ -167,32 +168,36 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
val title = data.getString("NameSuggestion");
val progress = data.getString("Progress").toInt();
val progressMax = data.getString("FileSizeInChunks").toInt();
if (!downloadIDs.containsKey(fileKey)) {
downloadIDs.put(fileKey, downloadIDs.count());
}
var dlID = downloadIDs.get(fileKey);
if (dlID == null) {
dlID = 0;
}
if (progress >= 0) {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createDownloadNotificationChannel(fileKey, fileKey)
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
};
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setOngoing(true)
.setContentTitle("Downloading")//todo: translate
.setContentText(title)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setProgress(progressMax, progress, false)
.setSound(null)
//.setSilent(true)
.build();
notificationManager.notify(dlID, newNotification);
// if we have seen a download finished update for this key then ignore it
if (!downloadFinishedIDs.containsKey(fileKey)) {
if (!downloadIDs.containsKey(fileKey)) {
downloadIDs.put(fileKey, downloadIDs.count());
}
var dlID = downloadIDs.get(fileKey);
if (dlID == null) {
dlID = 0;
}
if (progress >= 0) {
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createDownloadNotificationChannel(fileKey, fileKey)
} else {
// If earlier version channel ID is not used
// https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context)
""
};
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setOngoing(true)
.setContentTitle("Downloading")//todo: translate
.setContentText(title)
.setSmallIcon(android.R.drawable.stat_sys_download)
.setProgress(progressMax, progress, false)
.setSound(null)
//.setSilent(true)
.build();
notificationManager.notify(dlID, newNotification);
}
}
} catch (e: Exception) {
Log.d("FlwtchWorker->FileDownloadProgressUpdate", e.toString() + " :: " + e.getStackTrace());
@ -216,6 +221,8 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
Files.delete(sourcePath);
}
}
// Suppress future notifications...
downloadFinishedIDs.put(fileKey, downloadIDs.count());
if (downloadIDs.containsKey(fileKey)) {
notificationManager.cancel(downloadIDs.get(fileKey) ?: 0);
}

View File

@ -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()

View File

@ -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);
}
},

View File

@ -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

View File

@ -68,25 +68,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 +391,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") {

View File

@ -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

View File

@ -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

View File

@ -1,6 +1,10 @@
{
"@@locale": "cy",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Testun maint rhagosodedig (ffactor graddfa:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "da",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Standard størrelse tekst (skaleringsfaktor:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "de",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Starten oder Stoppen des Profils",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Text in Standardgröße (Skalierungsfaktor:",
"localeJa": "Japanese \/ 日本語",
@ -26,7 +30,6 @@
"profileAutostartLabel": "Autostart",
"profileEnabled": "Aktivieren",
"profileAutostartDescription": "Legt fest, ob das Profil beim Starten automatisch gestartet wird",
"profileEnabledDescription": "Starten oder Stoppen des Profils",
"acquiringTicketsFromServer": "Antispam-Herausforderung meistern",
"acquiredTicketsFromServer": "Antispam-Herausforderung abgeschlossen",
"shareProfileMenuTooltop": "Profil teilen über...",

View File

@ -1,6 +1,10 @@
{
"@@locale": "el",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Font Scaling",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "en",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Font Scaling",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "es",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Tamaño predeterminado de texto (factor de escala:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "fr",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Taille par défaut du texte (échelle:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "it",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Testo di dimensioni predefinite (fattore di scala:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "ja",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Font Scaling",
"localeJa": "Japanese \/ 日本語",
@ -64,7 +68,6 @@
"profileInfoHint": "ブログ、ウェブサイト、簡単な経歴など、ご自身に関する公開情報をここに追加してください。",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileEnabled": "Enable",
"localeNl": "Dutch \/ Dutch",
"experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes",

View File

@ -1,6 +1,10 @@
{
"@@locale": "ko",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "프로필 시각 또는 중지",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Font Scaling",
"localeJa": "Japanese \/ 日本語",
@ -124,7 +128,6 @@
"profileAutostartDescription": "시작 시 프로필이 자동으로 시작되는지 여부를 제어합니다.",
"profileAutostartLabel": "자동 시작",
"profileEnabled": "허락",
"profileEnabledDescription": "프로필 시각 또는 중지",
"replyingTo": "%1에 회신",
"tooltipCode": "Code \/ Monospace",
"tooltipStrikethrough": "Strikethrough",

View File

@ -1,6 +1,10 @@
{
"@@locale": "lb",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Font Scaling",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "nl",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Start of stop het profiel",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Standaardtekstgrootte (schaalfactor:",
"localeJa": "Japanese \/ 日本語",
@ -25,7 +29,6 @@
"profileAutostartDescription": "Regelt of het profiel automatisch wordt gestart bij het opstarten",
"profileAutostartLabel": "Automatisch starten",
"profileEnabled": "Inschakelen",
"profileEnabledDescription": "Start of stop het profiel",
"localeSk": "Slowaaks \/ Slovák",
"localePtBr": "Braziliaans Portugees \/ Português do Brasil",
"acquiredTicketsFromServer": "Anti-spam uitdaging voltooid",

View File

@ -1,6 +1,10 @@
{
"@@locale": "no",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Standard tekststørrelse (skaleringsfaktor:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "pl",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Domyślny rozmiar tekstu (skalowanie:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "pt",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Texto tamanho padrão (fator de escala: ",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "pt_BR",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Texto tamanho padrão (fator de escala: ",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "ro",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Dimensiunea implicită a textului (factor de scară:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "ru",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Размер текста по умолчанию (коэффициент масштабирования:",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Start or stop the profile",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",

View File

@ -1,6 +1,10 @@
{
"@@locale": "sk",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Spustiť alebo zastaviť profil",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Predvolená veľkosť textu (mierka):",
"localeJa": "Japanese \/ 日本語",
@ -383,6 +387,5 @@
"createGroupBtn": " Vytvoriť",
"defaultGroupName": "Úžasná Skupina",
"createGroupTitle": "Vytvoriť Skupinu",
"profileEnabled": "Povoliť",
"profileEnabledDescription": "Spustiť alebo zastaviť profil"
"profileEnabled": "Povoliť"
}

391
lib/l10n/intl_sv.arb Normal file
View File

@ -0,0 +1,391 @@
{
"@@locale": "sv",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainska \/ українською",
"profileEnabledDescription": "Aktivera eller inaktivera profilen",
"localeSw": "Swahili \/ Kiswahili",
"createGroupTitle": "Skapa grupp",
"serverLabel": "Server",
"defaultGroupName": "Fantastisk grupp",
"createGroupBtn": "Skapa",
"profileOnionLabel": "Skicka den här adressen till personer du vill ansluta till",
"addPeerTab": "Lägg till en kontakt",
"createGroupTab": "Skapa en grupp",
"joinGroupTab": "Gå med i en grupp",
"peerAddress": "Adress",
"peerName": "Namn",
"server": "Server",
"invitation": "Inbjudan",
"groupAddr": "Adress",
"addPeer": "Lägg till kontakt",
"createGroup": "Skapa grupp",
"joinGroup": "Gå med i grupp",
"newBulletinLabel": "Ny rapport",
"postNewBulletinLabel": "Lägg upp ny rapport",
"titlePlaceholder": "titel...",
"pasteAddressToAddContact": "Klistra in en cwtch-adress, inbjudan eller nyckelpaket här för att lägga till en ny konversation",
"blocked": "Blockerad",
"search": "Sök...",
"invitationLabel": "Inbjudan",
"serverInfo": "Server info",
"serverConnectivityConnected": "Server ansluten",
"serverConnectivityDisconnected": "Server frånkopplad",
"serverSynced": "Synkroniserad",
"serverNotSynced": "Synkroniserar nya meddelanden (det kan ta lite tid)...",
"viewServerInfo": "Server info",
"groupNameLabel": "Gruppens namn",
"saveBtn": "Spara",
"inviteToGroupLabel": "Bjud in till grupp",
"inviteBtn": "Bjud in",
"deleteBtn": "Ta bort",
"update": "Uppdatera",
"searchList": "Söklista",
"peerNotOnline": "Kontakten är offline. Program kan inte användas just nu.",
"addListItemBtn": "Lägg till",
"membershipDescription": "Nedan finns en lista över användare som har skickat meddelanden till gruppen. Den kanske inte innehåller alla användare som har åtkomst till gruppen dock.",
"dmTooltip": "Klicka för DM",
"couldNotSendMsgError": "Det gick inte att skicka detta meddelande",
"acknowledgedLabel": "Bekräftad",
"pendingLabel": "Väntar",
"peerBlockedMessage": "Kontakten är blockerad",
"peerOfflineMessage": "Kontakten är offline, meddelanden kan inte levereras just nu",
"copyBtn": "Kopiera",
"newGroupBtn": "Skapa ny grupp",
"acceptGroupInviteLabel": "Vill du acceptera inbjudan till",
"acceptGroupBtn": "Acceptera",
"rejectGroupBtn": "Avvisa",
"chatBtn": "Chatt",
"bulletinsBtn": "Rapporter",
"listsBtn": "Listor",
"puzzleGameBtn": "Pussel",
"addressLabel": "Adress",
"copiedToClipboardNotification": "Kopierat till urklipp",
"displayNameLabel": "Visningsnamn",
"blockBtn": "Blockera kontakt",
"savePeerHistory": "Spara historik",
"savePeerHistoryDescription": "Avgör om historik som är kopplad till kontakten ska tas bort.",
"dontSavePeerHistory": "Rensa historik",
"unblockBtn": "Avblockera kontakt",
"editProfileTitle": "Redigera profil",
"addProfileTitle": "Lägg till en ny profil",
"profileName": "Visningsnamn",
"defaultProfileName": "Alice",
"newProfile": "Ny profil",
"editProfile": "Redigera profil",
"radioUsePassword": "Lösenord",
"radioNoPassword": "Okrypterad (inget lösenord)",
"noPasswordWarning": "Att inte använda ett lösenord på detta konto innebär att all data som lagras lokalt är okrypterat",
"yourDisplayName": "Ditt visningsnamn",
"currentPasswordLabel": "Nuvarande lösenord",
"password1Label": "Lösenord",
"password2Label": "Ange lösenordet på nytt",
"passwordErrorEmpty": "Lösenordet kan inte vara tomt",
"createProfileBtn": "Skapa profil",
"saveProfileBtn": "Spara profil",
"passwordErrorMatch": "Lösenorden matchar inte",
"passwordChangeError": "Fel vid ändring av lösenord: Det angivna lösenordet avvisades",
"deleteProfileBtn": "Ta bort profil",
"deleteConfirmLabel": "Skriv DELETE för att bekräfta",
"deleteProfileConfirmBtn": "Ta bort profilen",
"deleteConfirmText": "TA BORT",
"addNewProfileBtn": "Lägg till en ny profil",
"enterProfilePassword": "Ange ett lösenord för att se dina profiler",
"password": "Lösenord",
"error0ProfilesLoadedForPassword": "0 profiler laddade med det lösenordet",
"yourProfiles": "Dina profiler",
"yourServers": "Dina servrar",
"unlock": "Lås upp",
"cwtchSettingsTitle": "Cwtch-inställningar",
"versionBuilddate": "Version: %1 Byggd: %2",
"zoomLabel": "Gränssnittszoom (påverkar främst storleken på text och knappar)",
"blockUnknownLabel": "Blockera okända kontakter",
"settingLanguage": "Språk",
"localeEn": "Engelska \/ English",
"localeFr": "Franska \/ Français",
"localePt": "Portugisiska \/ Portuguesa",
"localeDe": "Tyska \/ Deutsch",
"settingInterfaceZoom": "Zoomnivå",
"largeTextLabel": "Stor",
"settingTheme": "Använd ljusa teman",
"themeLight": "Ljust",
"themeDark": "Mörkt",
"experimentsEnabled": "Aktivera experimentella funktioner",
"versionTor": "Version %1 med tor %2",
"version": "Version %1",
"builddate": "Byggt: %2",
"defaultScalingText": "Teckensnittsskalning",
"smallTextLabel": "Liten",
"loadingTor": "Laddar tor...",
"viewGroupMembershipTooltip": "Visa gruppmedlemskap",
"networkStatusDisconnected": "Frånkopplad från internet, kontrollera din anslutning",
"networkStatusAttemptingTor": "Försöker ansluta till Tor-nätverket",
"networkStatusConnecting": "Ansluter till nätverk och kontakter...",
"networkStatusOnline": "Uppkopplad",
"newConnectionPaneTitle": "Ny anslutning",
"addListItem": "Lägg till ett nytt listobjekt",
"addNewItem": "Lägg till ett nytt objekt i listan",
"todoPlaceholder": "Att göra...",
"localeEs": "Spanska \/ Español",
"localeIt": "Italienska \/ Italiano",
"enableGroups": "Aktivera gruppchatt",
"enterCurrentPasswordForDelete": "Ange aktuellt lösenord för att ta bort den här profilen.",
"conversationSettings": "Konversationsinställningar",
"invalidImportString": "Ogiltig importsträng",
"contactAlreadyExists": "Kontakten finns redan",
"tooltipOpenSettings": "Öppna inställningsfönstret",
"tooltipAddContact": "Lägg till en ny kontakt eller konversation",
"titleManageContacts": "Konversationer",
"tooltipUnlockProfiles": "Lås upp krypterade profiler genom att ange deras lösenord.",
"titleManageProfiles": "Hantera Cwtch-profiler",
"descriptionExperiments": "Cwtch experimentella funktioner är frivilliga. De utökar funktionaliteten av Cwtch med t.ex. gruppchatt eller integration av botar vilket försämrar säkerhet och integritet jämfört med traditionell 1:1 metadataresistent chatt.",
"descriptionExperimentsGroups": "Grupper tillåter Cwtch att ansluta till opålitlig serverinfrastruktur för att möjliggöra kommunikation med mer än en kontakt.",
"descriptionBlockUnknownConnections": "Om det här alternativet är aktiverat stängs automatiskt anslutningar från Cwtch-användare som inte finns i din kontaktlista.",
"successfullAddedContact": "Har lagts till ",
"titleManageServers": "Hantera servrar",
"inviteToGroup": "Du har blivit inbjuden att gå med i en grupp:",
"leaveConversation": "Lämna denna konversation",
"reallyLeaveThisGroupPrompt": "Är du säker på att du vill lämna den här konversationen? Alla meddelanden och attribut kommer att raderas.",
"yesLeave": "Ja, lämna denna konversation",
"newPassword": "Nytt lösenord",
"chatHistoryDefault": "Den här konversationen kommer att raderas när Cwtch stängs! Meddelandehistorik kan aktiveras per konversation via menyn Inställningar uppe till höger.",
"accepted": "Accepterad!",
"rejected": "Avvisad!",
"contactSuggestion": "Detta är ett kontaktförslag för:",
"sendAnInvitation": "Du skickade en inbjudan till:",
"torStatus": "Tor-status",
"torVersion": "Tor-version",
"resetTor": "Återställ",
"cancel": "Avbryt",
"sendMessage": "Skicka meddelande",
"sendInvite": "Skicka en kontakt- eller gruppinbjudan",
"deleteProfileSuccess": "Profilen har tagits bort",
"addServerFirst": "Du måste lägga till en server innan du kan skapa en grupp",
"nickChangeSuccess": "Profilens smeknamn har ändrats",
"createProfileToBegin": "Skapa eller lås upp en profil för att börja",
"addContactFirst": "Lägg till eller välj en kontakt för att börja chatta.",
"torNetworkStatus": "Tor nätverksstatus",
"debugLog": "Aktivera debug-logg",
"profileDeleteSuccess": "Profilen har tagits bort",
"malformedMessage": "Felaktigt meddelande",
"shutdownCwtchTooltip": "Stäng ner Cwtch",
"shutdownCwtchDialog": "Är du säker på att du vill stänga ner Cwtch? Detta kommer att stänga alla anslutningar och avsluta programmet.",
"shutdownCwtchAction": "Stäng ner Cwtch",
"shutdownCwtchDialogTitle": "Stänga ner Cwtch?",
"groupInviteSettingsWarning": "Du har blivit inbjuden att gå med i en grupp! Aktivera gruppchatt i Inställningar för att se denna inbjudan.",
"tooltipShowPassword": "Visa lösenord",
"tooltipHidePassword": "Dölj lösenordet",
"notificationNewMessageFromPeer": "Nytt meddelande från en kontakt!",
"notificationNewMessageFromGroup": "Nytt meddelande i en grupp!",
"tooltipAcceptContactRequest": "Acceptera kontaktförfrågan.",
"tooltipReplyToThisMessage": "Svara på meddelande",
"tooltipRejectContactRequest": "Neka kontaktförfrågan",
"tooltipRemoveThisQuotedMessage": "Ta bort citerat meddelande.",
"localePl": "Polska \/ Polski",
"settingUIColumnPortrait": "UI-kolumner i stående läge",
"settingUIColumnOptionSame": "Samma som stående läge",
"settingUIColumnLandscape": "UI-kolumner i liggande läge",
"settingUIColumnSingle": "Enkel",
"settingUIColumnDouble12Ratio": "Dubbel (1:2)",
"settingUIColumnDouble14Ratio": "Dubbel (1:4)",
"contactGoto": "Gå till konversation med %1",
"addContact": "Lägg till kontakt",
"addContactConfirm": "Lägg till kontakt %1",
"encryptedProfileDescription": "Genom att kryptera en profil med ett lösenord skyddas den från andra personer som också kan använda den här enheten. Krypterade profiler kan inte dekrypteras, visas eller nås förrän rätt lösenord har angetts för att låsa upp dem.",
"plainProfileDescription": "Vi rekommenderar att du skyddar dina Cwtch-profiler med ett lösenord. Om du inte anger ett lösenord för den här profilen kan alla som har åtkomst till den här enheten komma åt information om den här profilen, inklusive kontakter, meddelanden och känsliga kryptografiska nycklar.",
"placeholderEnterMessage": "Skriv ett meddelande...",
"blockedMessageMessage": "Det här meddelandet kommer från en profil som du har blockerat.",
"showMessageButton": "Visa meddelande",
"blockUnknownConnectionsEnabledDescription": "Anslutningar från okända kontakter blockeras. Du kan ändra detta i Inställningar",
"archiveConversation": "Arkivera denna konversation",
"streamerModeLabel": "Streaming\/presentationsläge",
"descriptionStreamerMode": "Aktivera för att dölja privat information (exempelvis profil och kontaktadresser) vilket kan vara lämpligt vid streaming eller presentation.",
"retrievingManifestMessage": "Hämtar filinformation...",
"openFolderButton": "Öppna mapp",
"downloadFileButton": "Ladda ner",
"labelFilename": "Filnamn",
"labelFilesize": "Storlek",
"messageEnableFileSharing": "Aktivera fildelning för att kunna se meddelandet.",
"messageFileSent": "Du skickade en fil",
"messageFileOffered": "Kontakten försöker skicka en fil till dig",
"tooltipSendFile": "Skicka fil",
"settingFileSharing": "Fildelning",
"descriptionFileSharing": "Fildelning gör att du kan skicka och ta emot filer från Cwtch-kontakter och grupper. Observera att om du delar en fil med en grupp kommer medlemmarna i den gruppen behöva ansluta direkt till dig med Cwtch för att kunna ladda ner filen.",
"titleManageProfilesShort": "Profiler",
"addServerTitle": "Lägg till server",
"editServerTitle": "Redigera server",
"serverAddress": "Serveradress",
"serverDescriptionLabel": "Serverbeskrivning",
"serverDescriptionDescription": "Din beskrivning av servern. Endast synligt för dig, kommer aldrig att delas",
"serverEnabled": "Server startad",
"serverEnabledDescription": "Starta eller stoppa servern",
"serverAutostartLabel": "Autostart",
"serverAutostartDescription": "Styr om programmet automatiskt startar servern vid start",
"saveServerButton": "Spara server",
"serversManagerTitleLong": "Servrar du är värd för",
"serversManagerTitleShort": "Servrar",
"addServerTooltip": "Lägg till server",
"unlockServerTip": "Skapa eller lås upp en server för att börja!",
"unlockProfileTip": "Skapa eller lås upp en profil för att börja!",
"enterServerPassword": "Ange lösenord för att låsa upp servern",
"settingServers": "Server-värd",
"settingServersDescription": "Server-värd gör det möjligt att vara värd för och hantera Cwtch-servrar",
"copyAddress": "Kopiera adress",
"enterCurrentPasswordForDeleteServer": "Ange lösenord för att radera den här servern",
"deleteServerSuccess": "Servern har tagits bort",
"deleteServerConfirmBtn": "Bekräfta borttagning av servern",
"plainServerDescription": "Vi rekommenderar att du skyddar dina Cwtch-servrar med ett lösenord. Om du inte anger ett lösenord på den här servern kan alla som har åtkomst till den här enheten komma åt information om den här servern, inklusive känsliga kryptografiska nycklar.",
"encryptedServerDescription": "Genom att kryptera en server med ett lösenord skyddas den från andra personer som också kan använda den här enheten. Krypterade servrar kan inte dekrypteras, visas eller nås förrän rätt lösenord har angetts för att låsa upp dem.",
"fileSavedTo": "Sparad till",
"fileInterrupted": "Avbruten",
"fileCheckingStatus": "Kontrollera status för nedladdning",
"verfiyResumeButton": "Verifiera\/återuppta",
"copyServerKeys": "Kopiera nycklar",
"localeRU": "Ryska \/ Русский",
"newMessagesLabel": "Nya meddelanden",
"importLocalServerLabel": "Importera en lokal server",
"importLocalServerSelectText": "Välj lokal server",
"importLocalServerButton": "Importera %1",
"groupsOnThisServerLabel": "Grupper jag är med i på den här servern",
"fieldDescriptionLabel": "Beskrivning",
"manageKnownServersButton": "Hantera kända servrar",
"displayNameTooltip": "Ange ett visningsnamn",
"manageKnownServersLong": "Hantera kända servrar",
"manageKnownServersShort": "Servrar",
"serverMetricsLabel": "Serverinfo",
"serverTotalMessagesLabel": "Antal meddelanden",
"serverConnectionsLabel": "Anslutningar",
"enableExperimentClickableLinks": "Aktivera klickbara länkar",
"experimentClickableLinksDescription": "Med klickbara länkar aktiverat kan du klicka på webbadresser som delas i meddelanden",
"settingImagePreviews": "Förhandsgranskningar av bilder och profilbilder",
"settingImagePreviewsDescription": "Bilder och profilbilder kommer att laddas ner och förhandsgranskas automatiskt. Vi rekommenderar att du inte aktiverar detta om du använder Cwtch med opålitliga kontakter.",
"settingDownloadFolder": "Hämtade filer",
"themeNameCwtch": "Cwtch",
"themeNameWitch": "Häxa",
"themeNameVampire": "Vampyr",
"themeNameGhost": "Spöke",
"themeNamePumpkin": "Pumpa",
"themeNameMermaid": "Sjöjungfru",
"themeNameMidnight": "Midnatt",
"themeNameNeon1": "Neon1",
"themeNameNeon2": "Neon2",
"themeColorLabel": "Färgtema",
"loadingCwtch": "Laddar Cwtch...",
"storageMigrationModalMessage": "Migrerar profiler till nytt lagringsformat. Detta kan ta några minuter...",
"msgFileTooBig": "Filstorleken får inte överstiga 10 GB",
"msgConfirmSend": "Är du säker på att du vill skicka",
"btnSendFile": "Skicka fil",
"msgAddToAccept": "Lägg till det här kontot i dina kontakter för att acceptera den här filen.",
"torSettingsEnabledAdvanced": "Aktivera avancerad Tor-konfiguration",
"torSettingsEnabledAdvancedDescription": "Använd en befintlig Tor-tjänst på ditt system, eller ändra parametrarna för Cwtch Tor-tjänst",
"torSettingsCustomSocksPort": "Anpassad SOCKS-port",
"torSettingsCustomSocksPortDescription": "Använd en anpassad port för dataanslutningar till Tor-proxyn",
"torSettingsCustomControlPort": "Anpassad kontrollport",
"torSettingsCustomControlPortDescription": "Använd en anpassad port för kontrollanslutningar till Tor-proxyn",
"torSettingsUseCustomTorServiceConfiguration": "Använd en anpassad konfiguration av Tor (torrc)",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Åsidosätt standard-konfigurationen för Tor. Varning: Detta kan vara farligt. Använd bara om du vet vad du gör.",
"torSettingsErrorSettingPort": "Portnumret måste vara mellan 1 och 65535",
"fileSharingSettingsDownloadFolderDescription": "När filer laddas ned automatiskt (t.ex. bildfiler, när förhandsgranskning är aktiverat) behövs en standardplats att ladda ner filerna till.",
"fileSharingSettingsDownloadFolderTooltip": "Bläddra för att välja en annan standardmapp för nedladdade filer.",
"descriptionACNCircuitInfo": "Detaljerad information om den väg som det anonyma kommunikationsnätverket använder för att ansluta till den här konversationen.",
"labelACNCircuitInfo": "Info om ACN-vägen",
"labelTorNetwork": "Tor-nätverket",
"torSettingsEnableCache": "Lagra konsensus i cache",
"torSettingsEnabledCacheDescription": "Lagra nedladdad Tor-konsensus i cache för att återanvända nästa gång Cwtch öppnas. Detta gör att Tor kan starta snabbare. Om detta är inaktiverat kommer Cwtch rensa cache vid uppstart.",
"tooltipSelectACustomProfileImage": "Välj en anpassad profilbild",
"notificationPolicyOptIn": "Välj",
"notificationPolicyMute": "Tysta",
"conversationNotificationPolicyOptIn": "Välj",
"notificationPolicyDefaultAll": "Återställ standard",
"conversationNotificationPolicyDefault": "Standard",
"conversationNotificationPolicyNever": "Aldrig",
"notificationPolicySettingLabel": "Aviseringspolicy",
"notificationContentSettingLabel": "Aviseringsinnehåll",
"notificationPolicySettingDescription": "Styr programmets standardaviseringsbeteende",
"notificationContentSettingDescription": "Styr innehållet i konversationsaviseringar",
"settingGroupBehaviour": "Beteende",
"settingsGroupAppearance": "Utseende",
"settingsGroupExperiments": "Experiment",
"conversationNotificationPolicySettingLabel": "Policy för konversationsaviseringar",
"conversationNotificationPolicySettingDescription": "Ändra aviseringar för den här konversationen",
"notificationContentSimpleEvent": "Enkel händelse",
"notificationContentContactInfo": "Info om konversation",
"newMessageNotificationSimple": "Nytt meddelande",
"newMessageNotificationConversationInfo": "Nytt meddelande från %1",
"localeRo": "Rumänska \/ Română",
"localeLb": "Luxemburgiska \/ Lëtzebuergesch",
"localeNo": "Norska \/ Norsk",
"localeEl": "Grekiska \/ Ελληνικά",
"localeCy": "Walesiska \/ Cymraeg",
"localeDa": "Danska \/ Dansk",
"exportProfile": "Exportera profil",
"exportProfileTooltip": "Säkerhetskopiera den här profilen till en krypterad fil. Den krypterade filen kan importeras till en annan Cwtch-app.",
"importProfileTooltip": "Använd en krypterad Cwtch-säkerhetskopia för att importera en profil skapad i en annan instans av Cwtch.",
"importProfile": "Importera profil",
"failedToImportProfile": "Fel vid import av profil",
"successfullyImportedProfile": "Profilen har importerats: %profile",
"shuttingDownApp": "Stänger ner...",
"clickableLinksWarning": "Om du öppnar den här webbadressen startar en applikation utanför Cwtch som kan avslöja metadata eller på annat sätt äventyra säkerheten för Cwtch. Öppna bara webbadresser från personer du litar på. Är du säker på att du vill fortsätta?",
"clickableLinkOpen": "Öppna URL",
"clickableLinksCopy": "Kopiera URL",
"clickableLinkError": "Fel uppstod vid försök att öppna URL",
"formattingExperiment": "Meddelandeformatering",
"messageFormattingDescription": "Aktivera RTF-formatering i visade meddelanden, t.ex. **fet** och *kursiv*",
"thisFeatureRequiresGroupExpermientsToBeEnabled": "Den här funktionen kräver att Grupper är aktiverat i Inställningar.",
"settingAndroidPowerExemption": "Undanta Android batterioptimering",
"settingAndroidPowerExemptionDescription": "Valfritt: Begär att Android ska undanta Cwtch från optimerad energihantering. Detta kommer att leda till bättre stabilitet till priset av större batterianvändning.",
"settingsAndroidPowerReenablePopup": "Det går inte att aktivera batterioptimering från Cwtch. Gå till Android \/ Inställningar \/ Appar \/ Cwtch \/ Batteri och välj \"Optimerad\"",
"okButton": "OK",
"tooltipBoldText": "Fet",
"tooltipBackToMessageEditing": "Tillbaka till meddelanderedigering",
"tooltipItalicize": "Kursiv",
"tooltipSuperscript": "Upphöjd",
"tooltipSubscript": "Nedsänkt",
"tooltipStrikethrough": "Överstruket",
"tooltipCode": "Kod \/ Monospace",
"tooltipPreviewFormatting": "Förhandsgranska meddelandeformatering",
"manageSharedFiles": "Hantera delade filer",
"stopSharingFile": "Sluta dela fil",
"restartFileShare": "Dela fil",
"viewReplies": "Visa svar på detta meddelande",
"headingReplies": "Svar",
"messageNoReplies": "Det finns inga svar på detta meddelande.",
"fileDownloadUnavailable": "Den här filen verkar inte vara tillgänglig för nedladdning. Avsändaren kan ha inaktiverat nedladdningar för den här filen.",
"replyingTo": "Svarar %1",
"tooltipPinConversation": "Fäst konversationen högst upp i \"Konversationer\".",
"tooltipUnpinConversation": "Ta bort konversationen från toppen av \"Konversationer\"",
"localeTr": "Turkiska \/ Türk",
"errorDownloadDirectoryDoesNotExist": "Fildelning kan inte aktiveras eftersom nedladdningsmappen inte har ställts in eller är inställd på en mapp som inte finns.",
"acquiringTicketsFromServer": "Utför Antispam-test",
"acquiredTicketsFromServer": "Antispam-testet slutfört",
"shareProfileMenuTooltop": "Dela profil via...",
"shareMenuQRCode": "Visa QR-kod",
"enableExperimentQRCode": "QR-koder",
"experimentQRCodeDescription": "QR-kodstöd tillåter delning av data (som profilidentitet) med QR-koder",
"localeNl": "Nederländska \/ Dutch",
"localePtBr": "Brasiliansk portugisiska \/ Português do Brasil",
"profileAutostartLabel": "Starta automatiskt",
"profileEnabled": "Aktivera",
"profileAutostartDescription": "Styr om profilen ska startas automatiskt vid programstart",
"localeSk": "Slovakiska \/ Slovák",
"localeKo": "Koreanska \/ 한국어",
"blodeuweddExperimentEnable": "Blodeuwedd-assistenten",
"blodeuweddDescription": "Blodeuwedd-assistenten lägger till nya funktioner till Cwtch, såsom sammanfattning av koversationer och meddelandeöversättning. Allt via en språkmodell som körs i datorn.",
"blodeuweddNotSupported": "Denna version av Cwtch har kompilerats utan stöd för Blodeuwedd-assistenten.",
"blodeuweddPath": "Katalogen där Blodeuwedd finns på din dator.",
"blodeuweddSummarize": "Sammanfatta konversation",
"blodeuweddTranslate": "Översätt meddelande",
"blodeuweddWarning": "Blodeuwedd använder en lokal språkmodell och en uppsättning små hjälpmodeller för att driva dess funktionalitet. Dessa tekniker är ofta mycket effektiva, de är inte felfria.\n\nÄven om vi har vidtagit åtgärder för att minimera risken, finns det fortfarande en möjlighet att data från Blodeuwedd kan vara felaktigt, missvisande och\/eller stötande.\n\nPå grund av det kräver Blodeuwedd nedladdning av två ytterligare komponenter separat från Cwtch, Blodeuwedd Model (eller annan kompatibel modell) och Blodeuwedd Runner.\n\nLäs på https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd för mer information om hur du skaffar dessa komponenter och konfigurerar dem.",
"blodeuweddProcessing": "Blodeuwedd arbetar...",
"availabilityStatusAvailable": "Tillgänglig",
"availabilityStatusAway": "Borta",
"availabilityStatusBusy": "Upptagen",
"availabilityStatusTooltip": "Ange tillgänglighetsstatus",
"profileInfoHint": "Lägg till lite offentlig information om dig själv här, t.ex. blogg, webbplatser, kortfattad biografi.",
"profileInfoHint2": "Du kan lägga till upp till 3 fält.",
"profileInfoHint3": "Kontakter kommer att kunna se denna information under konversationsinställningar",
"retryConnection": "Försök igen",
"retryConnectionTooltip": "Cwtch försöker ansluta till noder regelbundet, men du kan be Cwtch att försöka tidigare genom att trycka på den här knappen.",
"localeSv": "Svenska \/ Svenska",
"localeJa": "Japanska \/ 日本語",
"fontScalingDescription": "Justera den relativa skalningsfaktorn för teckensnitt som tillämpas på text och widgets."
}

391
lib/l10n/intl_sw.arb Normal file
View File

@ -0,0 +1,391 @@
{
"@@locale": "sw",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Batilisha usanidi chaguo-msingi wa tor. Onyo: Hii inaweza kuwa hatari. Washa hii ikiwa tu unajua unachofanya.",
"torSettingsErrorSettingPort": "Nambari ya Mlango lazima iwe kati ya 1 na 65535",
"fileSharingSettingsDownloadFolderDescription": "Faili zinapopakuliwa kiotomatiki (km faili za picha, mapitio ya picha yanapowezeshwa) eneo chaguomsingi la kupakua faili linahitajika.",
"fileSharingSettingsDownloadFolderTooltip": "Vinjari ili kuchagua folda chaguo-msingi tofauti kwa faili zilizopakuliwa.",
"labelACNCircuitInfo": "Maelezo ya Mzunguko wa ACN",
"descriptionACNCircuitInfo": "Maelezo ya kina kuhusu njia ambayo mtandao wa mawasiliano usiojulikana unatumia kuunganisha kwenye mazungumzo haya.",
"labelTorNetwork": "Mtandao wa Tor",
"torSettingsEnableCache": "Makubaliano ya Cache Tor",
"tooltipSelectACustomProfileImage": "Chagua Picha Maalum ya Wasifu",
"notificationPolicyMute": "Nyamazisha",
"notificationPolicyOptIn": "Chagua Katika",
"notificationPolicyDefaultAll": "Chaguomsingi Zote",
"availabilityStatusBusy": "Shughuli",
"conversationNotificationPolicyDefault": "Chaguomsingi",
"conversationNotificationPolicyOptIn": "Chagua kujijumuisha ",
"conversationNotificationPolicyNever": "Kamwe",
"notificationPolicySettingLabel": "Sera ya Arifa",
"notificationContentSettingLabel": "Maudhui ya Arifa",
"notificationPolicySettingDescription": "Hudhibiti tabia ya arifa ya programu chaguomsingi",
"notificationContentSettingDescription": "Hudhibiti maudhui ya arifa za mazungumzo",
"settingGroupBehaviour": "Tabia",
"settingsGroupAppearance": "Muonekano",
"settingsGroupExperiments": "Majaribio",
"conversationNotificationPolicySettingLabel": "Sera ya Arifa ya Mazungumzo",
"conversationNotificationPolicySettingDescription": "Dhibiti tabia ya arifa kwa mazungumzo haya",
"notificationContentSimpleEvent": "Tukio la wazi",
"notificationContentContactInfo": "Taarifa za Mazungumzo",
"newMessageNotificationSimple": "Ujumbe Mpya",
"newMessageNotificationConversationInfo": "Ujumbe Mpya Kutoka %1",
"localeRo": "Kiromania \/ Kirumi",
"localeLb": "Luxembourgish \/ Lëtzebuergesch",
"localeNo": "Kinorwe \/ Norsk",
"localeEl": "Kigiriki \/ Ελληνικά",
"localeCy": "Welsh \/ Cymraeg",
"localeDa": "Kidenishi \/ Dansk",
"exportProfile": "Hamisha Wasifu",
"exportProfileTooltip": "Hifadhi nakala ya wasifu huu kwa faili iliyosimbwa. Faili iliyosimbwa kwa njia fiche inaweza kuingizwa kwenye programu nyingine ya Cwtch.",
"importProfile": "Ingiza Wasifu",
"importProfileTooltip": "Tumia chelezo iliyosimbwa kwa njia fiche ya Cwtch kuleta wasifu ulioundwa katika mfano mwingine wa Cwtch.",
"failedToImportProfile": "Hitilafu katika Kuingiza Wasifu",
"successfullyImportedProfile": "Imefaulu Kuingiza Wasifu: %wasifu",
"shuttingDownApp": "Inazima...",
"clickableLinksWarning": "Kufungua URL hii itafungua programu nje ya Cwtch na kunaweza kufichua metadata au vinginevyo kuhatarisha usalama wa Cwtch. Fungua URL kutoka kwa watu unaowaamini pekee. Je, una uhakika ungependa kuendelea?",
"clickableLinkOpen": "Fungua URL",
"clickableLinksCopy": "Nakili URL",
"clickableLinkError": "Hitilafu ilitokea wakati wa kujaribu kufungua URL",
"formattingExperiment": "Uumbizaji wa Ujumbe",
"messageFormattingDescription": "Washa uumbizaji wa maandishi umbizo katika ujumbe unaoonyeshwa kwa mfano **bold** na *italic*",
"thisFeatureRequiresGroupExpermientsToBeEnabled": "Kipengele hiki kinahitaji Majaribio ya Vikundi kuwashwa katika Mipangilio",
"settingAndroidPowerExemption": "Android Puuza Uboreshaji wa Betri",
"settingAndroidPowerExemptionDescription": "Hiari: Omba Android kuondoa Cwtch kutoka kwa usimamizi bora wa nishati. Hii itasababisha uthabiti bora kwa gharama ya matumizi makubwa ya betri.",
"settingsAndroidPowerReenablePopup": "Haiwezi kuwezesha upya Uboreshaji wa Betri kutoka ndani ya Cwtch. Tafadhali nenda kwa Android \/ Mipangilio \/ Programu \/ Cwtch \/ Betri na uweke Matumizi kwa 'Imeboreshwa'",
"okButton": "sawa",
"tooltipBoldText": "Kolevu",
"tooltipBackToMessageEditing": "Rudi kwa Kuhariri Ujumbe",
"tooltipItalicize": "Italiki",
"tooltipSubscript": "Usajili",
"tooltipStrikethrough": "Strikethrough",
"tooltipCode": "Nambari \/ Nafasi moja",
"tooltipPreviewFormatting": "Hakiki Uumbizaji wa Ujumbe",
"manageSharedFiles": "Dhibiti Faili Zilizoshirikiwa",
"stopSharingFile": "Acha Kushiriki Faili",
"restartFileShare": "Anza Kushiriki Faili",
"viewReplies": "Tazama majibu kwa ujumbe huu",
"headingReplies": "Majibu",
"messageNoReplies": "Hakuna majibu kwa ujumbe huu.",
"fileDownloadUnavailable": "Faili hii inaonekana haipatikani kwa kupakuliwa. Mtumaji anaweza kuwa amezima upakuaji wa faili hii.",
"replyingTo": "Kujibu %1",
"tooltipPinConversation": "Bandika mazungumzo juu ya \"Mazungumzo\"",
"tooltipUnpinConversation": "Bandua mazungumzo kutoka sehemu ya juu ya \"Mazungumzo\"",
"localeTr": "Kituruki \/ Kituruki",
"acquiringTicketsFromServer": "Kutekeleza Changamoto ya Antispam",
"acquiredTicketsFromServer": "Changamoto ya Antispam Imekamilika",
"shareProfileMenuTooltop": "Shiriki wasifu kupitia...",
"shareMenuQRCode": "Onyesha Msimbo wa QR",
"enableExperimentQRCode": "Misimbo ya QR",
"experimentQRCodeDescription": "Usaidizi wa Msimbo wa QR huruhusu kushiriki data (kama vile utambulisho wa wasifu) kwa Misimbo ya QR",
"localeNl": "Kiholanzi \/ Kiholanzi",
"localePtBr": "Kireno cha Kibrazili \/ Português do Brasil",
"profileAutostartLabel": "Anzisha kiotomatiki",
"profileEnabled": "Wezesha",
"profileAutostartDescription": "Hudhibiti ikiwa wasifu utazinduliwa kiotomatiki inapowashwa",
"profileEnabledDescription": "Washa au Lemaza wasifu.",
"localeSk": "Kislovakia \/ Kislovakia",
"localeKo": "Kikorea \/ 한국어",
"blodeuweddExperimentEnable": "Msaidizi wa Blodeuwedd",
"blodeuweddNotSupported": "Toleo hili la Cwtch limeundwa bila usaidizi kwa Msaidizi wa Blodeuwedd.",
"retryConnectionTooltip": "Cwtch retries wenzao mara kwa mara, lakini unaweza kuwaambia Cwtch kujaribu mapema kwa kubofya hiki kitufe",
"blodeuweddPath": "The directory where the Blodeuwedd is located on your computer.",
"blodeuweddSummarize": "Fupisha Mazungumzo",
"blodeuweddTranslate": "Tafsiri Ujumbe",
"blodeuweddProcessing": "Blodeuwedd inachakata...",
"availabilityStatusAvailable": "Inapatikana",
"availabilityStatusAway": "Sipo karibu",
"availabilityStatusTooltip": "Weka hali ya upatikanaji wako",
"profileInfoHint": "Ongeza taarifa za umma kukuhusu hapa kwa mfano blogu, tovuti, wasifu mfupi.",
"profileInfoHint2": "Unaweza kuongeza hadi sehemu 3.",
"profileInfoHint3": "Anwani zitaweza kuona maelezo haya katika Mipangilio ya Mazungumzo",
"retryConnection": "Jaribu tena",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"localeUk": "Kiukreni \/ українською",
"localeSv": "Kiswidi \/ Svenska",
"localeJa": "Kijapani \/ 日本語",
"localeSw": "Kiswahili \/ Kiswahili",
"blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.",
"blodeuweddDescription": "The Blodeuwedd assistant adds new features to Cwtch such as chat transcript summarization and message translation via a locally hosted language model.",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"tooltipSuperscript": "Superscript",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",
"torSettingsCustomControlPortDescription": "Use a custom port for control connections to the Tor proxy",
"torSettingsCustomControlPort": "Custom Control Port",
"torSettingsCustomSocksPortDescription": "Use a custom port for data connections to the Tor proxy",
"torSettingsCustomSocksPort": "Custom SOCKS Port",
"torSettingsEnabledAdvancedDescription": "Use an existing Tor service on your system, or change the parameters of the Cwtch Tor Service",
"torSettingsEnabledAdvanced": "Enable Advanced Tor Configuration",
"msgAddToAccept": "Add this account to your contacts in order to accept this file.",
"btnSendFile": "Send File",
"msgConfirmSend": "Are you sure you want to send",
"msgFileTooBig": "File size cannot exceed 10 GB",
"storageMigrationModalMessage": "Migrating profiles to new storage format. This could take a few minutes...",
"loadingCwtch": "Loading Cwtch...",
"themeColorLabel": "Color Theme",
"themeNameNeon2": "Neon2",
"themeNameNeon1": "Neon1",
"themeNameMidnight": "Midnight",
"themeNameMermaid": "Mermaid",
"themeNamePumpkin": "Pumpkin",
"themeNameGhost": "Ghost",
"themeNameVampire": "Vampire",
"themeNameWitch": "Witch",
"themeNameCwtch": "Cwtch",
"settingDownloadFolder": "Download Folder",
"settingImagePreviewsDescription": "Images and Profile Pictures will be downloaded and previewed automatically. We recommend that you do not enable this Experiment if you use Cwtch with untrusted contacts.",
"settingImagePreviews": "Image Previews and Profile Pictures",
"experimentClickableLinksDescription": "The clickable links experiment allows you to click on URLs shared in messages",
"enableExperimentClickableLinks": "Enable Clickable Links",
"serverConnectionsLabel": "Connection",
"serverTotalMessagesLabel": "Total Messages",
"serverMetricsLabel": "Server Metrics",
"manageKnownServersShort": "Servers",
"manageKnownServersLong": "Manage Known Servers",
"displayNameTooltip": "Please enter a display name",
"manageKnownServersButton": "Manage Known Servers",
"fieldDescriptionLabel": "Description",
"groupsOnThisServerLabel": "Groups I am in hosted on this server",
"importLocalServerButton": "Import %1",
"importLocalServerSelectText": "Select Local Server",
"importLocalServerLabel": "Import a locally hosted server",
"newMessagesLabel": "New Messages",
"localeRU": "Russian \/ Русский",
"copyServerKeys": "Copy keys",
"verfiyResumeButton": "Verify\/resume",
"fileCheckingStatus": "Checking download status",
"fileInterrupted": "Interrupted",
"fileSavedTo": "Saved to",
"encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.",
"deleteServerConfirmBtn": "Really delete server",
"deleteServerSuccess": "Successfully deleted server",
"enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server",
"copyAddress": "Copy Address",
"settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers",
"settingServers": "Hosting Servers",
"enterServerPassword": "Enter password to unlock server",
"unlockProfileTip": "Please create or unlock a profile to begin!",
"unlockServerTip": "Please create or unlock a server to begin!",
"addServerTooltip": "Add new server",
"serversManagerTitleShort": "Servers",
"serversManagerTitleLong": "Servers You Host",
"saveServerButton": "Save Server",
"serverAutostartDescription": "Controls if the application will automatically launch the server on start",
"serverAutostartLabel": "Autostart",
"serverEnabledDescription": "Start or stop the server",
"serverEnabled": "Server Enabled",
"serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared",
"serverDescriptionLabel": "Server Description",
"serverAddress": "Server Address",
"editServerTitle": "Edit Server",
"addServerTitle": "Add Server",
"titleManageProfilesShort": "Profiles",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses",
"streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation",
"blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings",
"showMessageButton": "Show Message",
"blockedMessageMessage": "This message is from a profile you have blocked.",
"placeholderEnterMessage": "Type a message...",
"plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.",
"encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
"settingUIColumnSingle": "Single",
"settingUIColumnLandscape": "UI Columns in Landscape Mode",
"settingUIColumnPortrait": "UI Columns in Portrait Mode",
"localePl": "Polish \/ Polski",
"tooltipRemoveThisQuotedMessage": "Remove quoted message.",
"tooltipReplyToThisMessage": "Reply to this message",
"tooltipRejectContactRequest": "Reject this contact request",
"tooltipAcceptContactRequest": "Accept this contact request.",
"notificationNewMessageFromGroup": "New message in a group!",
"notificationNewMessageFromPeer": "New message from a contact!",
"tooltipHidePassword": "Hide Password",
"tooltipShowPassword": "Show Password",
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
"shutdownCwtchAction": "Shutdown Cwtch",
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
"shutdownCwtchTooltip": "Shutdown Cwtch",
"malformedMessage": "Malformed message",
"profileDeleteSuccess": "Successfully deleted profile",
"debugLog": "Turn on console debug logging",
"torNetworkStatus": "Tor network status",
"addContactFirst": "Add or pick a contact to begin chatting.",
"createProfileToBegin": "Please create or unlock a profile to begin",
"nickChangeSuccess": "Profile nickname changed successfully",
"addServerFirst": "You need to add a server before you can create a group",
"deleteProfileSuccess": "Successfully deleted profile",
"sendInvite": "Send a contact or group invite",
"sendMessage": "Send Message",
"cancel": "Cancel",
"resetTor": "Reset",
"torStatus": "Tor Status",
"torVersion": "Tor Version",
"sendAnInvitation": "You sent an invitation for: ",
"contactSuggestion": "This is a contact suggestion for: ",
"rejected": "Rejected!",
"accepted": "Accepted!",
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
"newPassword": "New Password",
"yesLeave": "Yes, Leave This Conversation",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"leaveConversation": "Leave This Conversation",
"inviteToGroup": "You have been invited to join a group:",
"titleManageServers": "Manage Servers",
"successfullAddedContact": "Successfully added ",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionExperiments": "Cwtch experiments are optional, opt-in features that add additional functionality to Cwtch that may have different privacy considerations than traditional 1:1 metadata resistant chat e.g. group chat, bot integration etc.",
"titleManageProfiles": "Manage Cwtch Profiles",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"titleManageContacts": "Conversations",
"tooltipAddContact": "Add a new contact or conversation",
"tooltipOpenSettings": "Open the settings pane",
"contactAlreadyExists": "Contact Already Exists",
"invalidImportString": "Invalid import string",
"conversationSettings": "Conversation Settings",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"enableGroups": "Enable Group Chat",
"localeIt": "Italian \/ Italiano",
"localeEs": "Spanish \/ Español",
"todoPlaceholder": "Todo...",
"addNewItem": "Add a new item to the list",
"addListItem": "Add a New List Item",
"newConnectionPaneTitle": "New Connection",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connecting to network and contacts...",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"viewGroupMembershipTooltip": "View Group Membership",
"loadingTor": "Loading tor...",
"smallTextLabel": "Small",
"defaultScalingText": "Font Scaling",
"builddate": "Built on: %2",
"version": "Version %1",
"versionTor": "Version %1 with tor %2",
"experimentsEnabled": "Enable Experiments",
"themeDark": "Dark",
"themeLight": "Light",
"settingTheme": "Use Light Themes",
"largeTextLabel": "Large",
"settingInterfaceZoom": "Zoom level",
"localeDe": "German \/ Deutsch",
"localePt": "Portuguese \/ Portuguesa",
"localeFr": "French \/ Français",
"localeEn": "English \/ English",
"settingLanguage": "Language",
"blockUnknownLabel": "Block Unknown Contacts",
"zoomLabel": "Interface zoom (mostly affects text and button sizes)",
"versionBuilddate": "Version: %1 Built on: %2",
"cwtchSettingsTitle": "Cwtch Settings",
"unlock": "Unlock",
"yourServers": "Your Servers",
"yourProfiles": "Your Profiles",
"error0ProfilesLoadedForPassword": "0 profiles loaded with that password",
"password": "Password",
"enterProfilePassword": "Enter a password to view your profiles",
"addNewProfileBtn": "Add new profile",
"deleteConfirmText": "DELETE",
"deleteProfileConfirmBtn": "Really Delete Profile",
"deleteConfirmLabel": "Type DELETE to confirm",
"deleteProfileBtn": "Delete Profile",
"passwordChangeError": "Error changing password: Supplied password rejected",
"passwordErrorMatch": "Passwords do not match",
"saveProfileBtn": "Save Profile",
"createProfileBtn": "Create Profile",
"passwordErrorEmpty": "Password cannot be empty",
"password2Label": "Reenter password",
"password1Label": "Password",
"currentPasswordLabel": "Current Password",
"yourDisplayName": "Your Display Name",
"profileOnionLabel": "Send this address to contacts you want to connect with",
"noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted",
"radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password",
"editProfile": "Edit Profile",
"newProfile": "New Profile",
"defaultProfileName": "Alice",
"profileName": "Display name",
"editProfileTitle": "Edit Profile",
"addProfileTitle": "Add new profile",
"deleteBtn": "Delete",
"unblockBtn": "Unblock Contact",
"dontSavePeerHistory": "Delete History",
"savePeerHistoryDescription": "Determines whether to delete any history associated with the contact.",
"savePeerHistory": "Save History",
"blockBtn": "Block Contact",
"saveBtn": "Save",
"displayNameLabel": "Display Name",
"copiedToClipboardNotification": "Copied to Clipboard",
"addressLabel": "Address",
"puzzleGameBtn": "Puzzle Game",
"bulletinsBtn": "Bulletins",
"listsBtn": "Lists",
"chatBtn": "Chat",
"rejectGroupBtn": "Reject",
"acceptGroupBtn": "Accept",
"acceptGroupInviteLabel": "Do you want to accept the invitation to",
"newGroupBtn": "Create new group",
"copyBtn": "Copy",
"peerOfflineMessage": "Contact is offline, messages can't be delivered right now",
"peerBlockedMessage": "Contact is blocked",
"pendingLabel": "Pending",
"acknowledgedLabel": "Acknowledged",
"couldNotSendMsgError": "Could not send this message",
"dmTooltip": "Click to DM",
"membershipDescription": "Below is a list of users who have sent messages to the group. This list may not reflect all users who have access to the group.",
"addListItemBtn": "Add Item",
"peerNotOnline": "Contact is offline. Applications cannot be used right now.",
"searchList": "Search List",
"update": "Update",
"inviteBtn": "Invite",
"inviteToGroupLabel": "Invite to group",
"groupNameLabel": "Group Name",
"viewServerInfo": "Server Info",
"serverNotSynced": "Syncing New Messages (This can take some time)...",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
"serverConnectivityConnected": "Server Connected",
"serverInfo": "Server Information",
"invitationLabel": "Invitation",
"serverLabel": "Server",
"search": "Search...",
"blocked": "Blocked",
"pasteAddressToAddContact": "Paste a cwtch address, invitation or key bundle here to add a new conversation",
"titlePlaceholder": "title...",
"postNewBulletinLabel": "Post new bulletin",
"newBulletinLabel": "New Bulletin",
"joinGroup": "Join group",
"createGroup": "Create group",
"addPeer": "Add Contact",
"groupAddr": "Address",
"invitation": "Invitation",
"server": "Server",
"peerName": "Name",
"peerAddress": "Address",
"joinGroupTab": "Join a group",
"createGroupTab": "Create a group",
"addPeerTab": "Add a contact",
"createGroupBtn": "Create",
"defaultGroupName": "Awesome Group",
"createGroupTitle": "Create Group"
}

View File

@ -1,6 +1,10 @@
{
"@@locale": "tr",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"profileEnabledDescription": "Profili başlat veya durdur",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Varsayılan metin boyutu (ölçek faktörü: ",
"localeJa": "Japanese \/ 日本語",
@ -23,7 +27,6 @@
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Profili başlat veya durdur",
"profileAutostartLabel": "Otomatik başlatma",
"profileAutostartDescription": "Profilin başlangıçta otomatik olarak başlatılıp başlatılmayacağını kontrol eder",
"profileEnabled": "Etkinleştir",

391
lib/l10n/intl_uk.arb Normal file
View File

@ -0,0 +1,391 @@
{
"@@locale": "uk",
"@@last_modified": "2023-05-29T14:05:23+02:00",
"localeUk": "Ukrainian \/ українською",
"localeSw": "Swahili \/ Kiswahili",
"localeSv": "Swedish \/ Svenska",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"localeJa": "Japanese \/ 日本語",
"retryConnectionTooltip": "Cwtch retries peers regularly, but you can tell Cwtch to try sooner by pushing this button.",
"retryConnection": "Retry",
"profileInfoHint3": "Contacts will be able to see this information in Conversation Settings ",
"profileInfoHint2": "You can add up to 3 fields.",
"profileInfoHint": "Add some public information about yourself here e.g. blog, websites, brief bio.",
"availabilityStatusTooltip": "Set your availability status",
"availabilityStatusBusy": "Busy",
"availabilityStatusAway": "Away",
"availabilityStatusAvailable": "Available",
"blodeuweddWarning": "Blodeuwedd uses a local language model and a set of small auxiliary models to power its functionality. These techniques are often very effective they are not without error. \n\nWhile we have taken efforts to minimize the risk, there is still the possibility that Blodeuwedd outputs will be incorrect, hallucinated and\/or offensive.\n\nBecause of that Blodeuwedd requires downloading two additional components separate from Cwtch, the Blodeuwedd Model (or a compatible model) and the Blodeuwedd Runner. \n\nSee https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd for more information on obtaining these components and setting them up.",
"blodeuweddProcessing": "Blodeuwedd is processing...",
"blodeuweddTranslate": "Translate Message",
"blodeuweddSummarize": "Summarize Conversation",
"blodeuweddPath": "The directory where the Blodeuwedd is located on your computer.",
"blodeuweddNotSupported": "This version of Cwtch has been compiled without support for the Blodeuwedd Assistant.",
"blodeuweddDescription": "The Blodeuwedd assistant adds new features to Cwtch such as chat transcript summarization and message translation via a locally hosted language model.",
"blodeuweddExperimentEnable": "Blodeuwedd Assistant",
"localeKo": "Korean \/ 한국어",
"localeSk": "Slovak \/ Slovák",
"profileEnabledDescription": "Activate or Deactivate the profile.",
"profileAutostartDescription": "Controls if the profile will be automatically launched on startup",
"profileEnabled": "Enable",
"profileAutostartLabel": "Autostart",
"localePtBr": "Brazilian Portuguese \/ Português do Brasil",
"localeNl": "Dutch \/ Dutch",
"experimentQRCodeDescription": "QR Code support allows sharing data (such as profile identity) by QR Codes",
"enableExperimentQRCode": "QR Codes",
"shareMenuQRCode": "Show QR Code",
"shareProfileMenuTooltop": "Share profile via...",
"acquiredTicketsFromServer": "Antispam Challenge Complete",
"acquiringTicketsFromServer": "Performing Antispam Challenge",
"errorDownloadDirectoryDoesNotExist": "Filesharing cannot be enabled because the Download Folder has not been set, or is set to a folder that does not exist.",
"localeTr": "Turkish \/ Türk",
"tooltipUnpinConversation": "Unpin conversation from the top of \"Conversations\"",
"tooltipPinConversation": "Pin conversation to the top of \"Conversations\"",
"replyingTo": "Replying to %1",
"fileDownloadUnavailable": "This file appears unavailable for download. The sender may have disabled downloads for this file.",
"messageNoReplies": "There are no replies to this message.",
"headingReplies": "Replies",
"viewReplies": "View replies to this message",
"restartFileShare": "Start Sharing File",
"stopSharingFile": "Stop Sharing File",
"manageSharedFiles": "Manage Shared Files",
"tooltipPreviewFormatting": "Preview Message Formatting",
"tooltipCode": "Code \/ Monospace",
"tooltipStrikethrough": "Strikethrough",
"tooltipSubscript": "Subscript",
"tooltipSuperscript": "Superscript",
"tooltipItalicize": "Italic",
"tooltipBackToMessageEditing": "Back to Message Editing",
"tooltipBoldText": "Bold",
"okButton": "OK",
"settingsAndroidPowerReenablePopup": "Cannot re-enable Battery Optimization from within Cwtch. Please go to Android \/ Settings \/ Apps \/ Cwtch \/ Battery and set Usage to 'Optimized'",
"settingAndroidPowerExemptionDescription": "Optional: Request Android to exempt Cwtch from optimized power management. This will result in better stability at the cost of greater battery use.",
"settingAndroidPowerExemption": "Android Ignore Battery Optimizations",
"thisFeatureRequiresGroupExpermientsToBeEnabled": "This feature requires the Groups Experiment to be enabled in Settings",
"messageFormattingDescription": "Enable rich text formatting in displayed messages e.g. **bold** and *italic*",
"formattingExperiment": "Message Formatting",
"clickableLinkError": "Error encountered while attempting to open URL",
"clickableLinksCopy": "Copy URL",
"clickableLinkOpen": "Open URL",
"clickableLinksWarning": "Opening this URL will launch an application outside of Cwtch and may reveal metadata or otherwise compromise the security of Cwtch. Only open URLs from people you trust. Are you sure you want to continue?",
"shuttingDownApp": "Shutting down...",
"successfullyImportedProfile": "Successfully Imported Profile: %profile",
"failedToImportProfile": "Error Importing Profile",
"importProfileTooltip": "Use an encrypted Cwtch backup to bring in a profile created in another instance of Cwtch.",
"importProfile": "Import Profile",
"exportProfileTooltip": "Backup this profile to an encrypted file. The encrypted file can be imported into another Cwtch app.",
"exportProfile": "Export Profile",
"localeDa": "Danish \/ Dansk",
"localeCy": "Welsh \/ Cymraeg",
"localeEl": "Greek \/ Ελληνικά",
"localeNo": "Norwegian \/ Norsk",
"localeLb": "Luxembourgish \/ Lëtzebuergesch",
"localeRo": "Romanian \/ Română",
"newMessageNotificationConversationInfo": "New Message From %1",
"newMessageNotificationSimple": "New Message",
"notificationContentContactInfo": "Conversation Information",
"notificationContentSimpleEvent": "Plain Event",
"conversationNotificationPolicySettingDescription": "Control notification behaviour for this conversation",
"conversationNotificationPolicySettingLabel": "Conversation Notification Policy",
"settingsGroupExperiments": "Experiments",
"settingsGroupAppearance": "Appearance",
"settingGroupBehaviour": "Behaviour",
"notificationContentSettingDescription": "Controls the contents of conversation notifications",
"notificationPolicySettingDescription": "Controls the default application notification behaviour",
"notificationContentSettingLabel": "Notification Content",
"notificationPolicySettingLabel": "Notification Policy",
"conversationNotificationPolicyNever": "Never",
"conversationNotificationPolicyOptIn": "Opt In",
"conversationNotificationPolicyDefault": "Default",
"notificationPolicyDefaultAll": "Default All",
"notificationPolicyOptIn": "Opt In",
"notificationPolicyMute": "Mute",
"tooltipSelectACustomProfileImage": "Select a Custom Profile Image",
"torSettingsEnabledCacheDescription": "Cache the current downloaded Tor consensus to reuse next time Cwtch is opened. This will allow Tor to start faster. When disabled, Cwtch will purge cached data on start up.",
"torSettingsEnableCache": "Cache Tor Consensus",
"labelTorNetwork": "Tor Network",
"descriptionACNCircuitInfo": "In depth information about the path that the anonymous communication network is using to connect to this conversation.",
"labelACNCircuitInfo": "ACN Circuit Info",
"fileSharingSettingsDownloadFolderTooltip": "Browse to select a different default folder for downloaded files.",
"fileSharingSettingsDownloadFolderDescription": "When files are downloaded automatically (e.g. image files, when image previews are enabled) a default location to download the files to is needed.",
"torSettingsErrorSettingPort": "Port Number must be between 1 and 65535",
"torSettingsUseCustomTorServiceConfigurastionDescription": "Override the default tor configuration. Warning: This could be dangerous. Only turn this on if you know what you are doing.",
"torSettingsUseCustomTorServiceConfiguration": "Use a Custom Tor Service Configuration (torrc)",
"torSettingsCustomControlPortDescription": "Use a custom port for control connections to the Tor proxy",
"torSettingsCustomControlPort": "Custom Control Port",
"torSettingsCustomSocksPortDescription": "Use a custom port for data connections to the Tor proxy",
"torSettingsCustomSocksPort": "Custom SOCKS Port",
"torSettingsEnabledAdvancedDescription": "Use an existing Tor service on your system, or change the parameters of the Cwtch Tor Service",
"torSettingsEnabledAdvanced": "Enable Advanced Tor Configuration",
"msgAddToAccept": "Add this account to your contacts in order to accept this file.",
"btnSendFile": "Send File",
"msgConfirmSend": "Are you sure you want to send",
"msgFileTooBig": "File size cannot exceed 10 GB",
"storageMigrationModalMessage": "Migrating profiles to new storage format. This could take a few minutes...",
"loadingCwtch": "Loading Cwtch...",
"themeColorLabel": "Color Theme",
"themeNameNeon2": "Neon2",
"themeNameNeon1": "Neon1",
"themeNameMidnight": "Midnight",
"themeNameMermaid": "Mermaid",
"themeNamePumpkin": "Pumpkin",
"themeNameGhost": "Ghost",
"themeNameVampire": "Vampire",
"themeNameWitch": "Witch",
"themeNameCwtch": "Cwtch",
"settingDownloadFolder": "Download Folder",
"settingImagePreviewsDescription": "Images and Profile Pictures will be downloaded and previewed automatically. We recommend that you do not enable this Experiment if you use Cwtch with untrusted contacts.",
"settingImagePreviews": "Image Previews and Profile Pictures",
"experimentClickableLinksDescription": "The clickable links experiment allows you to click on URLs shared in messages",
"enableExperimentClickableLinks": "Enable Clickable Links",
"serverConnectionsLabel": "Connection",
"serverTotalMessagesLabel": "Total Messages",
"serverMetricsLabel": "Server Metrics",
"manageKnownServersShort": "Servers",
"manageKnownServersLong": "Manage Known Servers",
"displayNameTooltip": "Please enter a display name",
"manageKnownServersButton": "Manage Known Servers",
"fieldDescriptionLabel": "Description",
"groupsOnThisServerLabel": "Groups I am in hosted on this server",
"importLocalServerButton": "Import %1",
"importLocalServerSelectText": "Select Local Server",
"importLocalServerLabel": "Import a locally hosted server",
"newMessagesLabel": "New Messages",
"localeRU": "Russian \/ Русский",
"copyServerKeys": "Copy keys",
"verfiyResumeButton": "Verify\/resume",
"fileCheckingStatus": "Checking download status",
"fileInterrupted": "Interrupted",
"fileSavedTo": "Saved to",
"encryptedServerDescription": "Encrypting a server with a password protects it from other people who may also use this device. Encrypted servers cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"plainServerDescription": "We recommend that you protect your Cwtch servers with a password. If you do not set a password on this server then anyone who has access to this device may be able to access information about this server, including sensitive cryptographic keys.",
"deleteServerConfirmBtn": "Really delete server",
"deleteServerSuccess": "Successfully deleted server",
"enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server",
"copyAddress": "Copy Address",
"settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers",
"settingServers": "Hosting Servers",
"enterServerPassword": "Enter password to unlock server",
"unlockProfileTip": "Please create or unlock a profile to begin!",
"unlockServerTip": "Please create or unlock a server to begin!",
"addServerTooltip": "Add new server",
"serversManagerTitleShort": "Servers",
"serversManagerTitleLong": "Servers You Host",
"saveServerButton": "Save Server",
"serverAutostartDescription": "Controls if the application will automatically launch the server on start",
"serverAutostartLabel": "Autostart",
"serverEnabledDescription": "Start or stop the server",
"serverEnabled": "Server Enabled",
"serverDescriptionDescription": "Your description of the server for personal management use only, will never be shared",
"serverDescriptionLabel": "Server Description",
"serverAddress": "Server Address",
"editServerTitle": "Edit Server",
"addServerTitle": "Add Server",
"titleManageProfilesShort": "Profiles",
"descriptionFileSharing": "The file sharing experiment allows you to send and receive files from Cwtch contacts and groups. Note that sharing a file with a group will result in members of that group connecting with you directly over Cwtch to download it.",
"settingFileSharing": "File Sharing",
"tooltipSendFile": "Send File",
"messageFileOffered": "Contact is offering to send you a file",
"messageFileSent": "You sent a file",
"messageEnableFileSharing": "Enable the file sharing experiment to view this message.",
"labelFilesize": "Size",
"labelFilename": "Filename",
"downloadFileButton": "Download",
"openFolderButton": "Open Folder",
"retrievingManifestMessage": "Retrieving file information...",
"descriptionStreamerMode": "If turned on, this option makes the app more visually private for streaming or presenting with, for example, hiding profile and contact addresses",
"streamerModeLabel": "Streamer\/Presentation Mode",
"archiveConversation": "Archive this Conversation",
"blockUnknownConnectionsEnabledDescription": "Connections from unknown contacts are blocked. You can change this in Settings",
"showMessageButton": "Show Message",
"blockedMessageMessage": "This message is from a profile you have blocked.",
"placeholderEnterMessage": "Type a message...",
"plainProfileDescription": "We recommend that you protect your Cwtch profiles with a password. If you do not set a password on this profile then anyone who has access to this device may be able to access information about this profile, including contacts, messages and sensitive cryptographic keys.",
"encryptedProfileDescription": "Encrypting a profile with a password protects it from other people who may also use this device. Encrypted profiles cannot be decrypted, displayed or accessed until the correct password is entered to unlock them.",
"addContactConfirm": "Add contact %1",
"addContact": "Add contact",
"contactGoto": "Go to conversation with %1",
"settingUIColumnOptionSame": "Same as portrait mode setting",
"settingUIColumnDouble14Ratio": "Double (1:4)",
"settingUIColumnDouble12Ratio": "Double (1:2)",
"settingUIColumnSingle": "Single",
"settingUIColumnLandscape": "UI Columns in Landscape Mode",
"settingUIColumnPortrait": "UI Columns in Portrait Mode",
"localePl": "Polish \/ Polski",
"tooltipRemoveThisQuotedMessage": "Remove quoted message.",
"tooltipReplyToThisMessage": "Reply to this message",
"tooltipRejectContactRequest": "Reject this contact request",
"tooltipAcceptContactRequest": "Accept this contact request.",
"notificationNewMessageFromGroup": "New message in a group!",
"notificationNewMessageFromPeer": "New message from a contact!",
"tooltipHidePassword": "Hide Password",
"tooltipShowPassword": "Show Password",
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
"shutdownCwtchAction": "Shutdown Cwtch",
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
"shutdownCwtchTooltip": "Shutdown Cwtch",
"malformedMessage": "Malformed message",
"profileDeleteSuccess": "Successfully deleted profile",
"debugLog": "Turn on console debug logging",
"torNetworkStatus": "Tor network status",
"addContactFirst": "Add or pick a contact to begin chatting.",
"createProfileToBegin": "Please create or unlock a profile to begin",
"nickChangeSuccess": "Profile nickname changed successfully",
"addServerFirst": "You need to add a server before you can create a group",
"deleteProfileSuccess": "Successfully deleted profile",
"sendInvite": "Send a contact or group invite",
"sendMessage": "Send Message",
"cancel": "Cancel",
"resetTor": "Reset",
"torStatus": "Tor Status",
"torVersion": "Tor Version",
"sendAnInvitation": "You sent an invitation for: ",
"contactSuggestion": "This is a contact suggestion for: ",
"rejected": "Rejected!",
"accepted": "Accepted!",
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
"newPassword": "New Password",
"yesLeave": "Yes, Leave This Conversation",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"leaveConversation": "Leave This Conversation",
"inviteToGroup": "You have been invited to join a group:",
"titleManageServers": "Manage Servers",
"successfullAddedContact": "Successfully added ",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionExperiments": "Cwtch experiments are optional, opt-in features that add additional functionality to Cwtch that may have different privacy considerations than traditional 1:1 metadata resistant chat e.g. group chat, bot integration etc.",
"titleManageProfiles": "Manage Cwtch Profiles",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"titleManageContacts": "Conversations",
"tooltipAddContact": "Add a new contact or conversation",
"tooltipOpenSettings": "Open the settings pane",
"contactAlreadyExists": "Contact Already Exists",
"invalidImportString": "Invalid import string",
"conversationSettings": "Conversation Settings",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"enableGroups": "Enable Group Chat",
"localeIt": "Italian \/ Italiano",
"localeEs": "Spanish \/ Español",
"todoPlaceholder": "Todo...",
"addNewItem": "Add a new item to the list",
"addListItem": "Add a New List Item",
"newConnectionPaneTitle": "New Connection",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connecting to network and contacts...",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"viewGroupMembershipTooltip": "View Group Membership",
"loadingTor": "Loading tor...",
"smallTextLabel": "Small",
"defaultScalingText": "Font Scaling",
"builddate": "Built on: %2",
"version": "Version %1",
"versionTor": "Version %1 with tor %2",
"experimentsEnabled": "Enable Experiments",
"themeDark": "Dark",
"themeLight": "Light",
"settingTheme": "Use Light Themes",
"largeTextLabel": "Large",
"settingInterfaceZoom": "Zoom level",
"localeDe": "German \/ Deutsch",
"localePt": "Portuguese \/ Portuguesa",
"localeFr": "French \/ Français",
"localeEn": "English \/ English",
"settingLanguage": "Language",
"blockUnknownLabel": "Block Unknown Contacts",
"zoomLabel": "Interface zoom (mostly affects text and button sizes)",
"versionBuilddate": "Version: %1 Built on: %2",
"cwtchSettingsTitle": "Cwtch Settings",
"unlock": "Unlock",
"yourServers": "Your Servers",
"yourProfiles": "Your Profiles",
"error0ProfilesLoadedForPassword": "0 profiles loaded with that password",
"password": "Password",
"enterProfilePassword": "Enter a password to view your profiles",
"addNewProfileBtn": "Add new profile",
"deleteConfirmText": "DELETE",
"deleteProfileConfirmBtn": "Really Delete Profile",
"deleteConfirmLabel": "Type DELETE to confirm",
"deleteProfileBtn": "Delete Profile",
"passwordChangeError": "Error changing password: Supplied password rejected",
"passwordErrorMatch": "Passwords do not match",
"saveProfileBtn": "Save Profile",
"createProfileBtn": "Create Profile",
"passwordErrorEmpty": "Password cannot be empty",
"password2Label": "Reenter password",
"password1Label": "Password",
"currentPasswordLabel": "Current Password",
"yourDisplayName": "Your Display Name",
"profileOnionLabel": "Send this address to contacts you want to connect with",
"noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted",
"radioNoPassword": "Unencrypted (No password)",
"radioUsePassword": "Password",
"editProfile": "Edit Profile",
"newProfile": "New Profile",
"defaultProfileName": "Alice",
"profileName": "Display name",
"editProfileTitle": "Edit Profile",
"addProfileTitle": "Add new profile",
"deleteBtn": "Delete",
"unblockBtn": "Unblock Contact",
"dontSavePeerHistory": "Delete History",
"savePeerHistoryDescription": "Determines whether to delete any history associated with the contact.",
"savePeerHistory": "Save History",
"blockBtn": "Block Contact",
"saveBtn": "Save",
"displayNameLabel": "Display Name",
"copiedToClipboardNotification": "Copied to Clipboard",
"addressLabel": "Address",
"puzzleGameBtn": "Puzzle Game",
"bulletinsBtn": "Bulletins",
"listsBtn": "Lists",
"chatBtn": "Chat",
"rejectGroupBtn": "Reject",
"acceptGroupBtn": "Accept",
"acceptGroupInviteLabel": "Do you want to accept the invitation to",
"newGroupBtn": "Create new group",
"copyBtn": "Copy",
"peerOfflineMessage": "Contact is offline, messages can't be delivered right now",
"peerBlockedMessage": "Contact is blocked",
"pendingLabel": "Pending",
"acknowledgedLabel": "Acknowledged",
"couldNotSendMsgError": "Could not send this message",
"dmTooltip": "Click to DM",
"membershipDescription": "Below is a list of users who have sent messages to the group. This list may not reflect all users who have access to the group.",
"addListItemBtn": "Add Item",
"peerNotOnline": "Contact is offline. Applications cannot be used right now.",
"searchList": "Search List",
"update": "Update",
"inviteBtn": "Invite",
"inviteToGroupLabel": "Invite to group",
"groupNameLabel": "Group Name",
"viewServerInfo": "Server Info",
"serverNotSynced": "Syncing New Messages (This can take some time)...",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
"serverConnectivityConnected": "Server Connected",
"serverInfo": "Server Information",
"invitationLabel": "Invitation",
"serverLabel": "Server",
"search": "Search...",
"blocked": "Blocked",
"pasteAddressToAddContact": "Paste a cwtch address, invitation or key bundle here to add a new conversation",
"titlePlaceholder": "title...",
"postNewBulletinLabel": "Post new bulletin",
"newBulletinLabel": "New Bulletin",
"joinGroup": "Join group",
"createGroup": "Create group",
"addPeer": "Add Contact",
"groupAddr": "Address",
"invitation": "Invitation",
"server": "Server",
"peerName": "Name",
"peerAddress": "Address",
"joinGroupTab": "Join a group",
"createGroupTab": "Create a group",
"addPeerTab": "Add a contact",
"createGroupBtn": "Create",
"defaultGroupName": "Awesome Group",
"createGroupTitle": "Create Group"
}

View File

@ -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() {

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:cwtch/config.dart';
import 'package:flutter/widgets.dart';
enum ModalState { none, storageMigration, shutdown }
@ -30,11 +31,13 @@ class AppState extends ChangeNotifier {
void SetAppError(String error) {
appError = error;
EnvironmentConfig.debugLog("App Error: ${appError}");
notifyListeners();
}
void SetModalState(ModalState newState) {
modalState = newState;
EnvironmentConfig.debugLog("Modal State: ${newState}");
notifyListeners();
}

View File

@ -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" && this.antispamTickets > 0;
} else {
return this.isOnline();
}
}
ConversationNotificationPolicy get notificationsPolicy => _notificationPolicy;
set notificationsPolicy(ConversationNotificationPolicy newVal) {

View File

@ -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.

View File

@ -4,6 +4,7 @@ import 'package:cwtch/config.dart';
import 'package:cwtch/models/message.dart';
import 'package:cwtch/models/messages/malformedmessage.dart';
import 'package:cwtch/widgets/malformedbubble.dart';
import 'package:cwtch/widgets/messageBubbleWidgetHelpers.dart';
import 'package:cwtch/widgets/messagerow.dart';
import 'package:cwtch/widgets/quotedmessage.dart';
import 'package:flutter/widgets.dart';
@ -39,15 +40,7 @@ class QuotedMessage extends Message {
);
var content = message["body"];
var formatMessages = Provider.of<Settings>(bcontext).isExperimentEnabled(FormattingExperiment);
return SelectableLinkify(
text: content + '\u202F',
options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: false, looseUrl: true, defaultToHttps: true),
linkifiers: [UrlLinkifier()],
onOpen: null,
textAlign: TextAlign.left,
style: TextStyle(fontSize: 12.0 * Provider.of<Settings>(context).fontScaling, fontWeight: FontWeight.normal, fontFamily: "Inter", overflow: TextOverflow.ellipsis),
codeStyle: TextStyle(fontSize: 12.0 * Provider.of<Settings>(context).fontScaling, fontWeight: FontWeight.normal, fontFamily: "Inter", overflow: TextOverflow.ellipsis),
textWidthBasis: TextWidthBasis.longestLine);
return compileMessageContentWidget(context, false, content, FocusNode(), formatMessages, false);
} catch (e) {
return MalformedBubble();
}

View File

@ -12,6 +12,7 @@ import 'package:provider/provider.dart';
import '../../settings.dart';
import '../../third_party/linkify/flutter_linkify.dart';
import '../../widgets/messageBubbleWidgetHelpers.dart';
class TextMessage extends Message {
final MessageMetadata metadata;
@ -25,20 +26,8 @@ class TextMessage extends Message {
value: this.metadata,
builder: (bcontext, child) {
var formatMessages = Provider.of<Settings>(bcontext).isExperimentEnabled(FormattingExperiment);
return SelectableLinkify(
text: content + '\u202F',
options: LinkifyOptions(messageFormatting: formatMessages, parseLinks: false, looseUrl: true, defaultToHttps: true),
linkifiers: [UrlLinkifier()],
onOpen: null,
textAlign: TextAlign.left,
style: TextStyle(overflow: TextOverflow.fade, fontFamily: "Inter", fontSize: 12.0 * Provider.of<Settings>(context).fontScaling),
linkStyle: TextStyle(overflow: TextOverflow.fade, fontFamily: "Inter", fontSize: 12.0 * Provider.of<Settings>(context).fontScaling),
codeStyle: TextStyle(
overflow: TextOverflow.ellipsis,
fontFamily: "RobotoMono",
),
textWidthBasis: TextWidthBasis.parent,
);
return compileMessageContentWidget(context, false, content, FocusNode(), formatMessages, false);
;
});
}

View File

@ -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");
}
}

View File

@ -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;

View File

@ -19,6 +19,13 @@ 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 TextStyle defaultDropDownMenuItemTextStyle = TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: 16);
final themes = {
cwtch_theme: {mode_light: CwtchLight(), mode_dark: CwtchDark()},
ghost_theme: {mode_light: GhostLight(), mode_dark: GhostDark()},
@ -177,6 +184,7 @@ ThemeData mkThemeData(Settings opaque) {
? opaque.current().defaultButtonDisabledColor
: null),
enableFeedback: true,
textStyle: MaterialStateProperty.all(opaque.scaleFonts(defaultTextButtonStyle)),
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
shape: MaterialStateProperty.all(RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6.0),
@ -186,9 +194,12 @@ ThemeData mkThemeData(Settings opaque) {
scrollbarTheme: ScrollbarThemeData(isAlwaysShown: false, thumbColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor)),
tabBarTheme: TabBarTheme(
labelColor: opaque.current().mainTextColor,
unselectedLabelColor: opaque.current().mainTextColor,
indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor))),
labelColor: opaque.current().mainTextColor,
unselectedLabelColor: opaque.current().mainTextColor,
indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor)),
labelStyle: opaque.scaleFonts(defaultTextButtonStyle),
unselectedLabelStyle: opaque.scaleFonts(defaultTextStyle),
),
dialogTheme: DialogTheme(
backgroundColor: opaque.current().backgroundPaneColor,
titleTextStyle: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, color: opaque.current().mainTextColor),
@ -199,8 +210,8 @@ ThemeData mkThemeData(Settings opaque) {
textTheme: TextTheme(
// NOTE: The following font scales were arrived at after consulting the material text scale
// docs: https://m3.material.io/styles/typography/type-scale-tokens and some trial and error
displayMedium: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
displaySmall: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
displayMedium: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
displayLarge: TextStyle(fontFamily: "Inter", fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
titleSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
titleLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
@ -212,8 +223,8 @@ ThemeData mkThemeData(Settings opaque) {
headlineMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 26.0, color: opaque.current().mainTextColor),
headlineLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.bold, fontSize: opaque.fontScaling * 28.0, color: opaque.current().mainTextColor),
labelSmall: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w100, fontSize: opaque.fontScaling * 14.0, color: opaque.current().mainTextColor),
labelLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w200, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
labelMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w300, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
labelMedium: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w300, fontSize: opaque.fontScaling * 16.0, color: opaque.current().mainTextColor),
labelLarge: TextStyle(fontFamily: "Inter", fontWeight: FontWeight.w200, fontSize: opaque.fontScaling * 18.0, color: opaque.current().mainTextColor),
),
switchTheme: SwitchThemeData(
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor),

View File

@ -128,7 +128,12 @@ 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),
style: settings.scaleFonts(defaultDropDownMenuItemTextStyle),
overflow: TextOverflow.ellipsis,
),
);
}).toList()))),
SwitchListTile(
@ -165,7 +170,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
items: themes.keys.map<DropdownMenuItem<String>>((String themeId) {
return DropdownMenuItem<String>(
value: themeId,
child: Text(getThemeName(context, themeId)), //"ddi_$themeId", key: Key("ddi_$themeId")),
child: Text(getThemeName(context, themeId), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)), //"ddi_$themeId", key: Key("ddi_$themeId")),
);
}).toList())),
leading: Icon(Icons.palette, color: settings.current().mainTextColor),
@ -185,7 +190,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
items: Settings.uiColumnModeOptions(false).map<DropdownMenuItem<String>>((DualpaneMode value) {
return DropdownMenuItem<String>(
value: value.toString(),
child: Text(Settings.uiColumnModeToString(value, context)),
child: Text(Settings.uiColumnModeToString(value, context), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
);
}).toList()))),
ListTile(
@ -193,7 +198,6 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
AppLocalizations.of(context)!.settingUIColumnLandscape,
textWidthBasis: TextWidthBasis.longestLine,
softWrap: true,
style: TextStyle(color: settings.current().mainTextColor),
),
leading: Icon(Icons.stay_primary_landscape, color: settings.current().mainTextColor),
trailing: Container(
@ -210,10 +214,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
items: Settings.uiColumnModeOptions(true).map<DropdownMenuItem<String>>((DualpaneMode value) {
return DropdownMenuItem<String>(
value: value.toString(),
child: Text(
Settings.uiColumnModeToString(value, context),
overflow: TextOverflow.ellipsis,
),
child: Text(Settings.uiColumnModeToString(value, context), overflow: TextOverflow.ellipsis, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
);
}).toList())))),
ListTile(
@ -305,10 +306,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
items: NotificationPolicy.values.map<DropdownMenuItem<NotificationPolicy>>((NotificationPolicy value) {
return DropdownMenuItem<NotificationPolicy>(
value: value,
child: Text(
Settings.notificationPolicyToString(value, context),
overflow: TextOverflow.ellipsis,
),
child: Text(Settings.notificationPolicyToString(value, context), overflow: TextOverflow.ellipsis, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
);
}).toList())),
leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
@ -328,10 +326,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
items: NotificationContent.values.map<DropdownMenuItem<NotificationContent>>((NotificationContent value) {
return DropdownMenuItem<NotificationContent>(
value: value,
child: Text(
Settings.notificationContentToString(value, context),
overflow: TextOverflow.ellipsis,
),
child: Text(Settings.notificationContentToString(value, context), overflow: TextOverflow.ellipsis, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
);
}).toList())),
leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
@ -747,6 +742,15 @@ String getLanguageFull(context, String languageCode, String? countryCode) {
if (languageCode == "ja") {
return AppLocalizations.of(context)!.localeJa;
}
if (languageCode == "sv") {
return AppLocalizations.of(context)!.localeSv;
}
if (languageCode == "sw") {
return AppLocalizations.of(context)!.localeSw;
}
if (languageCode == "uk") {
return AppLocalizations.of(context)!.localeUk;
}
return languageCode;
}

View File

@ -2,6 +2,7 @@ import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:cwtch/models/appstate.dart';
import 'package:cwtch/models/contact.dart';
import 'package:cwtch/models/profile.dart';
import 'package:cwtch/themes/opaque.dart';
import 'package:flutter/services.dart';
import 'package:cwtch/widgets/buttontextfield.dart';
import 'package:cwtch/widgets/cwtchlabel.dart';
@ -180,7 +181,7 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
icon: Icon(CwtchIcons.leave_group),
label: Text(
AppLocalizations.of(context)!.leaveConversation,
style: TextStyle(decoration: TextDecoration.underline),
style: settings.scaleFonts(defaultTextButtonStyle.copyWith(decoration: TextDecoration.underline)),
),
))
])

View File

@ -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,21 @@ 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 performingAntiSpam = Provider.of<ContactInfoState>(context, listen: false).antispamTickets == 0;
bool cannotSend = Provider.of<ContactInfoState>(context, listen: false).canSend() == false;
bool isGroup = Provider.of<ContactInfoState>(context, listen: false).isGroup;
if (isOffline || (isGroup && performingAntiSpam)) {
if (cannotSend) {
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 +343,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 +355,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 +387,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 +419,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 +463,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 +478,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 +490,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 +506,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 +522,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 +538,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 +554,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 +570,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 +607,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 +622,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 +641,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 +733,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 +742,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

View File

@ -1,4 +1,5 @@
import 'package:cwtch/models/appstate.dart';
import 'package:cwtch/themes/opaque.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
@ -52,7 +53,7 @@ class _SplashViewState extends State<SplashView> {
: appState.modalState == ModalState.storageMigration
? AppLocalizations.of(context)!.storageMigrationModalMessage
: AppLocalizations.of(context)!.shuttingDownApp, // Todo l10n AppLocalizations.of(context)!.storageMigrationModalMessage
style: TextStyle(
style: defaultTextButtonStyle.copyWith(
fontSize: 16.0, color: appState.appError == "" ? Provider.of<Settings>(context).theme.mainTextColor : Provider.of<Settings>(context).theme.textfieldErrorColor))),
Visibility(
visible: appState.modalState == ModalState.storageMigration || appState.modalState == ModalState.shutdown,

View File

@ -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),
);
});
}

View File

@ -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,

View File

@ -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,

View File

@ -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,
);
}

View File

@ -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) {

View File

@ -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';
@ -149,8 +150,8 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
if (fromMe) {
widgetRow = <Widget>[
wdgSpacer,
wdgSeeReplies,
wdgTranslateMessage,
wdgSeeReplies,
wdgReply,
actualMessage,
];
@ -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) {

View File

@ -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(

View File

@ -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);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 B

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

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.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB