Compare commits

...

57 Commits

Author SHA1 Message Date
Sarah Jamie Lewis e019f5f52d Merge pull request 'DisableProfile, SaveHistorySetting, DeleteServerInfo, Draft Whonix Config' (#710) from stable-blockers into trunk
Reviewed-on: cwtch.im/cwtch-ui#710
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-08-22 23:27:47 +00:00
Sarah Jamie Lewis a36a5ea2fe Fix UI Tests. 2023-08-22 23:27:41 +00:00
Sarah Jamie Lewis 8e5074ec98 Update Cwtch 2023-08-22 23:27:41 +00:00
Sarah Jamie Lewis afb00e9295 DisableProfile, SaveHistorySetting, DeleteServerInfo, Draft Whonix Config
Fixes #593
Fixes #690
Fixes #629
2023-08-22 23:27:41 +00:00
Dan Ballard e249492641 Merge pull request 'add label to scaling slider' (#705) from scalingLabel into trunk
Reviewed-on: cwtch.im/cwtch-ui#705
Reviewed-by: Sarah Jamie Lewis <sarah@openprivacy.ca>
2023-08-04 00:56:33 +00:00
Dan Ballard cead758f78 add label to scaling slider 2023-08-02 17:44:19 -07:00
Sarah Jamie Lewis 75b7e77bc1 Merge pull request 'Upgrade Cwtch Autobindings Version' (#704) from search into trunk
Reviewed-on: cwtch.im/cwtch-ui#704
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-08-02 20:24:16 +00:00
Sarah Jamie Lewis ad215635d2 Upgrade Cwtch Autobindings Version 2023-08-02 13:19:46 -07:00
Sarah Jamie Lewis a937301d59 Merge pull request 'Update Goldens' (#703) from search into trunk
Reviewed-on: cwtch.im/cwtch-ui#703
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-08-02 19:56:02 +00:00
Sarah Jamie Lewis b19724036d Update Goldens 2023-08-02 19:55:57 +00:00
Sarah Jamie Lewis 70914c7a87 Merge pull request 'Fix up a few fonts. Add more scaling options' (#702) from search into trunk
Reviewed-on: cwtch.im/cwtch-ui#702
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-08-02 19:52:50 +00:00
Sarah Jamie Lewis 4577541e23 Update MessageText size 2023-08-02 12:50:58 -07:00
Sarah Jamie Lewis 35da8daed6 Fix font scaling for profile sharing menu 2023-08-02 12:43:32 -07:00
Sarah Jamie Lewis 45a0b8b767 Fix NPE in FileSharingView when no files have been shared 2023-08-02 12:38:48 -07:00
Sarah Jamie Lewis 3e6c3faeda Update PopupMenu Scaling and CwtchTextField Default 2023-08-02 11:25:37 -07:00
Sarah Jamie Lewis 81f2d171aa Add default dropdown style to peer/group settings pages 2023-08-02 11:14:18 -07:00
Sarah Jamie Lewis 388257bbff Fix up a few fonts. Add more scaling options 2023-08-02 10:26:30 -07:00
Sarah Jamie Lewis 1b35f8a32b Merge pull request 'Support Conversation Search, Upgrade Cwtch, Patch support for downloading new Cwtch library name formats' (#699) from search into trunk
Reviewed-on: cwtch.im/cwtch-ui#699
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-08-02 17:25:07 +00:00
Sarah Jamie Lewis dee5752d38 Cleanup + Android Support 2023-08-02 09:49:36 -07:00
Sarah Jamie Lewis 6188dffbc0 Support Conversation Search, Upgrade Cwtch, Patch support for downloading new Cwtch library name formats 2023-08-02 09:45:37 -07:00
Sarah Jamie Lewis 3d9d707b83 Merge pull request 'Upgrade Cwtch' (#694) from repbuilds-additional into trunk
Reviewed-on: cwtch.im/cwtch-ui#694
2023-07-13 20:19:00 +00:00
Sarah Jamie Lewis baccdee90e Upgrade Cwtch 2023-07-13 20:09:23 +00:00
Sarah Jamie Lewis e55f7af49c Merge pull request 'Make tar archives deterministic' (#693) from repbuilds-additional into trunk
Reviewed-on: cwtch.im/cwtch-ui#693
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-07-10 20:33:56 +00:00
Sarah Jamie Lewis 632764b407 Make tar archives deterministic 2023-07-10 10:52:50 -07:00
Sarah Jamie Lewis 3148a8e064 Reduce Build Variance (Path to Reproducible Builds) (#692)
Pass flags through CMake to strip generated binaries and linked libraries of os-specific or build-specific information.

Replace the default linker script with an override that suppresses the .comment section which can contain OS-specific references.

Reviewed-on: cwtch.im/cwtch-ui#692
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-07-07 01:44:20 +00:00
Sarah Jamie Lewis cc4403261e Merge pull request 'Remove subscript formatting.' (#687) from 1.12-rc into trunk
Reviewed-on: cwtch.im/cwtch-ui#687
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-06-16 01:52:16 +00:00
Sarah Jamie Lewis c305f7ba23 Remove subscript formatting.
It is rarely used, and often conflicts with other formatting
2023-06-15 15:06:50 -07:00
Sarah Jamie Lewis 49fcbdf9aa Merge pull request 'Upgrade Cwtch, Update Languages, Fix DeleteContact flow, various smaller fixes' (#686) from 1.12-rc into trunk
Reviewed-on: cwtch.im/cwtch-ui#686
Reviewed-by: Dan Ballard <dan@openprivacy.ca>
2023-06-14 21:54:57 +00:00
Sarah Jamie Lewis 2ab0456dd5 Enable Tor Test (without explicit version check) 2023-06-14 14:40:13 -07:00
Sarah Jamie Lewis 5afd7c8dd8 Remove issue with shutdown feature 2023-06-14 14:28:42 -07:00
Sarah Jamie Lewis cccd669608 Renable 02_save_load test 2023-06-14 14:26:46 -07:00
Sarah Jamie Lewis 7f7e4536f2 Move around integration tests to prevent issues with flutter gherkin 2023-06-14 14:09:31 -07:00
Sarah Jamie Lewis 4a3770d0ec Make Tor Version Check nicer 2023-06-14 12:27:22 -07:00
Sarah Jamie Lewis c3cd8d5d66 Fixup integration tests 2023-06-14 11:57:49 -07:00
Sarah Jamie Lewis e416638e65 Upgrade Cwtch, Update Languages, Fix DeleteContact flow, various smaller fixes 2023-06-14 10:59:25 -07:00
Sarah Jamie Lewis 040692c01e Merge pull request 'update _FlDartProject struct and use new flutter apis for some settings' (#684) from f10lin into trunk
Reviewed-on: cwtch.im/cwtch-ui#684
2023-06-05 17:36:22 +00:00
Dan Ballard af8ed5ac78 update _FlDartProject struct and use new flutter apis for some settings 2023-06-04 17:54:20 -07:00
Sarah Jamie Lewis 59c5004153 Merge pull request 'dep-upgrades-3.10.0' (#673) from dep-upgrades-3.10.0 into trunk
Reviewed-on: cwtch.im/cwtch-ui#673
2023-06-02 20:31:25 +00:00
Sarah Jamie Lewis b183966980 Merge pull request 'Ongoing UI and Android Fixes on top of depenencies.' (#678) from ui-many-fixes into dep-upgrades-3.10.0
Reviewed-on: cwtch.im/cwtch-ui#678
2023-06-02 19:57:58 +00:00
Sarah Jamie Lewis a69f3cb46e Language Updates (Mostly Swahili) 2023-06-02 19:57:53 +00:00
Sarah Jamie Lewis aedc033df9 Update Goldens 2023-06-02 19:57:53 +00:00
Sarah Jamie Lewis 3f262afcac Add support for Ukrainian and additional Swahili translations 2023-06-02 19:57:53 +00:00
Sarah Jamie Lewis 2d18089721 More font fixups 2023-06-02 19:57:53 +00:00
Sarah Jamie Lewis 103c1e08b5 Add Support for Swedish and Swahili 2023-06-02 19:57:53 +00:00
Sarah Jamie Lewis ea701546e7 Fixup Translate Position to minimize space when hidden 2023-06-02 19:57:53 +00:00
Sarah Jamie Lewis a2a09966e6 More small fixes / remove extra logging 2023-06-02 19:57:52 +00:00
Sarah Jamie Lewis ea22864341 Fix Android File Downloading Stuck Notification / Small Settings Font Tweaks 2023-06-02 19:57:52 +00:00
Sarah Jamie Lewis 67a99c903a Fix Up Quotes 2023-06-02 19:57:52 +00:00
Sarah Jamie Lewis 01b5c41208 Update Cwtch 2023-06-02 19:57:52 +00:00
Sarah Jamie Lewis 22bf5cfe92 Small UI Fixes / Font Styles / Abstractions 2023-06-02 19:57:52 +00:00
Dan Ballard 7f50036968 drone win cp files from right place 2023-06-02 12:30:02 -07:00
Sarah Jamie Lewis 0687718803 Merge pull request 'revert win toast to 0.0.2' (#682) from win-fixes into dep-upgrades-3.10.0
Reviewed-on: cwtch.im/cwtch-ui#682
2023-06-02 17:40:46 +00:00
Dan Ballard 90625eacb5 revert win toast to 0.0.2 2023-06-02 17:40:39 +00:00
Dan Ballard 81d62a06e3 update drone to flutter 3.10 containers 2023-05-29 17:40:44 +00:00
Dan Ballard 38ec143ef6 build updates for macos flutter 3.10 2023-05-29 17:40:44 +00:00
Sarah Jamie Lewis 7237318c53 Merge pull request 'windows fixes: especially new version of win toast' (#677) from win-fixes into dep-upgrades-3.10.0
Reviewed-on: cwtch.im/cwtch-ui#677
2023-05-22 20:10:03 +00:00
Dan Ballard 471ab96743 windows fixes: especially new version of win toast 2023-05-22 12:54:40 -07:00
105 changed files with 2890 additions and 845 deletions

View File

@ -8,7 +8,7 @@ clone:
steps:
- name: clone
image: openpriv/flutter-desktop:linux-fstable-3.7.1
image: openpriv/flutter-desktop:linux-fstable-3.10.2
environment:
buildbot_key_b64:
from_secret: buildbot_key_b64
@ -24,7 +24,7 @@ steps:
- git checkout $DRONE_COMMIT
- name: fetch
image: openpriv/flutter-desktop:linux-fstable-3.7.1
image: openpriv/flutter-desktop:linux-fstable-3.10.2
volumes:
- name: deps
path: /root/.pub-cache
@ -47,7 +47,7 @@ steps:
# #Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
- name: build-linux
image: openpriv/flutter-desktop:linux-fstable-3.7.1
image: openpriv/flutter-desktop:linux-fstable-3.10.2
volumes:
- name: deps
path: /root/.pub-cache
@ -57,20 +57,21 @@ steps:
- mkdir -p deploy/cwtch
- cp -r build/linux/x64/release/bundle/* deploy/cwtch
- cd deploy
- tar -czf cwtch-`cat ../VERSION`.tar.gz cwtch
# Tar archives need a few tricks to make this deterministic, see https://reproducible-builds.org/docs/archives/
- tar --sort=name --mtime=`cat COMMIT_DATE` --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime -czf cwtch-`cat ../VERSION`.tar.gz cwtch
- rm -r cwtch
- name: linux-ui-tests
image: openpriv/flutter-desktop:linux-fstable-3.7.1
image: openpriv/flutter-desktop:linux-fstable-3.10.2
volumes:
- name: deps
path: /root/.pub-cache
commands:
# Run 01_general, 02_global_settings, and 04_profile_mgmt features...
- ./run-tests-headless.sh "01_general|02_global_settings|04_profile_mgmt"
# Run 01_general, 01_tor, 02_global_settings, and 04_profile_mgmt features...
- ./run-tests-headless.sh "01_general|01_tor|02_global_settings|04_profile_mgmt"
- name: test-build-android
image: openpriv/flutter-desktop:linux-fstable-3.7.1
image: openpriv/flutter-desktop:linux-fstable-3.10.2
when:
event: pull_request
volumes:
@ -80,7 +81,7 @@ steps:
- flutter build apk --debug
- name: build-android
image: openpriv/flutter-desktop:linux-fstable-3.7.1
image: openpriv/flutter-desktop:linux-fstable-3.10.2
when:
event: push
environment:
@ -104,7 +105,7 @@ steps:
#- cp build/app/outputs/flutter-apk/app-debug.apk deploy/android
- name: widget-tests
image: openpriv/flutter-desktop:linux-fstable-3.7.1
image: openpriv/flutter-desktop:linux-fstable-3.10.2
volumes:
- name: deps
path: /root/.pub-cache
@ -177,7 +178,7 @@ clone:
steps:
- name: clone
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.7.1
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.10.2
environment:
buildbot_key_b64:
from_secret: buildbot_key_b64
@ -195,7 +196,7 @@ steps:
- git checkout $Env:DRONE_COMMIT
- name: fetch
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.7.1
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.10.2
commands:
- git describe --tags --abbrev=1 > VERSION
- git log -1 --format=%cd --date=format:'%Y-%m-%d-%H-%M' > COMMIT_DATE
@ -203,7 +204,7 @@ steps:
- .\fetch-libcwtch-go.ps1
- name: build-windows
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.7.1
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.10.2
commands:
- flutter pub get
- $Env:version += type .\VERSION
@ -214,9 +215,9 @@ steps:
# flutter hasn't worked out it's packaging of required dll's so we have to resort to this manual nonsense
# https://github.com/google/flutter-desktop-embedding/issues/587
# https://github.com/flutter/flutter/issues/53167
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30133\x64\Microsoft.VC142.CRT\vcruntime140.dll $Env:releasedir
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30133\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $Env:releasedir
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30133\x64\Microsoft.VC142.CRT\msvcp140.dll $Env:releasedir
- copy 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Redist\MSVC\14.36.32532\x64\Microsoft.VC143.CRT\vcruntime140.dll' $Env:releasedir
- copy 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Redist\MSVC\14.36.32532\x64\Microsoft.VC143.CRT\vcruntime140_1.dll' $Env:releasedir
- copy 'C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Redist\MSVC\14.36.32532\x64\Microsoft.VC143.CRT\msvcp140.dll' $Env:releasedir
- copy README.md $Env:releasedir\
- copy windows\*.bat $Env:releasedir\
- powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:releasedir\Tor"
@ -260,7 +261,7 @@ steps:
- move *.sha512.txt deploy\$Env:builddir
- name: deploy-windows
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.7.1
image: openpriv/flutter-desktop:windows-sdk30-fstable-3.10.2
when:
event: push
status: [ success ]

View File

@ -1,10 +1,30 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
# This file should be version controlled.
version:
revision: 78910062997c3a836feee883712c241a5fd22983
revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
channel: stable
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
- platform: macos
create_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
base_revision: 4d9e56e694b656610ab87fcf2efbcd226e0ed8cf
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

View File

@ -1 +1 @@
2023-05-09-13-30-v0.0.3-24-g5b2f3cf
2023-08-22-14-06-v0.0.6

View File

@ -21,6 +21,13 @@ Cwtch processes the following environment variables:
- `LOG_FILE=` will reroute all of libcwtch-go's logging to the specified file instead of the console
- `LOG_LEVEL=debug` will set the log level to debug instead of info
## Running Tests
You can run specific tests with `./run-tests-headless.sh`. See also the `.drone.yml` file for information on the specific tests that run.1
The gherkin test framework will occasionally fail silently with incomplete test. This is usually because a previous run resulted in an exception and the underlying Tor
process was not cleaned up (See #711).
## Building
### Getting Started
@ -64,10 +71,14 @@ To build a release version and load normal profiles, use `build-release.sh X` in
### Building on MacOS
- Cocaopods is required, you may need to `gem install cocaopods -v 1.9.3`
- copy `libCwtch.x64.dylib` and `libCwtch.arm/dylib` into the root folder, or run `fetch-libcwtch-go-macos.sh` to download it
- Cocoapods is required. Mac and Ruby don't seem to care about version stability and compatibility so good luck. The version of Ruby Mac seems to ship is very incompatible with software in the gem repo. You will probablly need to manually specify a specific older version of cocoapods that is compatible with your system. First you will also probably need to make sure you are on the latest MacOS version and then try `gem install cocoapods -v 1.x.x`
- For MacOS 13.4 `gem install cocoapods -v 1.11.3` worked on 2023.05.28
- copy `libCwtch.x64.dylib` and `libCwtch.arm.dylib` into the root folder, or run `fetch-libcwtch-go-macos.sh` to download it
- The error 'Podfile not found' has still been seen on fresh repos, depending on varios versions. To fix run `flutter create --platforms=macos . --org im.cwtch`
- You may have to temporarily rename the project folder a "dart project suitable name" like "cwtch"
- run `fetch-tor-macos.sh` to fetch Tor or Download and install Tor Browser and `cp -r /Applications/Tor\ Browser.app/Contents/MacOS/Tor ./macos/`
- `flutter build macos`
- If you are building on Mac Arm Silicon you may need to append `--no-tree-shake-icons` as the build seems to invoke a mac x64 binary that Arm Mac "cannot verify" and therefor will not run on some versions of Flutter
- optional: launch cwtch-ui release build with `./build/macos/Build/Products/Release/Cwtch.app/Contents/MacOS/Cwtch`
- To package the UI: `./macos/package-release.sh`, which results in a Cwtch.dmg that has libCwtch.dylib and tor in it as well and can be installed into Applications

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

@ -343,7 +343,7 @@ class MainActivity: FlutterActivity() {
return
}
"RestartSharing" -> {
"RestartFileShare" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val filepath: String = call.argument("filekey") ?: ""
result.success(Cwtch.restartFileShare(profile, filepath))
@ -357,11 +357,17 @@ class MainActivity: FlutterActivity() {
return
}
"DeleteServerInfo" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val handle: String = call.argument("handle") ?: ""
result.success(Cwtch.deleteServerInfo(profile, handle))
return
}
"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 +499,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 +521,14 @@ 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
}
"SearchConversations" -> {
val profile: String = call.argument("ProfileOnion") ?: ""
val pattern: String = call.argument("pattern") ?: ""
result.success(Cwtch.searchConversations(profile, pattern))
return
}
"ReconnectCwtchForeground" -> {
Cwtch.reconnectCwtchForeground()

241
elf_x86_64.x Normal file
View File

@ -0,0 +1,241 @@
/* Slightly Modified Default linker script, for normal executables */
/* The only difference is that this version suppresses the .comment section. See DISCARD section`
/* Copyright (C) 2014-2022 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
"elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
.interp : { *(.interp) }
.note.gnu.build-id : { *(.note.gnu.build-id) }
.hash : { *(.hash) }
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.rela.init : { *(.rela.init) }
.rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
.rela.fini : { *(.rela.fini) }
.rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
.rela.data.rel.ro : { *(.rela.data.rel.ro .rela.data.rel.ro.* .rela.gnu.linkonce.d.rel.ro.*) }
.rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
.rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
.rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
.rela.ctors : { *(.rela.ctors) }
.rela.dtors : { *(.rela.dtors) }
.rela.got : { *(.rela.got) }
.rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
.rela.ldata : { *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*) }
.rela.lbss : { *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*) }
.rela.lrodata : { *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*) }
.rela.ifunc : { *(.rela.ifunc) }
.rela.plt :
{
*(.rela.plt)
PROVIDE_HIDDEN (__rela_iplt_start = .);
*(.rela.iplt)
PROVIDE_HIDDEN (__rela_iplt_end = .);
}
.relr.dyn : { *(.relr.dyn) }
.init :
{
KEEP (*(SORT_NONE(.init)))
}
.plt : { *(.plt) *(.iplt) }
.plt.got : { *(.plt.got) }
.plt.sec : { *(.plt.sec) }
.text :
{
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
*(.text.exit .text.exit.*)
*(.text.startup .text.startup.*)
*(.text.hot .text.hot.*)
*(SORT(.text.sorted.*))
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf.em. */
*(.gnu.warning)
}
.fini :
{
KEEP (*(SORT_NONE(.fini)))
}
PROVIDE (__etext = .);
PROVIDE (_etext = .);
PROVIDE (etext = .);
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
.rodata1 : { *(.rodata1) }
.eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
.eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
.gnu_extab : ONLY_IF_RO { *(.gnu_extab*) }
/* These sections are generated by the Sun/Oracle C++ compiler. */
.exception_ranges : ONLY_IF_RO { *(.exception_ranges*) }
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
. = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
/* Exception handling */
.eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
.gnu_extab : ONLY_IF_RW { *(.gnu_extab) }
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
.exception_ranges : ONLY_IF_RW { *(.exception_ranges*) }
/* Thread Local Storage sections */
.tdata :
{
PROVIDE_HIDDEN (__tdata_start = .);
*(.tdata .tdata.* .gnu.linkonce.td.*)
}
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
PROVIDE_HIDDEN (__init_array_end = .);
}
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
PROVIDE_HIDDEN (__fini_array_end = .);
}
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
.dtors :
{
KEEP (*crtbegin.o(.dtors))
KEEP (*crtbegin?.o(.dtors))
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
KEEP (*(SORT(.dtors.*)))
KEEP (*(.dtors))
}
.jcr : { KEEP (*(.jcr)) }
.data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
.dynamic : { *(.dynamic) }
.got : { *(.got) *(.igot) }
. = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
.got.plt : { *(.got.plt) *(.igot.plt) }
.data :
{
*(.data .data.* .gnu.linkonce.d.*)
SORT(CONSTRUCTORS)
}
.data1 : { *(.data1) }
_edata = .; PROVIDE (edata = .);
. = .;
__bss_start = .;
.bss :
{
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
/* Align here to ensure that the .bss section occupies space up to
_end. Align after .bss to ensure correct alignment even if the
.bss section disappears because there are no input sections.
FIXME: Why do we need it? When there is no .bss section, we do not
pad the .data section. */
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
.lbss :
{
*(.dynlbss)
*(.lbss .lbss.* .gnu.linkonce.lb.*)
*(LARGE_COMMON)
}
. = ALIGN(64 / 8);
. = SEGMENT_START("ldata-segment", .);
.lrodata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{
*(.lrodata .lrodata.* .gnu.linkonce.lr.*)
}
.ldata ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
{
*(.ldata .ldata.* .gnu.linkonce.l.*)
. = ALIGN(. != 0 ? 64 / 8 : 1);
}
. = ALIGN(64 / 8);
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1. */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions. */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2. */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2. */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
/* SGI/MIPS DWARF 2 extensions. */
.debug_weaknames 0 : { *(.debug_weaknames) }
.debug_funcnames 0 : { *(.debug_funcnames) }
.debug_typenames 0 : { *(.debug_typenames) }
.debug_varnames 0 : { *(.debug_varnames) }
/* DWARF 3. */
.debug_pubtypes 0 : { *(.debug_pubtypes) }
.debug_ranges 0 : { *(.debug_ranges) }
/* DWARF 5. */
.debug_addr 0 : { *(.debug_addr) }
.debug_line_str 0 : { *(.debug_line_str) }
.debug_loclists 0 : { *(.debug_loclists) }
.debug_macro 0 : { *(.debug_macro) }
.debug_names 0 : { *(.debug_names) }
.debug_rnglists 0 : { *(.debug_rnglists) }
.debug_str_offsets 0 : { *(.debug_str_offsets) }
.debug_sup 0 : { *(.debug_sup) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) *(.comment) }
}

View File

@ -4,4 +4,5 @@ VERSION=`cat LIBCWTCH-GO.version`
echo $VERSION
curl --fail https://build.openprivacy.ca/files/libCwtch-autobindings-$VERSION/android/cwtch.aar --output android/cwtch/cwtch.aar
curl --fail https://build.openprivacy.ca/files/libCwtch-autobindings-$VERSION/linux/libCwtch.so --output linux/libCwtch.so
# FIXME...at some point we need to support different linux architectures...for now rely on existing expectations and rename x64 lib
curl --fail https://build.openprivacy.ca/files/libCwtch-autobindings-$VERSION/linux/libCwtch.x64.so --output linux/libCwtch.so

View File

@ -5,6 +5,7 @@ Feature: Settings pane opens and can save settings persistently
And I tap the 'OpenSettingsView' button
And I wait until the text 'Cwtch Settings' is present
And I take a screenshot
And I wait for 5 seconds
Scenario: Change every setting (except Language)
Given I wait until the widget with type 'ProfileMgrView' is present

View File

@ -1,8 +1,9 @@
Feature: Shutdown Cwtch button works correctly
Scenario: Clicking 'Shutdown Cwtch' shuts down Cwtch
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the button with tooltip 'Shutdown Cwtch'
Then I expect the text 'Shutdown Cwtch?' to be present
#Feature: Shutdown Cwtch button works correctly
# Scenario: Clicking 'Shutdown Cwtch' shuts down Cwtch
# Given I wait until the widget with type 'ProfileMgrView' is present
# And I tap the button with tooltip 'Shutdown Cwtch'
# Then I expect the text 'Shutdown Cwtch?' to be present
# And I wait for 5 seconds
#this also kills the testing framework sadly. will have to find a workaround
#And I tap the button that contains the text 'Shutdown Cwtch'
#Then I wait until the widget with type 'ProfileMgrView' is absent

View File

@ -2,7 +2,6 @@ Feature: Tor initializes correctly
Scenario: Check the Tor version
Given I wait until the widget with type 'ProfileMgrView' is present
And I tap the icon with type "TorIcon"
Then I expect the Tor version to be present
And I expect the string 'Online' to be present within 120 seconds
Scenario: Reset Tor

View File

@ -1,12 +1,14 @@
@env:clean
Feature: Splash screen displays and then closes
Scenario: splash screen appears
Then I expect the widget 'SplashView' to be present within 10 seconds
Then I expect the widget 'ProfileManagerView' to be present within 10 seconds
# first-run of cwtch creates expected files and folders
Then I expect the folder 'integration_test/env/temp' to exist
And I expect the folder 'integration_test/env/temp/dev' to exist
And I expect the file 'integration_test/env/temp/dev/SALT' to exist
Then I wait for the file 'integration_test/env/temp/dev/ui.globals' to exist
And I expect the file 'integration_test/env/temp/dev/ui.globals' to exist
And I expect the folder 'integration_test/env/temp/dev/tor' to exist
And I expect the file 'integration_test/env/temp/dev/tor/torrc' to exist
And I expect the file 'integration_test/env/temp/dev/tor/torrc' to exist
And I wait for 5 seconds

View File

@ -32,6 +32,9 @@ Feature: Basic Profile Management
Scenario: Load Unencrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present
Then I expect a "ProfileRow" widget with text "Alice (Unencrypted)"
# This test is too short...if the test finishes before flutter has finished initializing then
# the framework gets very confused...
And I wait for 2 seconds
Scenario: Create Encrypted Profile
Given I wait until the widget with type 'ProfileMgrView' is present

View File

@ -56,6 +56,7 @@ void main() async {
// files
FolderExists(),
FileExists(),
WaitFileExists(),
];
var sb = StringBuffer();

View File

@ -19,3 +19,16 @@ StepDefinitionGeneric FileExists() {
},
);
}
StepDefinitionGeneric WaitFileExists() {
return then1<String, FlutterWorld>(
RegExp(r'I wait for the file {string} to exist'),
(input1, context) async {
await context.world.appDriver.waitUntil(
() async {
await context.world.appDriver.waitForAppToSettle();
return File(input1).existsSync();
});
},
);
}

View File

@ -33,7 +33,6 @@ StepDefinitionGeneric TorVersionPresent() {
break;
}
}
print('File is now closed.');
} catch (e) {
print('Error: $e');
}
@ -41,15 +40,18 @@ StepDefinitionGeneric TorVersionPresent() {
context.expect(versionString, "#.#.#", reason: "error reading version string from fetch-tor.sh");
return;
}
context.world.attach(versionString, "text/plain", "Then I expect the Tor version to be present");
context.world.attach( versionString.substring(0,4), "text/plain", "Then I expect the Tor version to be present");
//context.reporter.message("test!!!", MessageLevel.info);
print("looking for version string $versionString");
final finder = context.world.appDriver.findBy(
versionString,
FindType.text,
);
final isP = await context.world.appDriver.isPresent(finder);
context.expect(isP, true);
return await context.world.appDriver.waitUntil(() async {
context.world.appDriver.waitForAppToSettle();
final finder = context.world.appDriver.findBy(
versionString.substring(0,4),
FindType.text,
);
return await context.world.appDriver.isPresent(finder);
});
},
);
}

View File

@ -19,3 +19,4 @@ StepDefinitionGeneric TakeScreenshot() {
},
);
}

View File

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View File

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

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
@ -143,4 +143,8 @@ abstract class Cwtch {
Future<String> TranslateMessage(String profile, int conversation, int message, String language);
bool IsBlodeuweddSupported();
// ignore: non_constant_identifier_names
Future<String> SearchConversations(String profile, String pattern);
void DeleteServerInfo(String profile, String handle);
}

View File

@ -59,7 +59,7 @@ class CwtchNotifier {
}
void handleMessage(String type, dynamic data) {
//EnvironmentConfig.debugLog("NewEvent $type $data");
// EnvironmentConfig.debugLog("NewEvent $type $data");
switch (type) {
case "CwtchStarted":
appState.SetCwtchInit();
@ -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;
@ -162,10 +161,8 @@ class CwtchNotifier {
}
break;
case "DeleteContact":
profileCN.getProfile(data["ProfileOnion"])?.contactList.removeContact(data["ConversationID"]);
break;
case "DeleteGroup":
profileCN.getProfile(data["ProfileOnion"])?.contactList.removeContact(data["ConversationID"]);
var identifier = int.parse(data["ConversationID"]);
profileCN.getProfile(data["ProfileOnion"])?.contactList.removeContact(identifier);
break;
case "PeerStateChange":
ContactInfoState? contact = profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(data["RemotePeer"]);
@ -327,6 +324,7 @@ class CwtchNotifier {
torStatus.updateVersion(data["Data"]);
break;
case "UpdateServerInfo":
EnvironmentConfig.debugLog("NewEvent UpdateServerInfo $type $data");
profileCN.getProfile(data["ProfileOnion"])?.replaceServers(data["ServerList"]);
break;
case "TokenManagerInfo":
@ -392,6 +390,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") {
@ -462,6 +461,12 @@ class CwtchNotifier {
profileCN.getProfile(data["ProfileOnion"])?.contactList.findContact(handle)?.acnCircuit = data["Data"];
}
break;
case "SearchResult":
String searchID = data["SearchID"];
var conversationIdentifier = int.parse(data["ConversationID"]);
var messageIndex = int.parse(data["RowIndex"]);
profileCN.getProfile(data["ProfileOnion"])?.handleSearchResult(searchID, conversationIdentifier, messageIndex);
break;
default:
EnvironmentConfig.debugLog("unhandled event: $type");
}

View File

@ -563,7 +563,7 @@ class CwtchFfi implements Cwtch {
@override
// ignore: non_constant_identifier_names
void DeleteContact(String profileOnion, int handle) {
var deleteContact = library.lookup<NativeFunction<string_int_to_void_function>>("c_DeleteContact");
var deleteContact = library.lookup<NativeFunction<string_int_to_void_function>>("c_DeleteConversation");
// ignore: non_constant_identifier_names
final DeleteContact = deleteContact.asFunction<VoidFromStringIntFn>();
final u1 = profileOnion.toNativeUtf8();
@ -788,7 +788,7 @@ class CwtchFfi implements Cwtch {
@override
// ignore: non_constant_identifier_names
Future<String> GetMessageByID(String profile, int handle, int index) async {
var getMessageC = library.lookup<NativeFunction<get_json_blob_from_str_int_int_function>>("c_GetMessageByID");
var getMessageC = library.lookup<NativeFunction<get_json_blob_from_str_int_int_function>>("c_GetMessageById");
// ignore: non_constant_identifier_names
final GetMessage = getMessageC.asFunction<GetJsonBlobFromStrIntIntFn>();
final utf8profile = profile.toNativeUtf8();
@ -880,7 +880,7 @@ class CwtchFfi implements Cwtch {
@override
void RestartSharing(String profile, String filekey) {
var restartSharingC = library.lookup<NativeFunction<void_from_string_string_function>>("c_RestartSharing");
var restartSharingC = library.lookup<NativeFunction<void_from_string_string_function>>("c_RestartFileShare");
// ignore: non_constant_identifier_names
final RestartSharing = restartSharingC.asFunction<VoidFromStringStringFn>();
final utf8profile = profile.toNativeUtf8();
@ -902,6 +902,18 @@ class CwtchFfi implements Cwtch {
malloc.free(ut8filekey);
}
@override
void DeleteServerInfo(String profile, String handle) {
var deleteServerInfoC = library.lookup<NativeFunction<void_from_string_string_function>>("c_DeleteServerInfo");
// ignore: non_constant_identifier_names
final StopSharing = deleteServerInfoC.asFunction<VoidFromStringStringFn>();
final utf8profile = profile.toNativeUtf8();
final ut8handle = handle.toNativeUtf8();
StopSharing(utf8profile, utf8profile.length, ut8handle, ut8handle.length);
malloc.free(utf8profile);
malloc.free(ut8handle);
}
@override
void UpdateSettings(String json) {
var updateSettings = library.lookup<NativeFunction<string_to_void_function>>("c_UpdateSettings");
@ -912,6 +924,9 @@ class CwtchFfi implements Cwtch {
malloc.free(u1);
}
@override
bool IsServersCompiled() {
return library.providesSymbol("c_LoadServers");
@ -967,7 +982,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 +997,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 +1022,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
@ -1027,4 +1042,20 @@ class CwtchFfi implements Cwtch {
malloc.free(utf8profile);
malloc.free(utf8onion);
}
@override
Future<String> SearchConversations(String profile, String pattern) async {
var searchConversationsC = library.lookup<NativeFunction<string_string_to_string_function>>("c_SearchConversations");
// ignore: non_constant_identifier_names
final SearchConversations = searchConversationsC.asFunction<StringFromStringStringFn>();
final utf8profile = profile.toNativeUtf8();
final utf8pattern = pattern.toNativeUtf8();
EnvironmentConfig.debugLog("Searching for $profile $pattern");
Pointer<Utf8> searchIDRaw = SearchConversations(utf8profile, utf8profile.length, utf8pattern, utf8pattern.length);
String searchID = searchIDRaw.toDartString();
_UnsafeFreePointerAnyUseOfThisFunctionMustBeDoubleApproved(searchIDRaw);
malloc.free(utf8profile);
malloc.free(utf8pattern);
return searchID;
}
}

View File

@ -352,7 +352,7 @@ class CwtchGomobile implements Cwtch {
@override
void RestartSharing(String profile, String filekey) {
cwtchPlatform.invokeMethod("RestartSharing", {"ProfileOnion": profile, "filekey": filekey});
cwtchPlatform.invokeMethod("RestartFileShare", {"ProfileOnion": profile, "filekey": filekey});
}
@override
@ -360,6 +360,12 @@ class CwtchGomobile implements Cwtch {
cwtchPlatform.invokeMethod("StopSharing", {"ProfileOnion": profile, "filekey": filekey});
}
@override
void DeleteServerInfo(String profile, String handle) {
cwtchPlatform.invokeMethod("DeleteServerInfo", {"ProfileOnion": profile, "handle": handle});
}
@override
void UpdateSettings(String json) {
cwtchPlatform.invokeMethod("UpdateSettings", {"json": json});
@ -390,25 +396,34 @@ 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
void AttemptReconnection(String profile, String onion) {
cwtchPlatform.invokeMethod("PeerWithOnion", {"ProfileOnion": profile, "onion": onion});
}
@override
Future<String> SearchConversations(String profile, String pattern) async {
return await cwtchPlatform.invokeMethod("SearchConversations", {"ProfileOnion": profile, "pattern": pattern});
}
}

View File

@ -1,6 +1,16 @@
{
"@@locale": "cy",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Mewn gwirionedd dileu gweinydd",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Grwpiau rydw i'n eu cynnal ar y gweinydd hwn",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -94,7 +103,6 @@
"serverMetricsLabel": "Metrigau Gweinydd",
"manageKnownServersLong": "Rheoli Gweinyddwyr Hysbys",
"manageKnownServersButton": "Rheoli Gweinyddwyr Hysbys",
"groupsOnThisServerLabel": "Grwpiau rydw i'n eu cynnal ar y gweinydd hwn",
"importLocalServerSelectText": "Dewiswch Gweinyddwr Lleol",
"importLocalServerLabel": "Mewnforio gweinydd a letyir yn lleol",
"enterCurrentPasswordForDeleteServer": "Rhowch y cyfrinair cyfredol i ddileu'r gweinydd hwn",
@ -110,7 +118,6 @@
"torSettingsCustomControlPortDescription": "Defnyddiwch borthladd wedi'i deilwra ar gyfer cysylltiadau rheoli i'r dirprwy Tor",
"torSettingsUseCustomTorServiceConfiguration": "Defnyddiwch Ffurfweddiad Gwasanaeth Custom Tor (torrc)",
"fileSharingSettingsDownloadFolderDescription": "Pan fydd ffeiliau'n cael eu llwytho i lawr yn awtomatig (ee ffeiliau delwedd, pan fydd rhagolygon delwedd yn cael eu galluogi) mae angen lleoliad rhagosodedig i lawrlwytho'r ffeiliau iddo.",
"deleteServerConfirmBtn": "Mewn gwirionedd dileu gweinydd",
"deleteServerSuccess": "Wedi dileu gweinydd yn llwyddiannus",
"enterServerPassword": "Rhowch gyfrinair i ddatgloi gweinydd",
"unlockProfileTip": "Crëwch neu ddatgloi proffil i ddechrau!",

View File

@ -1,6 +1,16 @@
{
"@@locale": "da",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Ja fjern server",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Grupper som jeg er vært for på denne server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -156,7 +165,6 @@
"displayNameTooltip": "Vælg et navn til præsentation",
"manageKnownServersButton": "Administrer kendte Servere",
"fieldDescriptionLabel": "Beskrivelse",
"groupsOnThisServerLabel": "Grupper som jeg er vært for på denne server",
"importLocalServerButton": "Importer %1",
"importLocalServerSelectText": "Vælg lokal Server",
"importLocalServerLabel": "Importer en lokalt administreret server",
@ -168,7 +176,6 @@
"fileSavedTo": "Gemt på",
"encryptedServerDescription": "Ved at kryptere en server med et password beskytter du mod angreb fra andre brugere af denne enhed. Krypterede servere kan ikke tilgås, aflæses eller vises før det korrekte password er blevet brugt til at åbne dem.",
"plainServerDescription": "Vi anbefaler at du beskytter dine Cwtch servere med et password. Hvis ikke du sætter et password kan alle med adgang til denne enhed indsamle information om denne server samt hente beskyttede krypteringsnøgler.",
"deleteServerConfirmBtn": "Ja fjern server",
"deleteServerSuccess": "Fjernede server",
"enterCurrentPasswordForDeleteServer": "Indtast nuværende password for at fjerne denne server",
"copyAddress": "Kopier Adresse",

View File

@ -1,11 +1,21 @@
{
"@@locale": "de",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Server wirklich löschen",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Gruppen, in denen ich bin, werden auf diesem Server gehostet",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"retryConnection": "Wiederholen",
"retryConnectionTooltip": "Cwtch wiederholt die Versuche regelmäßig, aber du kannst Cwtch anweisen, es früher zu versuchen, indem du diese Taste drückst.",
"localeJa": "Japanisch \/ 日本語",
"fontScalingDescription": "Passe den relativen Skalierungsfaktor der Schrift an, der auf Text und Widgets angewendet wird.",
"localeSv": "Schwedisch \/ Svenska",
"localeSw": "Suaheli \/ Kiswahili",
"localeUk": "Ukrainisch \/ українською",
"profileEnabledDescription": "Starten oder Stoppen des Profils",
"defaultScalingText": "Text in Standardgröße (Skalierungsfaktor:",
"localeJa": "Japanese \/ 日本語",
"retryConnectionTooltip": "Cwtch retries peers regularly, but you can tell Cwtch to try sooner by pushing this button.",
"retryConnection": "Retry",
"blodeuweddExperimentEnable": "Blodeuwedd Assistent",
"blodeuweddDescription": "Der Blodeuwedd-Assistent erweitert Cwtch um neue Funktionen wie die Zusammenfassung von Chat-Transkripten und die Übersetzung von Nachrichten über ein lokal gehostetes Sprachmodell.",
"blodeuweddNotSupported": "Diese Version von Cwtch wurde ohne Unterstützung für den Blodeuwedd-Assistenten kompiliert.",
@ -26,7 +36,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...",
@ -282,7 +291,6 @@
"copyAddress": "Adresse kopieren",
"enterCurrentPasswordForDeleteServer": "Bitte gib das aktuelle Passwort ein, um diesen Server zu löschen",
"deleteServerSuccess": "Server erfolgreich gelöscht",
"deleteServerConfirmBtn": "Server wirklich löschen",
"plainServerDescription": "Wir empfehlen, dass du deinen Cwtch-Server mit einem Passwort schützst. Wenn Du diesen Server nicht mit einem Kennwort versiehst, kann jeder, der Zugang zu diesem Gerät hat, auf Informationen über diesen Server zugreifen, einschließlich sensibler kryptografischer Schlüssel.",
"encryptedServerDescription": "Das Verschlüsseln eines Servers mit einem Kennwort schützt ihn vor anderen Personen, die dieses Gerät ebenfalls benutzen könnten. Verschlüsselte Server können nicht entschlüsselt, angezeigt oder aufgerufen werden, bis das richtige Kennwort eingegeben wird, um sie zu entsperren.",
"fileSavedTo": "Gesichert in",
@ -294,7 +302,6 @@
"importLocalServerLabel": "Importieren eines lokal gehosteten Servers",
"importLocalServerSelectText": "Lokalen Server auswählen",
"importLocalServerButton": "%1 importieren",
"groupsOnThisServerLabel": "Gruppen, in denen ich bin, werden auf diesem Server gehostet",
"fieldDescriptionLabel": "Beschreibung",
"manageKnownServersButton": "Bekannte Server verwalten",
"displayNameTooltip": "Bitte gib einen Anzeigenamen ein",

View File

@ -1,6 +1,16 @@
{
"@@locale": "el",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Really Delete Server?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Known Groups on this Cwtch Server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -199,11 +208,9 @@
"experimentClickableLinksDescription": "The clickable links experiment allows you to click on URLs shared in messages",
"enableExperimentClickableLinks": "Enable Clickable Links",
"serverMetricsLabel": "Server Metrics",
"groupsOnThisServerLabel": "Groups I am in hosted on this server",
"importLocalServerButton": "Import %1",
"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",
"settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers",
"settingServers": "Hosting Servers",
"unlockProfileTip": "Please create or unlock a profile to begin!",

View File

@ -1,6 +1,16 @@
{
"@@locale": "en",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Really Delete Server?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Known Groups on this Cwtch Server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -154,7 +163,6 @@
"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",
@ -167,7 +175,6 @@
"fileSavedTo": "Saved to",
"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.",
"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.",
"deleteServerConfirmBtn": "Really delete server",
"deleteServerSuccess": "Successfully deleted server",
"enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server",
"copyAddress": "Copy Address",

View File

@ -1,6 +1,16 @@
{
"@@locale": "es",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Realmente eliminar el servidor",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Grupos alojados en este servidor en los que estoy",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -129,8 +138,6 @@
"torSettingsCustomControlPort": "Puerto de control personalizado",
"torSettingsCustomSocksPortDescription": "Usar un puerto personalizad para conexiones de datos al proxy Tor",
"serverMetricsLabel": "Métricas del servidor",
"groupsOnThisServerLabel": "Grupos alojados en este servidor en los que estoy",
"deleteServerConfirmBtn": "Realmente eliminar el servidor",
"settingServersDescription": "El experimento de alojar servidores permite alojar y administrar servidores de Cwtch",
"settingServers": "Alojar Servidores",
"unlockProfileTip": "Crea o desbloquea un perfil para comenzar!",

View File

@ -1,6 +1,16 @@
{
"@@locale": "fr",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Supprimer vraiment le serveur",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Les groupes dont je fais partie sont hébergés sur ce serveur",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -165,7 +174,6 @@
"importLocalServerSelectText": "Sélectionnez le serveur local",
"importLocalServerLabel": "Importer un serveur hébergé localement",
"importLocalServerButton": "Importer %1",
"groupsOnThisServerLabel": "Les groupes dont je fais partie sont hébergés sur ce serveur",
"fieldDescriptionLabel": "Description",
"savePeerHistoryDescription": "Détermine s'il faut ou non supprimer tout historique associé au contact.",
"newMessagesLabel": "Nouveaux messages",
@ -178,7 +186,6 @@
"enterCurrentPasswordForDeleteServer": "Veuillez saisir le mot de passe actuel pour supprimer ce serveur",
"encryptedServerDescription": "Le chiffrement dun serveur avec un mot de passe le protège des autres personnes qui peuvent également utiliser cet appareil. Les serveurs cryptés ne peuvent pas être déchiffrés, affichés ou accessibles tant que le mot de passe correct nest pas entré pour les déverrouiller.",
"deleteServerSuccess": "Le serveur a été supprimé avec succès",
"deleteServerConfirmBtn": "Supprimer vraiment le serveur",
"unlockServerTip": "Veuillez créer ou déverrouiller un serveur pour commencer !",
"unlockProfileTip": "Veuillez créer ou déverrouiller un profil pour commencer !",
"settingServersDescription": "L'expérience des serveurs d'hébergement permet d'héberger et de gérer les serveurs Cwtch.",

View File

@ -1,34 +1,43 @@
{
"@@locale": "it",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Elimina davvero il server",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Gruppi di cui sono parte su questo server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"retryConnectionTooltip": "Cwtch riprova i peer regolarmente, ma puoi dire a Cwtch di provare prima premendo questo pulsante.",
"localeJa": "Giapponese \/ 日本語",
"fontScalingDescription": "Regola il fattore di scala relativo dei caratteri applicato al testo e ai widget.",
"localeSv": "Svedese \/ Svenska",
"localeSw": "Swahili \/ Kiswahili",
"localeUk": "Ucraino \/українською",
"localeNl": "Olandese \/ Dutch",
"localePtBr": "Portoghese brasiliano \/ Português do Brasil",
"profileAutostartLabel": "Avvio automatico",
"profileEnabled": "Abilita",
"profileAutostartDescription": "Controlla se il profilo verrà lanciato automaticamente all'avvio.",
"profileEnabledDescription": "Attiva o disattiva il profilo.",
"localeSk": "Slovacco \/ Slovák",
"localeKo": "Coreano \/ 한국어",
"blodeuweddExperimentEnable": "Assistente Blodeuwedd",
"blodeuweddDescription": "L'assistente Blodeuwedd aggiunge nuove funzionalità a Cwtch come il riepilogo della trascrizione della chat e la traduzione dei messaggi tramite un modello linguistico ospitato localmente.",
"blodeuweddNotSupported": "Questa versione di Cwtch è stata compilata senza il supporto per l'assistente Blodeuwedd.",
"blodeuweddPath": "La directory in cui si trova Blodeuwedd sul tuo computer.",
"blodeuweddSummarize": "Riassumi la conversazione",
"blodeuweddTranslate": "Traduci il messaggio",
"blodeuweddProcessing": "Blodeuwedd sta elaborando...",
"blodeuweddWarning": "Blodeuwedd utilizza un modello linguistico locale e una serie di piccoli modelli ausiliari per alimentare la sua funzionalità. Queste tecniche sono spesso molto efficaci ma non prive di errori. \n\nSebbene ci siamo impegnati per ridurre al minimo il rischio, esiste ancora la possibilità che i risultati di Blodeuwedd siano errati, allucinati e\/o offensivi. \n\nPer questo Blodeuwedd richiede il download di due componenti aggiuntivi separati da Cwtch, il Blodeuwedd Model (o un modello compatibile) e il Blodeuwedd Runner. \n\nVedere https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd per ulteriori informazioni su come ottenere questi componenti e configurarli.",
"availabilityStatusAvailable": "Disponibile",
"availabilityStatusAway": "Assente",
"availabilityStatusBusy": "Non disponibile",
"availabilityStatusTooltip": "Imposta il tuo stato di disponibilità",
"profileInfoHint": "Aggiungi qui alcune informazioni personali pubbliche, ad esempio blog, siti web, breve biografia.",
"profileInfoHint2": "È possibile aggiungere fino a 3 campi.",
"profileInfoHint3": "I contatti potranno vedere queste informazioni nelle Impostazioni di conversazione. ",
"retryConnection": "Riprova",
"defaultScalingText": "Testo di dimensioni predefinite (fattore di scala:",
"localeJa": "Japanese \/ 日本語",
"retryConnectionTooltip": "Cwtch retries peers regularly, but you can tell Cwtch to try sooner by pushing this button.",
"retryConnection": "Retry",
"availabilityStatusTooltip": "Set your availability status",
"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.",
"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": "Start or stop 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",
"tooltipPinConversation": "Aggiungi la conversazione in cima alla lista \"Conversazioni\"",
"tooltipUnpinConversation": "Rimuovi la conversazione dalla cima della lista \"Conversazioni\"",
"errorDownloadDirectoryDoesNotExist": "La condivisione file non può essere abilitata perché la destinazione dei download non è stata impostata o è impostata su una cartella che non esiste.",
@ -203,7 +212,6 @@
"profileOnionLabel": "Invia questo indirizzo ai contatti con cui vuoi connetterti",
"importLocalServerLabel": "Importa un server ospitato localmente",
"importLocalServerButton": "Importa %1",
"groupsOnThisServerLabel": "Gruppi di cui sono parte su questo server",
"fieldDescriptionLabel": "Descrizione",
"displayNameTooltip": "Inserisci un nome visualizzato",
"manageKnownServersShort": "Server",
@ -265,7 +273,6 @@
"copyAddress": "Copia indirizzo",
"enterCurrentPasswordForDeleteServer": "Inserisci la password attuale per eliminare questo server",
"deleteServerSuccess": "Server eliminato con successo",
"deleteServerConfirmBtn": "Elimina davvero il server",
"encryptedServerDescription": "Criptare un server con una password lo protegge da altre persone che potrebbero usare questo dispositivo. I server criptati non possono essere decriptati, visualizzati o accessibili finché non viene inserita la password corretta per sbloccarli.",
"fileSavedTo": "Salvato in",
"fileInterrupted": "Interrotto",

View File

@ -1,6 +1,16 @@
{
"@@locale": "ja",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Really Delete Server?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Known Groups on this Cwtch Server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +74,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",
@ -183,7 +192,6 @@
"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",
@ -193,7 +201,6 @@
"fileCheckingStatus": "Checking download status",
"fileInterrupted": "Interrupted",
"fileSavedTo": "Saved to",
"deleteServerConfirmBtn": "Really delete server",
"deleteServerSuccess": "Successfully deleted server",
"enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server",
"copyAddress": "Copy Address",

View File

@ -1,6 +1,138 @@
{
"@@locale": "ko",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Really Delete Server?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Known Groups on this Cwtch Server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"showMessageButton": "메시지 표시",
"addContactConfirm": "연락처 %1 추가",
"addContact": "연락처 추가",
"sendMessage": "메시지 보내기",
"cancel": "취소",
"rejected": "거부!",
"accepted": "수락!",
"localeIt": "이탈리아어 \/ Italiano",
"localeEs": "스페인어 \/ Español",
"yourProfiles": "내 프로필",
"addNewProfileBtn": "새 프로필 추가",
"copiedToClipboardNotification": "클립보드에 복사됨",
"tooltipHidePassword": "비밀번호 숨기기",
"tooltipShowPassword": "비밀번호 표시",
"enterCurrentPasswordForDelete": "이 프로파일을 삭제하려면 현재 비밀번호를 입력하십시오.",
"password": "비밀번호",
"passwordErrorMatch": "비밀번호가 일치하지 않습니다.",
"passwordErrorEmpty": "비밀번호는 비워 둘 수 없습니다.",
"password2Label": "비밀번호를 다시 입력하세요",
"password1Label": "비밀번호",
"currentPasswordLabel": "현제 비밀번호",
"noPasswordWarning": "이 계정에서 비밀번호를 사용하지 않으면 로컬에 저장된 모든 데이터가 암호화되지 않습니다",
"radioNoPassword": "암호화되지 않음(비밀번호 없음)",
"radioUsePassword": "비밀번호",
"newPassword": "새 비밀번호",
"yesLeave": "이 대화에서 나가기 확인",
"leaveConversation": "이 대화에서 나가기",
"shutdownCwtchAction": "Cwtch (크치)를 종료",
"shutdownCwtchTooltip": "Cwtch (크치)를 종료",
"shutdownCwtchDialogTitle": "Cwtch (크치)를 종료하시겠습니까?",
"successfullAddedContact": "성공적으로 추가되었습니다.",
"titleManageServers": "서버 관리",
"cwtchSettingsTitle": "Cwtch (크치) 설정",
"titleManageProfiles": "Cwtch (크치) 프로파일 관리",
"tooltipAddContact": "새 연락처 또는 대화 추가",
"tooltipOpenSettings": "설정 창 열기",
"contactAlreadyExists": "연락처가 이미 있음",
"conversationSettings": "대화 설정",
"titleManageContacts": "대화",
"enableGroups": "그룹 채팅 사용",
"zoomLabel": "인터페이스 확대\/축소(주로 텍스트 및 버튼 크기에 영향을 줌)",
"blockUnknownLabel": "알 수 없는 연락처 차단",
"unlock": "잠금 해제",
"acceptGroupInviteLabel": "초대를 수락하시겠습니까?",
"bulletinsBtn": "게시판",
"update": "업데이트",
"serverNotSynced": "새 메시지 동기화 중(시간이 걸릴 수 있음)...",
"serverConnectivityDisconnected": "서버 연결 끊김",
"search": "검색...",
"postNewBulletinLabel": "새 게시판 게시",
"newBulletinLabel": "새 게시판",
"joinGroup": "그룹 가입",
"invitation": "초대",
"joinGroupTab": "그룹 가입",
"displayNameLabel": "표시 이름",
"puzzleGameBtn": "퍼즐 게임",
"searchList": "목록 검색",
"inviteBtn": "초대",
"inviteToGroupLabel": "그룹에 초대",
"groupNameLabel": "그룹 이름",
"viewServerInfo": "서버 정보",
"titlePlaceholder": "자격",
"addProfileTitle": "새 프로필 추가",
"newProfile": "새 프로필",
"newConnectionPaneTitle": "새로운 연결",
"networkStatusOnline": "온라인",
"smallTextLabel": "작은",
"themeLight": "밝음",
"settingTheme": "밝은 테마 사용",
"themeDark": "어둠",
"largeTextLabel": "큰",
"localeDe": "독일어 \/ Deutsch",
"localePt": "포르투갈어 \/ Português",
"localeFr": "프랑스어 \/ Français",
"localeEn": "영어 \/ English",
"settingLanguage": "언어",
"yourServers": "서버",
"deleteBtn": "삭제",
"saveBtn": "저장",
"serverSynced": "동기화",
"serverConnectivityConnected": "서버 연결됨",
"serverInfo": "서버 정보",
"invitationLabel": "초대",
"serverLabel": "서버",
"blocked": "차단됨",
"createGroup": "그룹 만들기",
"addPeer": "연락처 추가",
"groupAddr": "주소",
"server": "서버",
"peerName": "이름",
"peerAddress": "주소",
"builddate": "Built on: %2",
"deleteProfileConfirmBtn": "프로필 삭제 확인",
"deleteConfirmLabel": "DELETE를 입력하여 확인",
"deleteProfileBtn": "프로필 삭제",
"saveProfileBtn": "프로필 저장",
"createProfileBtn": "프로필 만들기",
"yourDisplayName": "표시 이름",
"profileOnionLabel": "연결하려는 연락처에 이 주소를 보냅니다.",
"editProfile": "프로필 편집",
"defaultProfileName": "앨리스",
"profileName": "표시 이름",
"editProfileTitle": "프로필 편집",
"dontSavePeerHistory": "기록 삭제",
"savePeerHistory": "기록 저장",
"blockBtn": "연락처 차단",
"addressLabel": "주소",
"listsBtn": "목록",
"chatBtn": "채팅",
"acceptGroupBtn": "동의",
"rejectGroupBtn": "거부",
"newGroupBtn": "새 그룹 만들기",
"copyBtn": "복사",
"peerOfflineMessage": "연락처가 오프라인 상태이므로 지금은 메시지를 전달할 수 없습니다.",
"peerBlockedMessage": "연락처가 차단되었습니다.",
"pendingLabel": "보류 중",
"acknowledgedLabel": "인정됨",
"couldNotSendMsgError": "이 메시지를 보낼 수 없습니다.",
"dmTooltip": "DM으로 클릭하세요.",
"membershipDescription": "다음은 그룹에 메시지를 보낸 사용자 목록입니다. 이 목록에는 그룹에 액세스할 수 있는 모든 사용자가 표시되지 않을 수 있습니다.",
"addListItemBtn": "아이템 추가",
"peerNotOnline": "연락처가 오프라인 상태입니다. 지금은 응용 프로그램을 사용할 수 없습니다.",
"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 \/ 日本語",
@ -10,7 +142,6 @@
"addPeerTab": "연락처 추가",
"createGroupBtn": "창조",
"defaultGroupName": "굉장한 그룹",
"serverLabel": "Server",
"createGroupTitle": "그룹 만들기",
"availabilityStatusAway": "자리 비움",
"availabilityStatusBusy": "바쁘다",
@ -59,8 +190,6 @@
"notificationContentContactInfo": "대화 정보",
"newMessageNotificationConversationInfo": "%1의 새로운 메시지",
"localePl": "폴란드어 \/ Polski",
"addContact": "Add contact",
"addContactConfirm": "Add contact %1",
"downloadFileButton": "다운로드",
"labelFilesize": "크기",
"messageFileSent": "파일을 보냈습니다.",
@ -124,7 +253,6 @@
"profileAutostartDescription": "시작 시 프로필이 자동으로 시작되는지 여부를 제어합니다.",
"profileAutostartLabel": "자동 시작",
"profileEnabled": "허락",
"profileEnabledDescription": "프로필 시각 또는 중지",
"replyingTo": "%1에 회신",
"tooltipCode": "Code \/ Monospace",
"tooltipStrikethrough": "Strikethrough",
@ -169,7 +297,6 @@
"serverTotalMessagesLabel": "Total Messages",
"displayNameTooltip": "Please enter a display name",
"fieldDescriptionLabel": "Description",
"groupsOnThisServerLabel": "Groups I am in hosted on this server",
"importLocalServerSelectText": "Select Local Server",
"importLocalServerLabel": "Import a locally hosted server",
"copyServerKeys": "Copy keys",
@ -179,7 +306,6 @@
"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",
"enterCurrentPasswordForDeleteServer": "Please enter current password to delete this server",
"settingServersDescription": "The hosting servers experiment enables hosting and managing Cwtch servers",
"enterServerPassword": "Enter password to unlock server",
@ -204,7 +330,6 @@
"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.",
@ -222,13 +347,8 @@
"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",
@ -239,150 +359,37 @@
"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",
"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",
"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"
"pasteAddressToAddContact": "Paste a cwtch address, invitation or key bundle here to add a new conversation"
}

View File

@ -1,6 +1,16 @@
{
"@@locale": "lb",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Really Delete Server?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Known Groups on this Cwtch Server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -247,7 +256,6 @@
"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",
@ -259,7 +267,6 @@
"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",

View File

@ -1,31 +1,40 @@
{
"@@locale": "nl",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Server echt verwijderen",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Groepen waarin ik zit gehost op deze server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"profileEnabledDescription": "Activeer of deactiveer het profiel.",
"defaultScalingText": "Lettertype schalen",
"blodeuweddSummarize": "Gesprek samenvatten",
"blodeuweddTranslate": "Bericht vertalen",
"blodeuweddProcessing": "Blodeuwedd is aan het verwerken...",
"blodeuweddPath": "De map waar de Blodeuwedd zich bevindt op je computer.",
"blodeuweddNotSupported": "Deze versie van Cwtch is gecompileerd zonder ondersteuning voor de Blodeuwedd Assistant.",
"blodeuweddExperimentEnable": "Blodeuwedd Assistent",
"localeKo": "Koreaans \/ 한국어",
"fontScalingDescription": "Adjust the relative font scaling factor applied to text and widgets.",
"defaultScalingText": "Standaardtekstgrootte (schaalfactor:",
"localeJa": "Japanese \/ 日本語",
"availabilityStatusTooltip": "Stel je beschikbaarheidsstatus in",
"profileInfoHint2": "Je kunt maximaal 3 velden toevoegen.",
"availabilityStatusBusy": "Bezig",
"availabilityStatusAway": "Afwezig",
"availabilityStatusAvailable": "Beschikbaar",
"retryConnection": "Opnieuw",
"localeJa": "Japans \/ 日本語",
"localeSv": "Zweeds \/ Svenska",
"localeSw": "Swahili \/ Kiswahili",
"localeUk": "Oekraïens \/ українською",
"retryConnectionTooltip": "Cwtch retries peers regularly, but you can tell Cwtch to try sooner by pushing this button.",
"retryConnection": "Retry",
"availabilityStatusTooltip": "Set your availability status",
"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.",
"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 \/ 한국어",
"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",
@ -74,7 +83,6 @@
"addContactFirst": "Voeg een contact toe of kies een contact om te beginnen met chatten.",
"experimentClickableLinksDescription": "Het klikbare links experiment maakt het mogelijk op URLs te klikken in berichten",
"enableExperimentClickableLinks": "Klikbare links inschakelen",
"groupsOnThisServerLabel": "Groepen waarin ik zit gehost op deze server",
"displayNameTooltip": "Voer een weergavenaam in",
"tooltipBackToMessageEditing": "Terug naar bericht bewerken",
"editServerTitle": "Server bewerken",
@ -144,7 +152,6 @@
"fileCheckingStatus": "Downloadstatus controleren",
"fileInterrupted": "Onderbroken",
"fileSavedTo": "Opgeslagen in",
"deleteServerConfirmBtn": "Server echt verwijderen",
"deleteServerSuccess": "Server succesvol verwijderd",
"enterCurrentPasswordForDeleteServer": "Voer huidige wachtwoord in om deze server te verwijderen",
"settingServersDescription": "Het servers hosten experiment maakt het mogelijk Cwtch-servers te hosten en te beheren",

View File

@ -1,6 +1,16 @@
{
"@@locale": "no",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Slette tjener",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Grupper jeg er vert for på denne tjeneren",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -157,7 +166,6 @@
"displayNameTooltip": "Oppgi visningsnavn",
"manageKnownServersButton": "Tilpass kjente tjenere",
"fieldDescriptionLabel": "Beskrivelse",
"groupsOnThisServerLabel": "Grupper jeg er vert for på denne tjeneren",
"importLocalServerButton": "Importér %1",
"importLocalServerSelectText": "Velg lokal tjener",
"importLocalServerLabel": "Importér en lokal tjener",
@ -169,7 +177,6 @@
"fileSavedTo": "Lagret til",
"encryptedServerDescription": "Kryptering av en tjener med passord beskytter den mot andre som også bruker dette systemet. Krytperte tjenere må låses opp med det korrekte passordet før de kan dekrypteres, vises eller benyttes.",
"plainServerDescription": "Vi anbefaler at du beskyter Cwetch-tjenere med et passord. Dersom du ikke setter et passord for tjeneren så kan hvem som helst med tilgang til enheten få fult innsyn i informasjon om tjeneren, inklusive kryptonøkler.",
"deleteServerConfirmBtn": "Slette tjener",
"deleteServerSuccess": "Tjener slettet",
"enterCurrentPasswordForDeleteServer": "Oppgi passord for å slette tjener",
"copyAddress": "Kopiér adresse",

View File

@ -1,6 +1,16 @@
{
"@@locale": "pl",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Usuń",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Grupy na tym serwerze, których jesteś członkiem",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -258,7 +267,6 @@
"displayNameTooltip": "Wprowadź nazwę",
"manageKnownServersButton": "Zarządzaj znanymi serwerami",
"fieldDescriptionLabel": "Opis",
"groupsOnThisServerLabel": "Grupy na tym serwerze, których jesteś członkiem",
"importLocalServerButton": "Importuj %1",
"importLocalServerSelectText": "Wybierz lokalny serwer",
"importLocalServerLabel": "Importuj lokalnie hostowany serwer",
@ -266,7 +274,6 @@
"fileInterrupted": "Przerwano",
"encryptedServerDescription": "Zaszyfrowanie serwera hasłem chroni go przed dostępem innych osób korzystających z tego urządzenia.",
"plainServerDescription": "Zalecamy ustawienie hasła dla Cwtch. Jeśli hasło nie zostanie ustawione, każdy z dostępem do tego urządzenia uzyska dostęp do tego serwera, w tym do wrażliwych kluczy kryptograficznych.",
"deleteServerConfirmBtn": "Usuń",
"deleteServerSuccess": "Usunięto serwer",
"settingServersDescription": "Hostowanie serwerów (eksperymentalne) umożliwia tworzenie i zarządzanie serwerami Cwtch",
"settingServers": "Hostowanie serwerów",

View File

@ -1,6 +1,16 @@
{
"@@locale": "pt",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Really Delete Server?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Known Groups on this Cwtch Server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -152,7 +161,6 @@
"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",
@ -164,7 +172,6 @@
"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",

View File

@ -1,6 +1,16 @@
{
"@@locale": "pt_BR",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Realmente excluir servidor",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Grupos nos quais estou hospedado neste servidor",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -144,7 +153,6 @@
"displayNameTooltip": "Por favor, digite um nome de exibição",
"manageKnownServersButton": "Gerenciar Servidores Conhecidos",
"fieldDescriptionLabel": "Descrição",
"groupsOnThisServerLabel": "Grupos nos quais estou hospedado neste servidor",
"importLocalServerButton": "Importar %1",
"importLocalServerSelectText": "Selecione Servidor Local",
"importLocalServerLabel": "Importar um servidor hospedado localmente",
@ -157,7 +165,6 @@
"fileSavedTo": "Salvar em",
"encryptedServerDescription": "Criptografar um servidor com uma senha o protege de outras pessoas que também podem usar este dispositivo. Os servidores criptografados não podem ser descriptografados, exibidos ou acessados até que a senha correta seja inserida para desbloqueá-los.",
"plainServerDescription": "Recomendamos que você proteja seus servidores Cwtch com uma senha. Se você não definir uma senha neste servidor, qualquer pessoa que tenha acesso a este dispositivo poderá acessar informações sobre este servidor, incluindo chaves criptográficas sensíveis.",
"deleteServerConfirmBtn": "Realmente excluir servidor",
"deleteServerSuccess": "Servidor excluído com sucesso",
"enterCurrentPasswordForDeleteServer": "Por favor, digite a senha atual para excluir este servidor",
"copyAddress": "Copiar endereço",

View File

@ -1,6 +1,16 @@
{
"@@locale": "ro",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Sigur doriți sa ștergeți serverul",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Grupurile în care mă aflu care sunt găzduite pe acest server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -310,7 +319,6 @@
"copyAddress": "Copiați adresa",
"enterCurrentPasswordForDeleteServer": "Vă rugăm să introduceți parola actuală pentru a șterge acest server",
"deleteServerSuccess": "Serverul a fost șters cu succes",
"deleteServerConfirmBtn": "Sigur doriți sa ștergeți serverul",
"plainServerDescription": "Vă recomandăm să vă protejați serverele Cwtch cu o parolă. Dacă nu setați o parolă pe acest server, atunci oricine are acces la acest dispozitiva are acces la informații despre acest server, inclusiv la chei criptografice importante.",
"fileSavedTo": "Salvat în",
"fileInterrupted": "Întrerupt",
@ -321,7 +329,6 @@
"importLocalServerLabel": "Importați un server găzduit local",
"importLocalServerSelectText": "Selectați Server local",
"importLocalServerButton": "Importă %1",
"groupsOnThisServerLabel": "Grupurile în care mă aflu care sunt găzduite pe acest server",
"fieldDescriptionLabel": "Descriere",
"manageKnownServersButton": "Gestionați serverele cunoscute",
"displayNameTooltip": "Vă rugăm să introduceți un nume de afișat",

View File

@ -1,6 +1,16 @@
{
"@@locale": "ru",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Вы точно хотите удалить сервер?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Группы, в которых я нахожусь, размещены на этом сервере",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -128,7 +137,6 @@
"themeColorLabel": "Основной цвет темы",
"settingDownloadFolder": "Папка для загрузок",
"importLocalServerLabel": "Использовать локальный сервер",
"deleteServerConfirmBtn": "Вы точно хотите удалить сервер?",
"unlockProfileTip": "Создайте или импортируйте профиль, чтобы начать",
"unlockServerTip": "Создайте или импортируйте сервер, чтобы начать",
"saveServerButton": "Сохранить",
@ -184,7 +192,6 @@
"displayNameTooltip": "Введите отображаемое имя",
"manageKnownServersButton": "Управление серверами",
"fieldDescriptionLabel": "Описание",
"groupsOnThisServerLabel": "Группы, в которых я нахожусь, размещены на этом сервере",
"importLocalServerButton": "Импорт %1",
"importLocalServerSelectText": "Выбрать локальный сервер",
"newMessagesLabel": "Новое сообщение",

View File

@ -1,6 +1,16 @@
{
"@@locale": "sk",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Vážne vymazať server?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Skupiny ktorých som členom a sú hostované na tomto servery",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 \/ 日本語",
@ -146,7 +156,6 @@
"displayNameTooltip": "Prosím zadajte zobrazované meno",
"manageKnownServersButton": "Spravovať Známe Servery",
"fieldDescriptionLabel": "Popis",
"groupsOnThisServerLabel": "Skupiny ktorých som členom a sú hostované na tomto servery",
"importLocalServerButton": "Importovať %1",
"importLocalServerSelectText": "Zvoliť Lokálny Server",
"importLocalServerLabel": "Importovať lokálne hostovaný server",
@ -159,7 +168,6 @@
"fileSavedTo": "Uložené do",
"encryptedServerDescription": "Šifrovanie serveru heslom ho chráni pred ostatnými ľuďmi ktorý by mohli toto zariadenie využívať taktiež. Šifrované servery sa nedajú odšifrovať, zobraziť alebo sprístupniť pokiaľ nie je zadané správne heslo.",
"plainServerDescription": "Odporúčame aby ste si ochránili Cwtch servery heslom. Ak si na servery nenastavíte heslo, každý s prístupom k tomuto zariadeniu bude schopný vidieť informácie o danom servry, vrátane citlivých kryptografických klúčov.",
"deleteServerConfirmBtn": "Vážne vymazať server?",
"deleteServerSuccess": "Server bol úspešne vymazaný",
"enterCurrentPasswordForDeleteServer": "Prosím zadajte aktuálne heslo pre zmazanie tohoto serveru",
"copyAddress": "Kopírovať Adresu",
@ -383,6 +391,5 @@
"createGroupBtn": " Vytvoriť",
"defaultGroupName": "Úžasná Skupina",
"createGroupTitle": "Vytvoriť Skupinu",
"profileEnabled": "Povoliť",
"profileEnabledDescription": "Spustiť alebo zastaviť profil"
"profileEnabled": "Povoliť"
}

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

@ -0,0 +1,395 @@
{
"@@locale": "sv",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Bekräfta borttagning av servern",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Grupper jag är med i på den här servern",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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",
"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",
"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."
}

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

@ -0,0 +1,395 @@
{
"@@locale": "sw",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Futa kabisa seva",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Vikundi nilivyopangishwa kwenye seva hii",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"blodeuweddPath": "Saraka ambapo Blodeuwedd iko kwenye kompyuta yako.",
"tooltipSuperscript": "Superscript",
"tooltipStrikethrough": "Mgomo",
"errorDownloadDirectoryDoesNotExist": "Kushiriki faili hakuwezi kuwashwa kwa sababu Folda ya Upakuaji haijawekwa, au imewekwa kwenye folda ambayo haipo.",
"blodeuweddDescription": "Mratibu wa Blodeuwedd huongeza vipengele vipya kwenye Cwtch kama vile muhtasari wa manukuu ya gumzo na tafsiri ya ujumbe kupitia modeli ya lugha inayopangishwa ndani ya nchi.",
"blodeuweddWarning": "Blodeuwedd hutumia modeli ya lugha ya ndani na seti ya modeli ndogo ndogo ili kudhibiti utendakazi wake. Mbinu hizi mara nyingi ni nzuri sana sio bila makosa. \n\n Ingawa tumechukua juhudi za kupunguza hatari, bado kuna uwezekano kwamba matokeo ya Blodeuwedd yatakuwa yasiyo sahihi, ya kuonewa na\/au ya kukera. \n\n Kwa sababu hiyo Blodeuwedd inahitaji kupakua vijenzi viwili vya ziada tofauti na Cwtch, Mfano wa Blodeuwedd (au kielelezo kinachooana) na Blodeuwedd Runner. \n\n Tazama https:\/\/docs.cwtch.im\/docs\/settings\/experiments\/blodeuwedd kwa maelezo zaidi kuhusu kupata vipengele hivi na kuviweka.",
"fontScalingDescription": "Rekebisha kipengele cha kuongeza fonti kinachotumika kwa maandishi na wijeti.",
"createGroupTitle": "Unda Kikundi",
"serverLabel": "Seva",
"defaultGroupName": "Kundi la Kushangaza",
"createGroupBtn": "Unda",
"profileOnionLabel": "Tuma anwani hii kwa watu unaotaka kuungana nao",
"addPeerTab": "Ongeza anwani",
"createGroupTab": "Unda kikundi",
"joinGroupTab": "Jiunge na kikundi",
"peerAddress": "Anwani",
"peerName": "Jina",
"server": "Seva",
"invitation": "Mwaliko",
"groupAddr": "Anwani",
"addPeer": "Ongeza Anwani",
"createGroup": "Unda kikundi",
"joinGroup": "Jiunge na kikundi",
"newBulletinLabel": "Bulletin Mpya",
"postNewBulletinLabel": "Chapisha taarifa mpya",
"titlePlaceholder": "kichwa...",
"pasteAddressToAddContact": "Bandika anwani ya cwtch, mwaliko au kifungu muhimu hapa ili kuongeza mazungumzo mapya",
"blocked": "Imezuiwa",
"search": "Tafuta...",
"invitationLabel": "Mwaliko",
"serverInfo": "Taarifa za Seva",
"serverConnectivityConnected": "Seva Imeunganishwa",
"serverConnectivityDisconnected": "Seva Imetenganishwa",
"serverSynced": "Imesawazishwa",
"serverNotSynced": "Inasawazisha Ujumbe Mpya (Hii inaweza kuchukua muda)...",
"viewServerInfo": "Taarifa za Seva",
"groupNameLabel": "Jina la Kikundi",
"saveBtn": "Hifadhi",
"inviteToGroupLabel": "Alika kwenye kikundi",
"inviteBtn": "Alika",
"deleteBtn": "Futa",
"update": "Sasisha",
"searchList": "Orodha ya Utafutaji",
"peerNotOnline": "Anwani haipo mtandaoni. Maombo hayawezi kutumika kwa sasa.",
"addListItemBtn": "Ongeza Kipengee",
"membershipDescription": "Ifuatayo ni orodha ya watumiaji ambao wametuma ujumbe kwa kikundi. Orodha hii inaweza isiakisi watumiaji wote wanaoweza kufikia kikundi.",
"dmTooltip": "Bofya kwa DM",
"couldNotSendMsgError": "Haikuweza kutuma ujumbe huu",
"acknowledgedLabel": "Imekubaliwa",
"pendingLabel": "Inasubiri",
"peerBlockedMessage": "Anwani imezuiwa",
"peerOfflineMessage": "Anwani haipo mtandaoni, ujumbe hauwezi kutumwa kwa sasa",
"copyBtn": "Nakili",
"newGroupBtn": "Unda kikundi kipya",
"acceptGroupInviteLabel": "Je, ungependa kukubali mwaliko wa",
"acceptGroupBtn": "Kubali",
"rejectGroupBtn": "Kataa",
"chatBtn": "Soga",
"listsBtn": "Orodha",
"bulletinsBtn": "Viaridhishi",
"puzzleGameBtn": "Mchezo wa Mafumbo",
"addressLabel": "Anwani",
"copiedToClipboardNotification": "Imenakiliwa kwenye Ubao wa kunakili",
"displayNameLabel": "Jina la Kuonyesha",
"blockBtn": "Zuia Anwani",
"savePeerHistory": "Hifadhi Historia",
"savePeerHistoryDescription": "Huamua kama kufuta historia yoyote inayohusishwa na mwasiliani.",
"dontSavePeerHistory": "Futa Historia",
"unblockBtn": "Ondoa kizuizi kwa Anwani",
"addProfileTitle": "Ongeza wasifu mpya",
"editProfileTitle": "Hariri Wasifu",
"profileName": "Jina la kuonyesha",
"defaultProfileName": "Alice",
"newProfile": "Wasifu Mpya",
"editProfile": "Hariri Wasifu",
"radioUsePassword": "Nywila",
"radioNoPassword": "Haijasimbwa (Hakuna nywila)",
"noPasswordWarning": "Kutotumia nywila kwenye akaunti hii kunamaanisha kuwa data zote zilizohifadhiwa ndani hazitasimbwa kwa njia fiche",
"yourDisplayName": "Jina Lako la Kuonyesha",
"currentPasswordLabel": "Nywila ya sasa",
"password1Label": "Nywila",
"password2Label": "Ingiza tena Nywila",
"passwordErrorEmpty": "Nywila haiwezi kuwa tupu",
"createProfileBtn": "Unda Wasifu",
"saveProfileBtn": "Hifadhi Wasifu",
"passwordErrorMatch": "Nywila hailingani",
"passwordChangeError": "Hitilafu katika kubadilisha nywila: Nenosiri lililotolewa limekataliwa",
"deleteProfileBtn": "Futa Wasifu",
"deleteConfirmLabel": "Andika FUTA ili kuthibitisha",
"deleteProfileConfirmBtn": "Kweli Futa Wasifu",
"deleteConfirmText": "FUTA",
"addNewProfileBtn": "Ongeza wasifu mpya",
"enterProfilePassword": "weka nywila ili uone wasifu wako",
"password": "Nywila",
"error0ProfilesLoadedForPassword": "0 wasifu iliyopakiwa na nywila hiyo",
"yourProfiles": "Wasifu Wako",
"yourServers": "Seva zako",
"unlock": "Fungua",
"cwtchSettingsTitle": "Mipangilio ya Cwtch",
"versionBuilddate": "Toleo: %1 Imejengwa kwa: %2",
"zoomLabel": "Ukuza wa kiolesura (huathiri zaidi maandishi na ukubwa wa vitufe)",
"blockUnknownLabel": "Zuia Anwani Zisizojulikana",
"settingLanguage": "Lugha",
"localeEn": "Kiingereza \/ Kiingereza",
"localeFr": "Kifaransa \/ Kifaransa",
"localePt": "Kireno \/ Kireno",
"localeDe": "Kijerumani \/ Kijerumani",
"settingInterfaceZoom": "Kiwango cha kukuza",
"largeTextLabel": "Kubwa",
"settingTheme": "Tumia Mandhari Mwanga",
"themeLight": "Mwanga",
"themeDark": "Giza",
"experimentsEnabled": "Washa Majaribio",
"versionTor": "Toleo la %1 lenye tor %2",
"version": "Toleo %1",
"builddate": "Imejengwa kwa: %2",
"defaultScalingText": "Kuongeza herufi",
"smallTextLabel": "Ndogo",
"loadingTor": "Inapakia tor...",
"viewGroupMembershipTooltip": "Tazama Uanachama wa Kikundi",
"networkStatusDisconnected": "Imetenganishwa na mtandao, angalia muunganisho wako",
"networkStatusAttemptingTor": "Inajaribu kuunganisha kwenye mtandao wa Tor",
"networkStatusConnecting": "Inaunganisha kwenye mtandao na anwani...",
"networkStatusOnline": "Mtandaoni",
"newConnectionPaneTitle": "Muunganisho Mpya",
"addListItem": "Ongeza Kipengee Kipya cha Orodha",
"addNewItem": "Ongeza kipengee kipya kwenye orodha",
"todoPlaceholder": "Kufanya...",
"localeEs": "Kihispania \/ Kihispania",
"localeIt": "Kiitaliano \/ Kiitaliano",
"enableGroups": "Washa Gumzo la Kikundi",
"enterCurrentPasswordForDelete": "Tafadhali weka nywila ya sasa ili kufuta wasifu huu.",
"conversationSettings": "Mipangilio ya Mazungumzo",
"invalidImportString": "Mfuatano batili wa kuleta",
"contactAlreadyExists": "Anwani Tayari Ipo",
"tooltipOpenSettings": "Fungua kidirisha cha mipangilio",
"tooltipAddContact": "Ongeza anwani au mazungumzo mapya",
"titleManageContacts": "Mazungumzo",
"tooltipUnlockProfiles": "Fungua wasifu uliosimbwa kwa njia fiche kwa kuweka nenosiri lao.",
"titleManageProfiles": "Dhibiti Wasifu wa Cwtch",
"descriptionExperiments": "Majaribio ya Cwtch ni ya hiari, vipengele vya kujijumuisha vinavyoongeza utendakazi wa ziada kwenye Cwtch ambavyo vinaweza kuwa na masuala ya faragha tofauti na gumzo la kawaida la metadata 1:1 kwa mfano gumzo la kikundi, muunganisho wa roboti n.k.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionBlockUnknownConnections": "Ikiwashwa, chaguo hili litafunga kiotomatiki miunganisho kutoka kwa watumiaji wa Cwtch ambao hawajaongezwa kwenye orodha yako ya anwani.",
"successfullAddedContact": "Imefanikiwa kuongezwa",
"titleManageServers": "Dhibiti Seva",
"inviteToGroup": "Umealikwa kujiunga na kikundi:",
"leaveConversation": "Acha Mazungumzo Haya",
"reallyLeaveThisGroupPrompt": "Je, una uhakika unataka kuondoka kwenye mazungumzo haya? Ujumbe na sifa zote zitafutwa.",
"yesLeave": "Ndiyo, Acha Mazungumzo Haya",
"newPassword": "Nywila Mpya",
"chatHistoryDefault": "Mazungumzo haya yatafutwa Cwtch itakapofungwa! Historia ya ujumbe inaweza kuwashwa kwa kila mazungumzo kupitia menyu ya Mipangilio iliyo upande wa juu kulia.",
"accepted": "Imekubaliwa!",
"rejected": "Imekataliwa!",
"contactSuggestion": "Hili ni pendekezo la mawasiliano kwa:",
"sendAnInvitation": "Ulituma mwaliko kwa:",
"torVersion": "Toleo la Tor",
"torStatus": "Hali ya Tor",
"resetTor": "Weka upya",
"cancel": "Ghairi",
"sendMessage": "Tuma Ujumbe",
"sendInvite": "Tuma mwaliko wa anwani au kikundi",
"deleteProfileSuccess": "Imefaulu kufuta wasifu",
"addServerFirst": "Unahitaji kuongeza seva kabla ya kuunda kikundi",
"nickChangeSuccess": "Jina la utani la wasifu limebadilishwa",
"createProfileToBegin": "Tafadhali unda au fungua wasifu wa kuanza",
"addContactFirst": "Ongeza au uchague anwani ili kuanza kupiga gumzo.",
"torNetworkStatus": "Hali ya mtandao wa Tor",
"debugLog": "Washa kumbukumbu ya utatuzi wa kiweko",
"profileDeleteSuccess": "Imefaulu kufuta wasifu",
"malformedMessage": "Ujumbe wenye hitilafu",
"shutdownCwtchTooltip": "Zima Cwtch",
"shutdownCwtchDialogTitle": "Zima Cwtch?",
"shutdownCwtchDialog": "Je, una uhakika unataka kuzima Cwtch? Hii itafunga miunganisho yote, na kuacha programu.",
"shutdownCwtchAction": "Zima Cwtch",
"groupInviteSettingsWarning": "Umealikwa kujiunga na kikundi! Tafadhali washa Jaribio la Gumzo la Kikundi katika Mipangilio ili kutazama Mwaliko huu.",
"tooltipShowPassword": "Onyesha Nywila",
"tooltipHidePassword": "Ficha Nywila",
"notificationNewMessageFromPeer": "Ujumbe mpya kutoka kwa anwani unayowasiliana nayo!",
"notificationNewMessageFromGroup": "Ujumbe mpya katika kikundi!",
"tooltipAcceptContactRequest": "Kubali ombi hili la mawasiliano.",
"tooltipRejectContactRequest": "Kataa ombi hili la mawasiliano",
"tooltipReplyToThisMessage": "Jibu ujumbe huu",
"tooltipRemoveThisQuotedMessage": "Ondoa ujumbe ulionukuliwa.",
"localePl": "Kipolishi \/ Polski",
"settingUIColumnPortrait": "Safu wima za UI katika Hali ya Wima",
"settingUIColumnLandscape": "Safu wima za UI katika Hali ya Mandhari",
"settingUIColumnSingle": "Moja",
"settingUIColumnDouble12Ratio": "Mara mbili (1:2)",
"settingUIColumnDouble14Ratio": "Mara mbili (1:4)",
"settingUIColumnOptionSame": "Sawa na mpangilio wa hali ya wima",
"contactGoto": "Nenda kwenye mazungumzo na %1",
"addContact": "Ongeza anwani",
"addContactConfirm": "Ongeza anwani %1",
"encryptedProfileDescription": "Kusimba wasifu kwa nenosiri huilinda dhidi ya watu wengine ambao wanaweza pia kutumia kifaa hiki. Wasifu uliosimbwa kwa njia fiche hauwezi kusimbwa, kuonyeshwa au kufikiwa hadi nenosiri sahihi liingizwe ili kuzifungua.",
"plainProfileDescription": "Tunapendekeza kwamba ulinde wasifu wako wa Cwtch kwa nenosiri. Ikiwa hutaweka nenosiri kwenye wasifu huu basi mtu yeyote anayeweza kufikia kifaa hiki anaweza kufikia maelezo kuhusu wasifu huu, ikiwa ni pamoja na anwani, ujumbe na funguo nyeti za kriptografia.",
"placeholderEnterMessage": "Andika ujumbe...",
"blockedMessageMessage": "Ujumbe huu unatoka kwa wasifu uliouzuia.",
"showMessageButton": "Onyesha Ujumbe",
"blockUnknownConnectionsEnabledDescription": "Miunganisho kutoka kwa anwani zisizojulikana umezuiwa. Unaweza kubadilisha hii katika Mipangilio",
"archiveConversation": "Hifadhi Mazungumzo haya kwenye kumbukumbu",
"streamerModeLabel": "Hali ya Kipeperushi\/Uwasilishaji",
"descriptionStreamerMode": "Ikiwashwa, chaguo hili hufanya programu ionekane kuwa ya faragha zaidi kwa ajili ya kutiririsha au kuwasilisha, kwa mfano, kuficha wasifu na anwani za mawasiliano.",
"retrievingManifestMessage": "Inarejesha maelezo ya faili...",
"openFolderButton": "Fungua Folda",
"downloadFileButton": "Pakua",
"labelFilename": "Jina la faili",
"labelFilesize": "Ukubwa",
"messageEnableFileSharing": "wezesha jaribio la kushiriki faili ili kuona ujumbe huu.",
"messageFileSent": "Umetuma faili",
"messageFileOffered": "Anwani inajitolea kukutumia faili",
"tooltipSendFile": "Tuma Faili",
"settingFileSharing": "Kushiriki faili",
"descriptionFileSharing": "Jaribio la kushiriki faili hukuruhusu kutuma na kupokea faili kutoka kwa anwani na vikundi vya Cwtch. Kumbuka kwamba kushiriki faili na kikundi kutasababisha washiriki wa kikundi hicho kuungana nawe moja kwa moja kupitia Cwtch ili kuipakua.",
"titleManageProfilesShort": "Wasifu",
"addServerTitle": "Ongeza Seva",
"editServerTitle": "Hariri Seva",
"serverAddress": "Anuani ya server",
"serverDescriptionLabel": "Maelezo ya Seva",
"serverDescriptionDescription": "Maelezo yako ya seva kwa matumizi ya usimamizi wa kibinafsi pekee, hayatashirikiwa kamwe",
"serverEnabled": "Seva Imewezeshwa",
"serverEnabledDescription": "Anzisha au usimamishe seva",
"serverAutostartLabel": "Anzisha kiotomatiki",
"serverAutostartDescription": "Hudhibiti ikiwa programu itazindua seva kiotomatiki inapoanza",
"saveServerButton": "Hifadhi Seva",
"serversManagerTitleLong": "Seva Unazopangisha",
"serversManagerTitleShort": "Seva",
"addServerTooltip": "Ongeza seva mpya",
"unlockServerTip": "Tafadhali unda au kufungua seva kuanza!",
"unlockProfileTip": "Tafadhali unda au fungua wasifu ili kuanza!",
"enterServerPassword": "Weka nenosiri ili kufungua seva",
"settingServers": "Seva za Kukaribisha",
"settingServersDescription": "Jaribio la seva za kupangisha huwezesha kupangisha na kudhibiti seva za Cwtch",
"copyAddress": "Nakili Anwani",
"enterCurrentPasswordForDeleteServer": "Tafadhali ingiza nywila ya sasa ili kufuta seva hii",
"deleteServerSuccess": "Seva iliyo fanikiwa futwa",
"plainServerDescription": "Tunapendekeza kwamba ulinde seva zako za Cwtch kwa nenosiri. Ikiwa hutaweka nenosiri kwenye seva hii basi mtu yeyote ambaye ana idhini ya kufikia kifaa hiki anaweza kufikia maelezo kuhusu seva hii, ikiwa ni pamoja na funguo nyeti za kriptografia.",
"encryptedServerDescription": "Kusimba seva kwa nenosiri huilinda dhidi ya watu wengine ambao wanaweza pia kutumia kifaa hiki. Seva zilizosimbwa kwa njia fiche haziwezi kusimbwa, kuonyeshwa au kufikiwa hadi nenosiri sahihi liingizwe ili kuzifungua.",
"fileSavedTo": "Imehifadhiwa kwa",
"fileInterrupted": "Imekatizwa",
"fileCheckingStatus": "Inakagua hali ya upakuaji",
"verfiyResumeButton": "Thibitisha\/endelea tena",
"copyServerKeys": "Nakili funguo",
"localeRU": "Kirusi \/ Русский",
"newMessagesLabel": "Ujumbe Mpya",
"importLocalServerLabel": "Ingiza seva iliyopangishwa ndani ya nchi",
"importLocalServerSelectText": "Chagua Seva ya Karibu",
"importLocalServerButton": "Ingiza %1",
"fieldDescriptionLabel": "Maelezo",
"manageKnownServersButton": "Dhibiti Seva Zinazojulikana",
"displayNameTooltip": "Tafadhali ingiza jina la kuonyesha",
"manageKnownServersLong": "Dhibiti Seva Zinazojulikana",
"manageKnownServersShort": "Seva",
"serverMetricsLabel": "Vipimo vya Seva",
"serverTotalMessagesLabel": "Jumla ya Ujumbe",
"serverConnectionsLabel": "Muunganisho",
"enableExperimentClickableLinks": "Wezesha Viungo Vinavyobofka",
"experimentClickableLinksDescription": "Jaribio la viungo vinavyoweza kubofya hukuruhusu kubofya URL zilizoshirikiwa katika ujumbe",
"settingImagePreviews": "Muhtasari wa Picha na Picha za Wasifu",
"settingImagePreviewsDescription": "Picha na Picha za Profaili zitapakuliwa na kuoneshwa kiotomatiki. Tunapendekeza kwamba usiwezeshe Jaribio hili ikiwa unatumia Cwtch na anwani zisizoaminika.",
"settingDownloadFolder": "Pakua Folda",
"themeNameCwtch": "Cwtch",
"themeNameWitch": "Mchawi",
"themeNameVampire": "Vampire",
"themeNameGhost": "Roho",
"themeNamePumpkin": "Maboga",
"themeNameMermaid": "Mermaid",
"themeNameMidnight": "Usiku wa manane",
"themeNameNeon1": "Neon1",
"themeNameNeon2": "Neon2",
"themeColorLabel": "Mandhari ya Rangi",
"loadingCwtch": "Inapakia Cwtch...",
"storageMigrationModalMessage": "Inahamisha wasifu hadi umbizo jipya la hifadhi. Hii inaweza kuchukua dakika chache...",
"msgFileTooBig": "Ukubwa wa faili hauwezi kuzidi GB 10",
"msgConfirmSend": "Je, una uhakika unataka kutuma",
"btnSendFile": "Tuma Faili",
"msgAddToAccept": "Ongeza akaunti hii kwa watu unaowasiliana nao ili ukubali faili hii.",
"torSettingsEnabledAdvanced": "Wezesha Usanidi wa juu wa Tor",
"torSettingsEnabledAdvancedDescription": "Tumia huduma iliyopo ya Tor kwenye mfumo wako, au ubadilishe vigezo vya Huduma ya Cwtch Tor",
"torSettingsCustomSocksPort": "Bandari Maalum ya SOKS",
"torSettingsCustomSocksPortDescription": "Tumia mlango maalum kwa miunganisho ya data kwa seva mbadala ya Tor",
"torSettingsCustomControlPort": "Mlango wa Kudhibiti Maalum",
"torSettingsCustomControlPortDescription": "Tumia mlango maalum kudhibiti miunganisho kwa seva mbadala ya Tor",
"torSettingsUseCustomTorServiceConfiguration": "Tumia Usanidi Maalum wa Huduma ya Tor (torrc)",
"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",
"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",
"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",
"localeUk": "Kiukreni \/ українською",
"localeSv": "Kiswidi \/ Svenska",
"localeJa": "Kijapani \/ 日本語",
"localeSw": "Kiswahili \/ Kiswahili",
"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."
}

View File

@ -1,6 +1,16 @@
{
"@@locale": "tr",
"@@last_modified": "2023-05-15T21:16:36+02:00",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Sunucuyu gerçekten sil",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Bu sunucuda içinde bulunduğum gruplar",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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 +33,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",
@ -295,7 +304,6 @@
"copyAddress": "Adresi Kopyala",
"enterCurrentPasswordForDeleteServer": "Lütfen sunucuyu silmek için şifreyi girin",
"deleteServerSuccess": "Sunucu başarıyla silindi",
"deleteServerConfirmBtn": "Sunucuyu gerçekten sil",
"plainServerDescription": "Cwtch sunucularınızı bir parola ile korumanızı öneririz. Bir parola belirlemezseniz, bu cihaza erişimi olan herkes kriptografik anahtarlar da dahil olmak üzere sunucunun hassas bilgilerine erişebilir.",
"encryptedServerDescription": "Bir sunucuyu parola ile şifrelemek, sunucuyu bu aygıtı kullanabilecek diğer kişilerden korur. Doğru şifre girilene kadar şifrelenmiş sunucular görüntülenemez veya erişilemez.",
"fileSavedTo": "Şuraya kaydedildi",
@ -307,7 +315,6 @@
"importLocalServerLabel": "Yerel bir sunucuyu içeri aktar",
"importLocalServerSelectText": "Yerel Sunucu Seç",
"importLocalServerButton": "%1'i içeri aktar",
"groupsOnThisServerLabel": "Bu sunucuda içinde bulunduğum gruplar",
"fieldDescriptionLabel": "Açıklama",
"manageKnownServersButton": "Bilinen Sunucuları Yönet",
"displayNameTooltip": "Lütfen bir ad girin",

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

@ -0,0 +1,395 @@
{
"@@locale": "uk",
"@@last_modified": "2023-08-21T19:40:19+02:00",
"deleteServerConfirmBtn": "Really Delete Server?",
"cannotDeleteServerIfActiveGroups": "There are active groups associated with this Cwtch Server. Please delete them prior to deleting this Cwtch Server entry.",
"groupsOnThisServerLabel": "Known Groups on this Cwtch Server",
"serverinfoNoGroupInfo": "There are no groups associated with this Cwtch Server.",
"preserveHistorySettingDescription": "By default, Cwtch will purge conversation history when Cwtch is shutdown. If this setting is enabled, Cwtch will preserve the conversation history of peer conversations.",
"defaultPreserveHistorySetting": "Preserve Conversation History",
"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",
"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.",
"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

@ -44,6 +44,8 @@ Future<void> main() async {
print("Cwtch version: ${EnvironmentConfig.BUILD_VER} built on: ${EnvironmentConfig.BUILD_DATE}");
LicenseRegistry.addLicense(() => licenses());
WidgetsFlutterBinding.ensureInitialized();
// window_manager requires (await recommended but probably not required if not using immediately)
windowManager.ensureInitialized();
print("runApp()");
return runApp(Flwtch());
}
@ -76,6 +78,7 @@ class FlwtchState extends State<Flwtch> with WindowListener {
@override
initState() {
print("initState() started, setting up handlers");
globalSettings = Settings(Locale("en", ''), CwtchDark());
globalErrorHandler = ErrorHandler();
globalTorStatus = TorStatus();
@ -85,7 +88,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);
@ -104,9 +106,14 @@ class FlwtchState extends State<Flwtch> with WindowListener {
new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, newDesktopNotificationsManager(_notificationSelectConvo), globalAppState, globalServersList, this);
cwtch = CwtchFfi(cwtchNotifier);
}
startConnectivityListener();
print("initState: invoking cwtch.Start()");
cwtch.Start();
print("initState: starting connectivityListener");
if (EnvironmentConfig.TEST_MODE == false) {
startConnectivityListener();
} else {
connectivityStream = null;
}
print("initState: done!");
super.initState();
}
@ -288,7 +295,8 @@ class FlwtchState extends State<Flwtch> with WindowListener {
);
// On Gnome follows up a clicked notification with a "Cwtch is ready" notification that takes you to the app. AFAICT just because Gnome is bad
// https://askubuntu.com/questions/1286206/how-to-skip-the-is-ready-notification-and-directly-open-apps-in-ubuntu-20-4
windowManager.focus();
await windowManager.show();
await windowManager.focus();
}
// using windowManager flutter plugin until proper lifecycle management lands in desktop
@ -303,6 +311,8 @@ class FlwtchState extends State<Flwtch> with WindowListener {
globalAppState.focus = false;
}
void onWindowClose() {}
@override
void dispose() {
globalAppState.SetModalState(ModalState.shutdown);

View File

@ -1,5 +1,6 @@
import 'dart:async';
import 'package:cwtch/config.dart';
import 'package:flutter/widgets.dart';
enum ModalState { none, storageMigration, shutdown }
@ -11,6 +12,7 @@ class AppState extends ChangeNotifier {
String appError = "";
String? _selectedProfile;
int? _selectedConversation;
int? _selectedSearchMessage;
int _initialScrollIndex = 0;
bool _unreadMessagesBelow = false;
bool _disableFilePicker = false;
@ -30,11 +32,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();
}
@ -51,6 +55,12 @@ class AppState extends ChangeNotifier {
notifyListeners();
}
int? get selectedSearchMessage => _selectedSearchMessage;
set selectedSearchMessage(int? newVal) {
this._selectedSearchMessage = newVal;
notifyListeners();
}
bool get disableFilePicker => _disableFilePicker;
set disableFilePicker(bool newVal) {
this._disableFilePicker = newVal;

View File

@ -68,6 +68,7 @@ class ContactInfoState extends ChangeNotifier {
MessageDraft _messageDraft = MessageDraft.empty();
var _hoveredIndex = -1;
var _pendingScroll = -1;
ContactInfoState(
this.profileOnion,
@ -264,6 +265,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) {
@ -429,6 +439,12 @@ class ContactInfoState extends ChangeNotifier {
notifyListeners();
}
int get pendingScroll => _pendingScroll;
set pendingScroll(int newVal) {
this._pendingScroll = newVal;
notifyListeners();
}
String statusString(BuildContext context) {
switch (this.availabilityStatus) {
case ProfileStatusMenu.available:

View File

@ -160,6 +160,7 @@ class ById implements CacheHandler {
if (messageInfo == null) {
return Future.value(null);
}
EnvironmentConfig.debugLog("fetching $profileOnion $conversationIdentifier $id ${messageInfo.wrapper}");
cache.addUnindexed(messageInfo);
return Future.value(messageInfo);
}

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

@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:cwtch/config.dart';
import 'package:cwtch/models/remoteserver.dart';
import 'package:cwtch/models/search.dart';
import 'package:flutter/widgets.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
@ -10,6 +11,7 @@ import '../views/contactsview.dart';
import 'contact.dart';
import 'contactlist.dart';
import 'filedownloadprogress.dart';
import 'message.dart';
import 'messagecache.dart';
import 'profileservers.dart';
@ -65,9 +67,11 @@ class ProfileInfoState extends ChangeNotifier {
List<dynamic> contacts = jsonDecode(contactsJson);
this._contacts.addAll(contacts.map((contact) {
this._unreadMessages += contact["numUnread"] as int;
return ContactInfoState(this.onion, contact["identifier"], contact["onion"],
nickname: contact["name"],
localNickname: contact["localname"],
localNickname: contact["attributes"]?["local.profile.name"] ?? "", // contact may not have a local name
status: contact["status"],
imagePath: contact["picture"],
defaultImagePath: contact["isGroup"] ? contact["picture"] : contact["defaultPicture"],
@ -91,6 +95,23 @@ class ProfileInfoState extends ChangeNotifier {
}
}
// Code for managing the state of the profile-wide search feature...
String activeSearchID = "";
List<SearchResult> activeSearchResults = List.empty(growable: true);
void newSearch(String activeSearchID) {
this.activeSearchID = activeSearchID;
this.activeSearchResults.clear();
notifyListeners();
}
void handleSearchResult(String searchID, int conversationIdentifier, int messageIndex) {
if (searchID == activeSearchID) {
activeSearchResults.add(SearchResult(searchID: searchID, conversationIdentifier: conversationIdentifier, messageIndex: messageIndex));
notifyListeners();
}
}
// Parse out the server list json into our server info state struct...
void replaceServers(String serversJson) {
if (serversJson != "" && serversJson != "null") {

6
lib/models/search.dart Normal file
View File

@ -0,0 +1,6 @@
class SearchResult {
String searchID;
int conversationIdentifier;
int messageIndex;
SearchResult({required this.searchID, required this.conversationIdentifier, required this.messageIndex});
}

View File

@ -31,11 +31,33 @@ class NullNotificationsManager implements NotificationsManager {
class WindowsNotificationManager implements NotificationsManager {
bool active = false;
bool initialized = false;
late Future<void> Function(String, int) notificationSelectConvo;
// TODO This needs testing and redefining...
WindowsNotificationManager() {
WindowsNotificationManager(Future<void> Function(String, int) notificationSelectConvo) {
this.notificationSelectConvo = notificationSelectConvo;
scheduleMicrotask(() async {
initialized = await WinToast.instance().initialize(clsid: 'cwtch', displayName: 'Cwtch', aumId: 'Open Privacy Research Society', iconPath: '');
initialized = await WinToast.instance().initialize(appName: 'cwtch', productName: 'Cwtch', companyName: 'Open Privacy Research Society');
/*
* WinToast 0.3.0 code for when we can compile it
*
var init = await WinToast.instance().initialize(
aumId: 'OpenPrivacyResearchSociety.Cwtch',
displayName: 'Cwtch',
iconPath: '', // TODO NEED ICON
clsid: 'cwtch',
);
WinToast.instance().setActivatedCallback((event) {
if (event.argument != "close") {
try {
Map<String, dynamic> payloadMap = jsonDecode(event.argument);
var payload = NotificationPayload.fromJson(payloadMap);
notificationSelectConvo(payload.profileOnion, payload.convoId);
} catch (e) {
/* it failed, is ok, may have been 'close'? */
}
}
});*/
initialized = true;
});
}
@ -43,15 +65,43 @@ class WindowsNotificationManager implements NotificationsManager {
if (initialized && !globalAppState.focus) {
if (!active) {
active = true;
// WinToast.instance().clear();
//final toast = await WinToast.instance().showToast(toast: Toast(children: ,type: ToastType.text01, title: message));
//toast?.eventStream.listen((event) {
// if (event is ActivatedEvent) {
// WinToast.instance().bringWindowToFront();
// }
WinToast.instance().clear();
final toast = await WinToast.instance().showToast(type: ToastType.text01, title: message);
toast?.eventStream.listen((event) {
if (event is ActivatedEvent) {
WinToast.instance().bringWindowToFront();
}
active = false;
// });
});
}
/*
* WinToast 0.3.0 code for when we can compile it
*
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",
),
]),
]));
active = false;
}*/
}
}
}
@ -113,12 +163,12 @@ class NixNotificationManager implements NotificationsManager {
linuxAssetsPath = "";
}
var linuxIcon = FilePathLinuxIcon(path.join(linuxAssetsPath, 'assets/knott.png'));
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);
final InitializationSettings initializationSettings =
InitializationSettings(android: null, iOS: null, macOS: DarwinInitializationSettings(defaultPresentSound: false), linux: initializationSettingsLinux);
flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<MacOSFlutterLocalNotificationsPlugin>()?.requestPermissions(
alert: true,
@ -126,7 +176,9 @@ class NixNotificationManager implements NotificationsManager {
sound: false,
);
await flutterLocalNotificationsPlugin.initialize(initializationSettings, );
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
);
});
}
@ -173,7 +225,7 @@ NotificationsManager newDesktopNotificationsManager(Future<void> Function(String
}
} else if (Platform.isWindows) {
try {
return WindowsNotificationManager();
return WindowsNotificationManager(notificationSelectConvo);
} catch (e) {
EnvironmentConfig.debugLog("Failed to create Windows desktoasts notification manager");
}

View File

@ -53,6 +53,7 @@ class Settings extends ChangeNotifier {
NotificationPolicy _notificationPolicy = NotificationPolicy.DefaultAll;
NotificationContent _notificationContent = NotificationContent.SimpleEvent;
bool preserveHistoryByDefault = false;
bool blockUnknownConnections = false;
bool streamerMode = false;
String _downloadPath = "";
@ -131,13 +132,18 @@ class Settings extends ChangeNotifier {
switchLocaleByCode(settings["Locale"]);
// Decide whether to enable Experiments
_fontScaling = double.parse(settings["FontScaling"].toString()).clamp(0.5, 2.0);
var fontScale = settings["FontScaling"];
if (fontScale == null) {
fontScale = 1.0;
}
_fontScaling = double.parse(fontScale.toString()).clamp(0.5, 2.0);
blockUnknownConnections = settings["BlockUnknownConnections"] ?? false;
streamerMode = settings["StreamerMode"] ?? false;
// Decide whether to enable Experiments
experimentsEnabled = settings["ExperimentsEnabled"] ?? false;
preserveHistoryByDefault = settings["DefaultSaveHistory"] ?? false;
// Set the internal experiments map. Casting from the Map<dynamic, dynamic> that we get from JSON
experiments = new HashMap<String, bool>.from(settings["Experiments"]);
@ -192,6 +198,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;
@ -203,6 +214,18 @@ class Settings extends ChangeNotifier {
notifyListeners();
}
/// Preserve the History of all Conversations By Default (can be overridden for specific conversations)
setPreserveHistoryDefault() {
preserveHistoryByDefault = true;
notifyListeners();
}
/// Delete the History of all Conversations By Default (can be overridden for specific conversations)
setDeleteHistoryDefault() {
preserveHistoryByDefault = false;
notifyListeners();
}
/// Block Unknown Connections will autoblock connections if they authenticate with public key not in our contacts list.
/// This is one of the best tools we have to combat abuse, while it isn't ideal it does allow a user to curate their contacts
/// list without being bothered by spurious requests (either permanently, or as a short term measure).
@ -467,7 +490,8 @@ class Settings extends ChangeNotifier {
"UseTorCache": _useTorCache,
"TorCacheDir": _torCacheDir,
"BlodeuweddPath": _blodeuweddPath,
"FontScaling": _fontScaling
"FontScaling": _fontScaling,
"DefaultSaveHistory": preserveHistoryByDefault
};
}
}

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.w400, fontSize: 13);
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

@ -121,7 +121,7 @@ class UrlLinkifier extends Linkifier {
Formatter(_boldRegex, BoldElement.new),
Formatter(_italicRegex, ItalicElement.new),
Formatter(_superRegex, SuperElement.new),
Formatter(_subRegex, SubElement.new),
// Formatter(_subRegex, SubElement.new),
Formatter(_strikeRegex, StrikeElement.new)
];

View File

@ -227,7 +227,8 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
// Enabled
Visibility(
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty && !Provider.of<ProfileInfoState>(context).enabled,
// FIXME don't show the disable switch in test mode...this is a bug relating to scrolling things into view
visible: Provider.of<ProfileInfoState>(context).onion.isNotEmpty && (EnvironmentConfig.TEST_MODE == false),
child: SwitchListTile(
title: Text(AppLocalizations.of(context)!.profileEnabled, style: TextStyle(color: Provider.of<Settings>(context).current().mainTextColor)),
subtitle: Text(AppLocalizations.of(context)!.profileEnabledDescription),

View File

@ -7,6 +7,7 @@ import 'package:cwtch/models/contact.dart';
import 'package:cwtch/models/contactlist.dart';
import 'package:cwtch/models/profile.dart';
import 'package:cwtch/models/profilelist.dart';
import 'package:cwtch/models/search.dart';
import 'package:cwtch/views/profileserversview.dart';
import 'package:flutter/material.dart';
import 'package:cwtch/widgets/contactrow.dart';
@ -15,8 +16,11 @@ import 'package:cwtch/widgets/textfield.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../config.dart';
import '../main.dart';
import '../models/message.dart';
import '../settings.dart';
import '../themes/opaque.dart';
import 'addcontactview.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:qr_flutter/qr_flutter.dart';
@ -35,10 +39,23 @@ class ContactsView extends StatefulWidget {
}
// selectConversation can be called from anywhere to set the active conversation
void selectConversation(BuildContext context, int handle) {
void selectConversation(BuildContext context, int handle, int? messageIndex) {
int? index = null;
if (messageIndex != null) {
// this message is loaded
Provider.of<AppState>(context, listen: false).selectedSearchMessage = messageIndex;
Provider.of<AppState>(context, listen: false).initialScrollIndex = messageIndex!;
EnvironmentConfig.debugLog("Looked up index $messageIndex");
}
if (handle == Provider.of<AppState>(context, listen: false).selectedConversation) {
if (messageIndex != null) {
Provider.of<ContactInfoState>(context, listen: false).messageScrollController.scrollTo(index: messageIndex, duration: Duration(milliseconds: 100));
}
return;
}
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
var unread = Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages;
var previouslySelected = Provider.of<AppState>(context, listen: false).selectedConversation;
@ -48,9 +65,12 @@ void selectConversation(BuildContext context, int handle) {
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.selected();
// triggers update in Double/TripleColumnView
Provider.of<AppState>(context, listen: false).initialScrollIndex = unread;
Provider.of<AppState>(context, listen: false).selectedConversation = handle;
Provider.of<ContactInfoState>(context, listen: false).hoveredIndex = -1;
Provider.of<AppState>(context, listen: false).selectedConversation = handle;
if (index != null) {
Provider.of<AppState>(context, listen: false).initialScrollIndex = unread;
}
// if in singlepane mode, push to the stack
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(context, handle);
@ -164,21 +184,15 @@ class _ContactsViewState extends State<ContactsView> {
itemBuilder: (BuildContext context) => <PopupMenuEntry<ProfileStatusMenu>>[
PopupMenuItem<ProfileStatusMenu>(
value: ProfileStatusMenu.available,
child: Text(
AppLocalizations.of(context)!.availabilityStatusAvailable!,
),
child: Text(AppLocalizations.of(context)!.availabilityStatusAvailable!, style: Provider.of<Settings>(context, listen: false).scaleFonts(defaultTextButtonStyle)),
),
PopupMenuItem<ProfileStatusMenu>(
value: ProfileStatusMenu.away,
child: Text(
AppLocalizations.of(context)!.availabilityStatusAway!,
),
child: Text(AppLocalizations.of(context)!.availabilityStatusAway!, style: Provider.of<Settings>(context, listen: false).scaleFonts(defaultTextButtonStyle)),
),
PopupMenuItem<ProfileStatusMenu>(
value: ProfileStatusMenu.busy,
child: Text(
AppLocalizations.of(context)!.availabilityStatusBusy!,
),
child: Text(AppLocalizations.of(context)!.availabilityStatusBusy!, style: Provider.of<Settings>(context, listen: false).scaleFonts(defaultTextButtonStyle)),
),
],
),
@ -237,11 +251,11 @@ class _ContactsViewState extends State<ContactsView> {
itemBuilder: (BuildContext context) => <PopupMenuEntry<ShareMenu>>[
PopupMenuItem<ShareMenu>(
value: ShareMenu.copyCode,
child: Text(AppLocalizations.of(context)!.copyAddress),
child: Text(AppLocalizations.of(context)!.copyAddress, style: Provider.of<Settings>(context, listen: false).scaleFonts(defaultTextButtonStyle)),
),
PopupMenuItem<ShareMenu>(
value: ShareMenu.qrcode,
child: Text(AppLocalizations.of(context)!.shareMenuQRCode),
child: Text(AppLocalizations.of(context)!.shareMenuQRCode, style: Provider.of<Settings>(context, listen: false).scaleFonts(defaultTextButtonStyle)),
),
],
));
@ -287,6 +301,10 @@ class _ContactsViewState extends State<ContactsView> {
controller: ctrlrFilter,
hintText: AppLocalizations.of(context)!.search,
onChanged: (newVal) {
String profileHandle = Provider.of<ProfileInfoState>(context, listen: false).onion;
Provider.of<FlwtchState>(context, listen: false).cwtch.SearchConversations(profileHandle, newVal).then((value) {
Provider.of<ProfileInfoState>(context, listen: false).newSearch(value);
});
Provider.of<ContactListState>(context, listen: false).filter = newVal;
},
);
@ -294,7 +312,7 @@ class _ContactsViewState extends State<ContactsView> {
}
Widget _buildContactList() {
final tiles = Provider.of<ContactListState>(context).filteredList().map((ContactInfoState contact) {
var tilesSearchResult = Provider.of<ContactListState>(context).filteredList().map((ContactInfoState contact) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: contact),
@ -310,15 +328,28 @@ class _ContactsViewState extends State<ContactsView> {
initialScroll = 0;
}
if (showSearchBar) {
List<SearchResult> searchResults = Provider.of<ProfileInfoState>(context).activeSearchResults;
tilesSearchResult = searchResults.map((SearchResult searchResult) {
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(searchResult.conversationIdentifier)),
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).serverList),
],
builder: (context, child) => ContactRow(messageIndex: searchResult.messageIndex),
);
});
} else {}
var contactList = ScrollablePositionedList.separated(
itemScrollController: Provider.of<ProfileInfoState>(context).contactListScrollController,
itemCount: Provider.of<ContactListState>(context).numFiltered,
itemCount: tilesSearchResult.length,
initialScrollIndex: initialScroll,
shrinkWrap: true,
physics: BouncingScrollPhysics(),
semanticChildCount: Provider.of<ContactListState>(context).numFiltered,
semanticChildCount: tilesSearchResult.length,
itemBuilder: (context, index) {
return tiles.elementAt(index);
return tilesSearchResult.elementAt(index);
},
separatorBuilder: (BuildContext context, int index) {
return Divider(height: 1);
@ -354,6 +385,7 @@ class _ContactsViewState extends State<ContactsView> {
Navigator.of(context).push(
PageRouteBuilder(
settings: RouteSettings(name: "profileremoteservers"),
pageBuilder: (bcontext, a1, a2) {
return MultiProvider(
providers: [ChangeNotifierProvider.value(value: profileInfoState), Provider.value(value: Provider.of<FlwtchState>(context))],

View File

@ -37,7 +37,7 @@ class _FileSharingViewState extends State<FileSharingView> {
future: Provider.of<FlwtchState>(context, listen: false).cwtch.GetSharedFiles(profileHandle, Provider.of<ContactInfoState>(context).identifier),
builder: (context, snapshot) {
if (snapshot.hasData) {
List<dynamic> sharedFiles = jsonDecode(snapshot.data as String);
List<dynamic> sharedFiles = jsonDecode(snapshot.data as String) ?? List<dynamic>.empty();
sharedFiles.sort((a, b) {
return a["DateShared"].toString().compareTo(b["DateShared"].toString());
});

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(
@ -229,8 +230,9 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
EnvironmentConfig.debugLog("Font Scaling: $value");
},
min: 0.5,
divisions: 6,
divisions: 12,
max: 2.0,
label: '${settings.fontScaling * 100}%',
activeColor: settings.current().defaultButtonColor,
thumbColor: settings.current().mainTextColor,
overlayColor: MaterialStateProperty.all(settings.current().mainTextColor),
@ -305,10 +307,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 +327,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),
@ -354,6 +350,24 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
secondary: Icon(CwtchIcons.block_unknown, color: settings.current().mainTextColor),
),
SwitchListTile(
title: Text(AppLocalizations.of(context)!.defaultPreserveHistorySetting),
subtitle: Text(AppLocalizations.of(context)!.preserveHistorySettingDescription),
value: settings.preserveHistoryByDefault,
onChanged: (bool value) {
if (value) {
settings.setPreserveHistoryDefault();
} else {
settings.setDeleteHistoryDefault();
}
// Save Settings...
saveSettings(context);
},
activeTrackColor: settings.theme.defaultButtonColor,
inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
secondary: Icon(CwtchIcons.block_unknown, color: settings.current().mainTextColor),
),
SizedBox(
height: 40,
),
@ -472,6 +486,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
testKey: Key("DownloadFolderPicker"),
label: AppLocalizations.of(context)!.settingDownloadFolder,
initialValue: settings.downloadPath,
textStyle: settings.scaleFonts(defaultDropDownMenuItemTextStyle),
description: AppLocalizations.of(context)!.fileSharingSettingsDownloadFolderDescription,
tooltip: AppLocalizations.of(context)!.fileSharingSettingsDownloadFolderTooltip,
onSave: (newVal) {
@ -747,6 +762,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';
@ -130,7 +131,7 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
items: ConversationNotificationPolicy.values.map<DropdownMenuItem<ConversationNotificationPolicy>>((ConversationNotificationPolicy value) {
return DropdownMenuItem<ConversationNotificationPolicy>(
value: value,
child: Text(value.toName(context)),
child: Text(value.toName(context), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
);
}).toList(),
onChanged: (ConversationNotificationPolicy? newVal) {
@ -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

@ -14,6 +14,7 @@ import 'package:cwtch/models/message_draft.dart';
import 'package:cwtch/models/messagecache.dart';
import 'package:cwtch/models/messages/quotedmessage.dart';
import 'package:cwtch/models/profile.dart';
import 'package:cwtch/themes/opaque.dart';
import 'package:cwtch/third_party/linkify/flutter_linkify.dart';
import 'package:cwtch/widgets/malformedbubble.dart';
import 'package:cwtch/widgets/messageloadingbubble.dart';
@ -44,7 +45,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 +68,6 @@ class _MessageViewState extends State<MessageView> {
showDown = false;
}
});
ctrlrCompose.text = Provider.of<ContactInfoState>(context, listen: false).messageDraft.messageText ?? "";
super.initState();
}
@ -88,7 +87,6 @@ class _MessageViewState extends State<MessageView> {
@override
void dispose() {
focusNode.dispose();
ctrlrCompose.dispose();
super.dispose();
}
@ -321,22 +319,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 +344,7 @@ class _MessageViewState extends State<MessageView> {
var bytes1 = utf8.encode(data!.metadata.senderHandle + data.wrapper);
var digest1 = sha256.convert(bytes1);
var contentHash = base64Encode(digest1.bytes);
var quotedMessage = jsonEncode(QuotedMessageStructure(contentHash, ctrlrCompose.value.text));
var quotedMessage = jsonEncode(QuotedMessageStructure(contentHash, messageWithoutNewLine));
ChatMessage cm = new ChatMessage(o: QuotedMessageOverlay, d: quotedMessage);
Provider.of<FlwtchState>(context, listen: false)
.cwtch
@ -359,7 +356,7 @@ class _MessageViewState extends State<MessageView> {
Provider.of<ContactInfoState>(context, listen: false).messageDraft.clearQuotedReference();
});
} else {
ChatMessage cm = new ChatMessage(o: TextMessageOverlay, d: ctrlrCompose.value.text);
ChatMessage cm = new ChatMessage(o: TextMessageOverlay, d: messageWithoutNewLine);
Provider.of<FlwtchState>(context, listen: false)
.cwtch
.SendMessage(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).identifier, jsonEncode(cm))
@ -391,8 +388,7 @@ class _MessageViewState extends State<MessageView> {
// At this point we have decided to send the text to the backend, failure is still possible
// but it will show as an error-ed message, as such the draft can be purged.
Provider.of<ContactInfoState>(context, listen: false).messageDraft = MessageDraft.empty();
ctrlrCompose.clear();
Provider.of<ContactInfoState>(context, listen: false).messageDraft.clearDraft();
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
@ -424,7 +420,7 @@ class _MessageViewState extends State<MessageView> {
var wdgMessage = Padding(
padding: EdgeInsets.all(8),
child: SelectableLinkify(
text: ctrlrCompose.text + '\n',
text: Provider.of<ContactInfoState>(context).messageDraft.messageText + '\n',
options: LinkifyOptions(messageFormatting: true, parseLinks: showClickableLinks, looseUrl: true, defaultToHttps: true),
linkifiers: [UrlLinkifier()],
onOpen: showClickableLinks ? null : null,
@ -468,7 +464,7 @@ class _MessageViewState extends State<MessageView> {
margin: EdgeInsets.all(2),
// 164 minimum height + 16px for every line of text so the entire message is displayed when previewed.
height: 164 + ((ctrlrCompose.text.split("\n").length - 1) * 16),
height: 164 + ((Provider.of<ContactInfoState>(context).messageDraft.messageText.split("\n").length - 1) * 16),
child: Column(
children: [
Row(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [preview]),
@ -483,11 +479,11 @@ class _MessageViewState extends State<MessageView> {
}
Widget _buildComposeBox(BuildContext context) {
bool isOffline = Provider.of<ContactInfoState>(context).isOnline() == false;
bool cannotSend = Provider.of<ContactInfoState>(context).canSend() == false;
bool isGroup = Provider.of<ContactInfoState>(context).isGroup;
var showToolbar = Provider.of<Settings>(context).isExperimentEnabled(FormattingExperiment);
var charLength = ctrlrCompose.value.text.characters.length;
var expectedLength = ctrlrCompose.value.text.length;
var charLength = Provider.of<ContactInfoState>(context).messageDraft.messageText.characters.length;
var expectedLength = Provider.of<ContactInfoState>(context).messageDraft.messageText.length;
var numberOfBytesMoreThanChar = (expectedLength - charLength);
var bold = IconButton(
@ -495,12 +491,14 @@ class _MessageViewState extends State<MessageView> {
tooltip: AppLocalizations.of(context)!.tooltipBoldText,
onPressed: () {
setState(() {
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
var selection = ctrlrCompose.selection;
var start = ctrlrCompose.selection.start;
var end = ctrlrCompose.selection.end;
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "**" + selected + "**");
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 2, extentOffset: selection.start + 2);
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
});
});
@ -509,12 +507,14 @@ class _MessageViewState extends State<MessageView> {
tooltip: AppLocalizations.of(context)!.tooltipItalicize,
onPressed: () {
setState(() {
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
var selection = ctrlrCompose.selection;
var start = ctrlrCompose.selection.start;
var end = ctrlrCompose.selection.end;
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "*" + selected + "*");
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
});
});
@ -523,12 +523,14 @@ class _MessageViewState extends State<MessageView> {
tooltip: AppLocalizations.of(context)!.tooltipCode,
onPressed: () {
setState(() {
var ctrlrCompose = Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose;
var selected = ctrlrCompose.selection.textInside(ctrlrCompose.text);
var selection = ctrlrCompose.selection;
var start = ctrlrCompose.selection.start;
var end = ctrlrCompose.selection.end;
ctrlrCompose.text = ctrlrCompose.text.replaceRange(start, end, "`" + selected + "`");
ctrlrCompose.selection = selection.copyWith(baseOffset: selection.start + 1, extentOffset: selection.start + 1);
Provider.of<ContactInfoState>(context, listen: false).messageDraft.ctrlCompose = ctrlrCompose;
});
});
@ -537,40 +539,46 @@ 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;
});
});
var subscript = IconButton(
icon: Icon(Icons.subscript),
tooltip: AppLocalizations.of(context)!.tooltipSubscript,
onPressed: () {
setState(() {
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);
});
});
// var subscript = IconButton(
// icon: Icon(Icons.subscript),
// 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;
// });
// });
var strikethrough = IconButton(
icon: Icon(Icons.format_strikethrough),
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;
});
});
@ -589,7 +597,7 @@ class _MessageViewState extends State<MessageView> {
var formattingToolbar = Container(
decoration: BoxDecoration(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor),
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [bold, italic, code, superscript, subscript, strikethrough, vline, preview]));
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [bold, italic, code, superscript, strikethrough, vline, preview]));
var textField = Container(
decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of<Settings>(context).theme.defaultButtonActiveColor))),
@ -600,7 +608,7 @@ class _MessageViewState extends State<MessageView> {
padding: EdgeInsets.all(8),
child: TextFormField(
key: Key('txtCompose'),
controller: ctrlrCompose,
controller: Provider.of<ContactInfoState>(context).messageDraft.ctrlCompose,
focusNode: focusNode,
autofocus: !Platform.isAndroid,
textInputAction: TextInputAction.newline,
@ -612,22 +620,20 @@ class _MessageViewState extends State<MessageView> {
maxLengthEnforcement: MaxLengthEnforcement.enforced,
maxLines: 3,
onFieldSubmitted: _sendMessage,
style: TextStyle(
fontFamily: "Inter",
fontSize: 12.0 * Provider.of<Settings>(context).fontScaling,
fontWeight: FontWeight.w500,
),
style: Provider.of<Settings>(context).scaleFonts(defaultMessageTextStyle).copyWith(
fontWeight: FontWeight.w500,
),
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,
hintStyle: TextStyle(fontFamily: "Inter", fontSize: 10.0 * Provider.of<Settings>(context).fontScaling, color: Provider.of<Settings>(context).theme.sendHintTextColor),
hintText: AppLocalizations.of(context)!.placeholderEnterMessage,
hintStyle:
Provider.of<Settings>(context).scaleFonts(defaultMessageTextStyle).copyWith(color: Provider.of<Settings>(context).theme.sendHintTextColor, fontWeight: FontWeight.bold),
enabledBorder: InputBorder.none,
focusedBorder: InputBorder.none,
enabled: true,
@ -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

@ -14,6 +14,7 @@ import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../themes/opaque.dart';
/// Peer Settings View Provides way to Configure .
class PeerSettingsView extends StatefulWidget {
@ -216,8 +217,9 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
subtitle: Text(AppLocalizations.of(context)!.savePeerHistoryDescription),
leading: Icon(CwtchIcons.peer_history, color: settings.current().mainTextColor),
trailing: DropdownButton(
value: Provider.of<ContactInfoState>(context).savePeerHistory == "DefaultDeleteHistory"
? AppLocalizations.of(context)!.dontSavePeerHistory
value: (Provider.of<ContactInfoState>(context).savePeerHistory == "DefaultDeleteHistory" ||
Provider.of<ContactInfoState>(context).savePeerHistory == "HistoryDefault")
? AppLocalizations.of(context)!.conversationNotificationPolicyDefault
: (Provider.of<ContactInfoState>(context).savePeerHistory == "SaveHistory"
? AppLocalizations.of(context)!.savePeerHistory
: AppLocalizations.of(context)!.dontSavePeerHistory),
@ -229,8 +231,13 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
const SaveHistoryKey = "profile.SavePeerHistory";
const SaveHistory = "SaveHistory";
const DelHistory = "DeleteHistoryConfirmed";
const HistoryDefault = "HistoryDefault";
if (newValue == AppLocalizations.of(context)!.savePeerHistory) {
// NOTE: .savePeerHistory is used to show ephemeral warnings so it's state is important to update.
if(newValue == AppLocalizations.of(context)!.conversationNotificationPolicyDefault) {
Provider.of<ContactInfoState>(context, listen: false).savePeerHistory = HistoryDefault;
Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, SaveHistoryKey, HistoryDefault);
} else if (newValue == AppLocalizations.of(context)!.savePeerHistory) {
Provider.of<ContactInfoState>(context, listen: false).savePeerHistory = SaveHistory;
Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, SaveHistoryKey, SaveHistory);
} else {
@ -239,10 +246,10 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
}
});
},
items: [AppLocalizations.of(context)!.savePeerHistory, AppLocalizations.of(context)!.dontSavePeerHistory].map<DropdownMenuItem<String>>((String value) {
items: [AppLocalizations.of(context)!.conversationNotificationPolicyDefault, AppLocalizations.of(context)!.savePeerHistory, AppLocalizations.of(context)!.dontSavePeerHistory].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
child: Text(value, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
);
}).toList())),
ListTile(
@ -254,7 +261,7 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
items: ConversationNotificationPolicy.values.map<DropdownMenuItem<ConversationNotificationPolicy>>((ConversationNotificationPolicy value) {
return DropdownMenuItem<ConversationNotificationPolicy>(
value: value,
child: Text(value.toName(context)),
child: Text(value.toName(context), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
);
}).toList(),
onChanged: (ConversationNotificationPolicy? newVal) {

View File

@ -20,6 +20,8 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../errorHandler.dart';
import '../main.dart';
import '../config.dart';
import '../models/appstate.dart';
import '../themes/opaque.dart';
/// Pane to add or edit a server
class RemoteServerView extends StatefulWidget {
@ -91,12 +93,26 @@ class _RemoteServerViewState extends State<RemoteServerView> {
SizedBox(
height: 20,
),
Row(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [
Tooltip(
message: serverInfoState.groups.isNotEmpty ? AppLocalizations.of(context)!.cannotDeleteServerIfActiveGroups : AppLocalizations.of(context)!.leaveConversation,
child: ElevatedButton.icon(
onPressed: serverInfoState.groups.isNotEmpty ? null : () {
showAlertDialog(context);
},
icon: Icon(CwtchIcons.leave_group),
label: Text(
AppLocalizations.of(context)!.deleteBtn,
style: settings.scaleFonts(defaultTextButtonStyle),
),
))
]),
Padding(
padding: EdgeInsets.all(8),
child: Text(AppLocalizations.of(context)!.groupsOnThisServerLabel),
),
Expanded(child: _buildGroupsList(serverInfoState))
Expanded(child: _buildGroupsList(serverInfoState)),
])));
});
}
@ -147,3 +163,40 @@ class _RemoteServerViewState extends State<RemoteServerView> {
]));
}
}
showAlertDialog(BuildContext context) {
// set up the buttons
Widget cancelButton = ElevatedButton(
child: Text(AppLocalizations.of(context)!.cancel),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
);
Widget continueButton = ElevatedButton(
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
child: Text(AppLocalizations.of(context)!.deleteServerConfirmBtn),
onPressed: () {
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
var serverInfoState = Provider.of<RemoteServerInfoState>(context, listen: false);
Provider.of<FlwtchState>(context, listen: false).cwtch.DeleteServerInfo(profileOnion, serverInfoState.onion);
Navigator.popUntil(context, (route) => route.settings.name == "profileremoteservers");
},
);
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text(AppLocalizations.of(context)!.deleteServerConfirmBtn),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
builder: (BuildContext context) {
return alert;
},
);
}

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

@ -14,6 +14,7 @@ class CwtchButtonTextField extends StatefulWidget {
this.labelText,
this.testKey,
this.onChanged,
this.textStyle,
});
final TextEditingController controller;
final Function()? onPressed;
@ -22,6 +23,7 @@ class CwtchButtonTextField extends StatefulWidget {
final String tooltip;
final bool readonly;
final Key? testKey;
final TextStyle? textStyle;
String? labelText;
@override
@ -56,7 +58,7 @@ class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
enableIMEPersonalizedLearning: false,
onChanged: widget.onChanged,
maxLines: 1,
style: TextStyle(overflow: TextOverflow.clip),
style: widget.textStyle == null ? TextStyle(overflow: TextOverflow.clip) : widget.textStyle,
decoration: InputDecoration(
labelText: widget.labelText,
labelStyle: TextStyle(color: theme.current().mainTextColor, backgroundColor: theme.current().textfieldBackgroundColor),

View File

@ -10,21 +10,34 @@ import 'package:cwtch/widgets/profileimage.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart';
import '../models/message.dart';
import '../settings.dart';
import 'package:intl/intl.dart';
class ContactRow extends StatefulWidget {
int? messageIndex;
ContactRow({this.messageIndex});
@override
_ContactRowState createState() => _ContactRowState();
}
class _ContactRowState extends State<ContactRow> {
bool isHover = false;
Message? cachedMessage = null;
@override
Widget build(BuildContext context) {
var contact = Provider.of<ContactInfoState>(context);
if (widget.messageIndex != null && this.cachedMessage == null) {
messageHandler(context, Provider.of<ProfileInfoState>(context, listen: false).onion, contact.identifier, ByIndex(widget.messageIndex!)).then((value) {
setState(() {
this.cachedMessage = value;
});
});
}
// Only groups have a sync status
Widget? syncStatus;
if (contact.isGroup) {
@ -37,11 +50,19 @@ class _ContactRowState extends State<ContactRow> {
));
}
bool selected = Provider.of<AppState>(context).selectedConversation == contact.identifier;
if (selected && widget.messageIndex != null) {
if (selected && widget.messageIndex == Provider.of<AppState>(context).selectedSearchMessage) {
selected = true;
} else {
selected = false;
}
}
return InkWell(
enableFeedback: true,
splashFactory: InkSplash.splashFactory,
child: Ink(
color: Provider.of<AppState>(context).selectedConversation == contact.identifier ? Provider.of<Settings>(context).theme.backgroundHilightElementColor : Colors.transparent,
color: selected ? Provider.of<Settings>(context).theme.backgroundHilightElementColor : Colors.transparent,
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Padding(
padding: const EdgeInsets.all(6.0), //border size
@ -85,6 +106,17 @@ class _ContactRowState extends State<ContactRow> {
overflow: TextOverflow.ellipsis,
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor : Provider.of<Settings>(context).theme.mainTextColor)),
),
// we need to ignore the child widget in this context, otherwise gesture events will flow down...
IgnorePointer(
ignoring: true,
child: Visibility(
visible: this.cachedMessage != null,
maintainSize: false,
maintainInteractivity: false,
maintainSemantics: false,
maintainState: false,
child: this.cachedMessage == null ? CircularProgressIndicator() : this.cachedMessage!.getPreviewWidget(context),
)),
Container(
padding: EdgeInsets.all(0),
child: contact.isInvitation == true
@ -124,7 +156,7 @@ class _ContactRowState extends State<ContactRow> {
icon: Icon(Icons.block, color: Provider.of<Settings>(context).theme.mainTextColor),
onPressed: () {},
)
: Text(dateToNiceString(contact.lastMessageTime))),
: Text(dateToNiceString(widget.messageIndex == null ? contact.lastMessageTime : (this.cachedMessage?.getMetadata().timestamp ?? DateTime.now())))),
),
],
))),
@ -149,7 +181,7 @@ class _ContactRowState extends State<ContactRow> {
])),
onTap: () {
setState(() {
selectConversation(context, contact.identifier);
selectConversation(context, contact.identifier, widget.messageIndex);
});
},
onHover: (hover) {

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,
@ -467,7 +430,7 @@ class FileBubbleState extends State<FileBubble> {
icon: Icon(Icons.arrow_downward),
onPressed: androidExport,
label: Text(
AppLocalizations.of(context)!.saveBtn,
AppLocalizations.of(bcontext)!.saveBtn,
)))),
]),
))));

View File

@ -14,7 +14,8 @@ class CwtchFolderPicker extends StatefulWidget {
final String description;
final Function(String)? onSave;
final Key? testKey;
const CwtchFolderPicker({Key? key, this.testKey, this.label = "", this.tooltip = "", this.initialValue = "", this.onSave, this.description = ""}) : super(key: key);
final TextStyle? textStyle;
const CwtchFolderPicker({Key? key, this.testKey, this.textStyle, this.label = "", this.tooltip = "", this.initialValue = "", this.onSave, this.description = ""}) : super(key: key);
@override
_CwtchFolderPickerState createState() => _CwtchFolderPickerState();
@ -40,6 +41,7 @@ class _CwtchFolderPickerState extends State<CwtchFolderPicker> {
child: CwtchButtonTextField(
testKey: widget.testKey,
controller: ctrlrVal,
textStyle: widget.textStyle,
readonly: Platform.isAndroid,
onPressed: Provider.of<AppState>(context).disableFilePicker
? null

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

@ -38,7 +38,8 @@ class _MessageListState extends State<MessageList> {
//bool isGroupAndSynced = Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status == "Synced";
//bool isGroupAndNotAuthenticated = Provider.of<ContactInfoState>(context).isGroup && Provider.of<ContactInfoState>(context).status != "Authenticated";
bool showEphemeralWarning = (isP2P && Provider.of<ContactInfoState>(context).savePeerHistory != "SaveHistory");
bool preserveHistoryByDefault = Provider.of<Settings>(context, listen:false).preserveHistoryByDefault;
bool showEphemeralWarning = (isP2P && (!preserveHistoryByDefault && Provider.of<ContactInfoState>(context).savePeerHistory != "SaveHistory"));
bool showOfflineWarning = Provider.of<ContactInfoState>(context).isOnline() == false;
bool showSyncing = isGroupAndSyncing;
bool showMessageWarning = showEphemeralWarning || showOfflineWarning || showSyncing;

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,
];
@ -289,7 +290,9 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
)))));
if (Provider.of<ContactInfoState>(context).newMarkerMsgIndex == widget.index) {
return Column(crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [Align(alignment: Alignment.center, child: _bubbleNew()), mr]);
return Column(
crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [Align(alignment: Alignment.center, child: Padding(padding: EdgeInsets.all(5.0), child: _bubbleNew())), mr]);
} else {
return mr;
}
@ -307,7 +310,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) {
@ -339,7 +342,7 @@ class MessageRowState extends State<MessageRow> with SingleTickerProviderStateMi
if (id == null) {
// Can't happen
} else {
selectConversation(context, id);
selectConversation(context, id, null);
var contactIndex = Provider.of<ProfileInfoState>(context, listen: false).contactList.filteredList().indexWhere((element) => element.identifier == id);
Provider.of<ProfileInfoState>(context, listen: false).contactListScrollController.jumpTo(index: contactIndex);
}

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

@ -3,6 +3,7 @@ import 'package:cwtch/models/profile.dart';
import 'package:cwtch/models/profileservers.dart';
import 'package:cwtch/models/remoteserver.dart';
import 'package:cwtch/models/servers.dart';
import 'package:cwtch/themes/opaque.dart';
import 'package:cwtch/views/addeditservers.dart';
import 'package:cwtch/views/remoteserverview.dart';
import 'package:cwtch/widgets/profileimage.dart';
@ -50,7 +51,9 @@ class _RemoteServerRowState extends State<RemoteServerRow> {
Text(
description,
semanticsLabel: description,
style: TextStyle(fontFamily: "Inter", fontSize: 10.0 * Provider.of<Settings>(context).fontScaling, fontWeight: FontWeight.bold)
style: Provider.of<Settings>(context)
.scaleFonts(defaultFormLabelTextStyle)
.copyWith(fontWeight: FontWeight.bold)
.apply(color: running ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor : Provider.of<Settings>(context).theme.portraitOfflineBorderColor),
softWrap: true,
overflow: TextOverflow.ellipsis,

View File

@ -1,7 +1,6 @@
import 'package:cwtch/main.dart';
import 'package:cwtch/models/servers.dart';
import 'package:cwtch/themes/opaque.dart';
import 'package:cwtch/views/addeditservers.dart';
import 'package:cwtch/widgets/profileimage.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
@ -43,7 +42,8 @@ class _ServerRowState extends State<ServerRow> {
Text(
server.description,
semanticsLabel: server.description,
style: TextStyle(fontFamily: "Inter", fontSize: 10.0 * Provider.of<Settings>(context).fontScaling)
style: Provider.of<Settings>(context)
.scaleFonts(defaultFormLabelTextStyle)
.apply(color: server.running ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor : Provider.of<Settings>(context).theme.portraitOfflineBorderColor),
softWrap: true,
overflow: TextOverflow.ellipsis,
@ -55,7 +55,9 @@ class _ServerRowState extends State<ServerRow> {
server.onion,
softWrap: true,
overflow: TextOverflow.ellipsis,
style: TextStyle(color: server.running ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor : Provider.of<Settings>(context).theme.portraitOfflineBorderColor),
style: Provider.of<Settings>(context)
.scaleFonts(defaultFormLabelTextStyle)
.copyWith(color: server.running ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor : Provider.of<Settings>(context).theme.portraitOfflineBorderColor),
)))
],
)),

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

View File

@ -1,3 +1,4 @@
import 'package:cwtch/themes/opaque.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -64,7 +65,7 @@ class _CwtchTextFieldState extends State<CwtchTextField> {
scrollController: _scrollController,
enableIMEPersonalizedLearning: false,
focusNode: _focusNode,
style: TextStyle(overflow: TextOverflow.clip),
style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle).copyWith(overflow: TextOverflow.clip),
decoration: InputDecoration(
errorMaxLines: 2,
hintText: widget.hintText,

View File

@ -1,11 +1,19 @@
# Project-level configuration.
cmake_minimum_required(VERSION 3.10)
project(runner LANGUAGES CXX)
# The name of the executable created for the application. Change this to change
# the on-disk name of your application.
set(BINARY_NAME "cwtch")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
set(APPLICATION_ID "im.cwtch.cwtch")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
cmake_policy(SET CMP0063 NEW)
# Load bundled libraries from the lib/ directory relative to the binary.
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
# Root filesystem for cross-building.
@ -18,7 +26,7 @@ if(FLUTTER_TARGET_PLATFORM_SYSROOT)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endif()
# Configure build options.
# Define build configuration options.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE "Debug" CACHE
STRING "Flutter build mode" FORCE)
@ -27,13 +35,20 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
endif()
# Compilation settings that should be applied to most targets.
#
# Be cautious about adding new options here, as plugins use this function by
# default. In most cases, you should add new options to specific targets instead
# of modifying this function.
function(APPLY_STANDARD_SETTINGS TARGET)
target_compile_features(${TARGET} PUBLIC cxx_std_14)
target_compile_options(${TARGET} PRIVATE -Wall -Werror)
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>")
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>")
# -Os -fno-ident (optimize for size, and strip idents, don't generate a build-id, fix hash style)
target_compile_options(${TARGET} PRIVATE -Wall -Werror -Os -fno-ident)
target_link_libraries(${TARGET} PRIVATE -Qn -Os -Wl,-s,--hash-style=gnu,--build-id=none,--script=${PROJECT_BUILD_DIR}/../elf_x86_64.x)
endfunction()
# Flutter library and tool build rules.
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
# Flutter library and tool build rules.
@ -45,16 +60,27 @@ pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
# Application build
# Define the application target. To change its name, change BINARY_NAME above,
# not the value here, or `flutter run` will no longer work.
#
# Any new source files that you add to the application should be added here.
add_executable(${BINARY_NAME}
"main.cc"
"my_application.cc"
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
)
# Apply the standard set of build settings. This can be removed for applications
# that need different build settings.
apply_standard_settings(${BINARY_NAME})
# Add dependency libraries. Add any application-specific dependencies here.
target_link_libraries(${BINARY_NAME} PRIVATE flutter)
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
# Run the Flutter tool portions of the build. This must not be removed.
add_dependencies(${BINARY_NAME} flutter_assemble)
# Only the install-generated bundle's copy of the executable will launch
# correctly, since the resources must in the right relative locations. To avoid
# people trying to run the unbundled copy, put it in a subdirectory instead of
@ -64,6 +90,7 @@ set_target_properties(${BINARY_NAME}
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
)
# Generated plugin build rules, which manage building the plugins and adding
# them to the application.
include(flutter/generated_plugins.cmake)
@ -94,11 +121,11 @@ install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
if(PLUGIN_BUNDLED_LIBRARIES)
install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
install(FILES "${bundled_library}"
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
COMPONENT Runtime)
endif()
endforeach(bundled_library)
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.

57
linux/cwtch-whonix.yml Normal file
View File

@ -0,0 +1,57 @@
# TODO: This can likely be restricted even further, especially in regards to the ADD_ONION pattern
- exe-paths:
- ''
users:
- '*'
hosts:
- '*'
commands:
AUTHCHALLENGE:
- 'SAFECOOKIE .*'
SETEVENTS:
- 'CIRC WARN ERR'
- 'CIRC ORCONN INFO NOTICE WARN ERR HS_DESC HS_DESC_CONTENT'
GETINFO:
- 'net/listeners/socks'
- '.*'
GETCONF:
- 'DisableNetwork'
SETCONF:
- 'DisableNetwork.*'
ADD_ONION:
- '.*'
DEL_ONION:
- '.+'
HSFETCH:
- '.+'
events:
CIRC:
suppress: true
ORCONN:
suppress: true
INFO:
suppress: true
NOTICE:
suppress: true
WARN:
suppress: true
ERR:
suppress: true
HS_DESC:
response:
- pattern: '650 HS_DESC CREATED (\S+) (\S+) (\S+) \S+ (.+)'
replacement: '650 HS_DESC CREATED {} {} {} redacted {}'
- pattern: '650 HS_DESC UPLOAD (\S+) (\S+) .*'
replacement: '650 HS_DESC UPLOAD {} {} redacted redacted'
- pattern: '650 HS_DESC UPLOADED (\S+) (\S+) .+'
replacement: '650 HS_DESC UPLOADED {} {} redacted'
- pattern: '650 HS_DESC REQUESTED (\S+) NO_AUTH'
replacement: '650 HS_DESC REQUESTED {} NO_AUTH'
- pattern: '650 HS_DESC REQUESTED (\S+) NO_AUTH \S+ \S+'
replacement: '650 HS_DESC REQUESTED {} NO_AUTH redacted redacted'
- pattern: '650 HS_DESC RECEIVED (\S+) NO_AUTH \S+ \S+'
replacement: '650 HS_DESC RECEIVED {} NO_AUTH redacted redacted'
- pattern: '.*'
replacement: ''
HS_DESC_CONTENT:
suppress: true

View File

@ -10,6 +10,7 @@
#include <pwd.h>
#include <flutter_linux/flutter_linux.h>
#include <flutter_linux/fl_dart_project.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
@ -28,7 +29,6 @@ struct _MyApplication {
struct _FlDartProject {
GObject parent_instance;
gboolean enable_mirrors;
gchar* aot_library_path;
gchar* assets_path;
gchar* icu_data_path;
@ -65,29 +65,29 @@ static void my_application_activate(GApplication* application) {
// if future cases occur).
gboolean use_header_bar = TRUE;
#ifdef GDK_WINDOWING_X11
GdkScreen *screen = gtk_window_get_screen(window);
GdkScreen* screen = gtk_window_get_screen(window);
if (GDK_IS_X11_SCREEN(screen)) {
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
use_header_bar = FALSE;
}
}
#endif
if (use_header_bar) {
GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "cwtch");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
}
else {
} else {
gtk_window_set_title(window, "cwtch");
}
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
g_signal_connect(G_OBJECT(window),
// custom hook to catch app exit and send notification to flutter to cleanup
g_signal_connect(G_OBJECT(window),
"destroy", G_CALLBACK(on_shutdown), NULL);
g_autoptr(FlDartProject) project = fl_dart_project_new();
@ -98,58 +98,51 @@ static void my_application_activate(GApplication* application) {
if (stat(fl_dart_project_get_assets_path(project), &info ) != 0 ) {
if( stat("lib/cwtch", &info) == 0) {
// use local dir structure
project->assets_path = g_build_filename("data", "flutter_assets", nullptr);
fl_dart_project_set_assets_path(project, g_build_filename("data", "flutter_assets", nullptr));
project->aot_library_path = g_build_filename("lib", "libapp.so", nullptr);
project->icu_data_path = g_build_filename("data", "icudtl.dat", nullptr);
fl_dart_project_set_icu_data_path(project, g_build_filename("data", "icudtl.dat", nullptr));
gtk_window_set_icon_from_file(window, "./cwtch.png", NULL);
} else if( stat("/usr/share/cwtch/data/flutter_assets", &info ) != 0 ) {
// if we're non in sys installed structure, use home dir structure
struct passwd *pw = getpwuid(getuid());
const char *homedir = pw->pw_dir;
// /home/$USER/.local/share/cwtch/data/flutter_assets
project->assets_path = g_build_filename(homedir, ".local", "share", "cwtch", "data", "flutter_assets", nullptr);
fl_dart_project_set_assets_path(project, g_build_filename(homedir, ".local", "share", "cwtch", "data", "flutter_assets", nullptr));
// /home/$USER/.local/lib/cwtch/
project->aot_library_path = g_build_filename(homedir, ".local", "lib", "cwtch", "libapp.so", nullptr);
// /home/$USER/.local/share/cwtch/data
project->icu_data_path = g_build_filename(homedir, ".local", "share", "cwtch", "data", "icudtl.dat", nullptr);
fl_dart_project_set_icu_data_path(project, g_build_filename(homedir, ".local", "share", "cwtch", "data", "icudtl.dat", nullptr));
gtk_window_set_icon_from_file(window, g_build_filename(homedir, ".local", "share", "icons", "cwtch.png", nullptr), NULL);
} else {
// else assume we are in sys installed structure
// /usr/share/cwtch/data/flutter_assets
project->assets_path = g_build_filename("/", "usr", "share", "cwtch", "data", "flutter_assets", nullptr);
fl_dart_project_set_assets_path(project, g_build_filename("/", "usr", "share", "cwtch", "data", "flutter_assets", nullptr));
// /usr/lib/cwtch
project->aot_library_path = g_build_filename("/", "usr", "lib", "cwtch", "libapp.so", nullptr);
// /usr/share/cwtch/data
project->icu_data_path = g_build_filename("/", "usr", "share", "cwtch", "data", "icudtl.dat", nullptr);
fl_dart_project_set_icu_data_path(project, g_build_filename("/", "usr", "share", "cwtch", "data", "icudtl.dat", nullptr));
gtk_window_set_icon_from_file(window, "/usr/share/icons/cwtch.png", NULL);
}
}
printf("my_application.cc: using aot_library_path or '%s'\n", project->aot_library_path);
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
// Create a specific channel for shutting down cwtch when the close button is triggered
// We have registered the "destroy" handle above for this reason
FlEngine *engine = fl_view_get_engine(view);
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
g_autoptr(FlBinaryMessenger) messenger = fl_engine_get_binary_messenger(engine);
channel = fl_method_channel_new(messenger, "im.cwtch.linux.shutdown", FL_METHOD_CODEC(codec));
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
g_autoptr(FlBinaryMessenger) messenger = fl_engine_get_binary_messenger(engine);
channel = fl_method_channel_new(messenger, "im.cwtch.linux.shutdown", FL_METHOD_CODEC(codec));
gtk_widget_grab_focus(GTK_WIDGET(view));
}
// Implements GApplication::local_command_line.
static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) {
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {
MyApplication* self = MY_APPLICATION(application);
// Strip out the first argument as it is the binary name.
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
@ -168,7 +161,7 @@ static gboolean my_application_local_command_line(GApplication* application, gch
}
// Implements GObject::dispose.
static void my_application_dispose(GObject *object) {
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);

View File

@ -1,4 +1,4 @@
platform :osx, '10.11'
platform :osx, '10.14'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

View File

@ -1,11 +1,16 @@
PODS:
- connectivity_plus (0.0.1):
- FlutterMacOS
- ReachabilitySwift
- flutter_local_notifications (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- package_info_plus_macos (0.0.1):
- package_info_plus (0.0.1):
- FlutterMacOS
- path_provider_macos (0.0.1):
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- ReachabilitySwift (5.0.0)
- screen_retriever (0.0.1):
- FlutterMacOS
- url_launcher_macos (0.0.1):
@ -14,23 +19,30 @@ PODS:
- FlutterMacOS
DEPENDENCIES:
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- package_info_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
SPEC REPOS:
trunk:
- ReachabilitySwift
EXTERNAL SOURCES:
connectivity_plus:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
flutter_local_notifications:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos
FlutterMacOS:
:path: Flutter/ephemeral
package_info_plus_macos:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus_macos/macos
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
package_info_plus:
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
path_provider_foundation:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
screen_retriever:
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
url_launcher_macos:
@ -39,14 +51,16 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
SPEC CHECKSUMS:
connectivity_plus: 18d3c32514c886e046de60e9c13895109866c747
flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
package_info_plus_macos: f010621b07802a241d96d01876d6705f15e77c1c
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce
path_provider_foundation: eaf5b3e458fc0e5fbb9940fb09980e853fe058b8
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
COCOAPODS: 1.11.2
COCOAPODS: 1.11.3

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 51;
objectVersion = 54;
objects = {
/* Begin PBXAggregateTarget section */
@ -202,7 +202,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 0930;
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
33CC10EC2044A3C60003C045 = {
@ -255,6 +255,7 @@
/* Begin PBXShellScriptBuildPhase section */
3399D490228B24CF009A79C7 /* ShellScript */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
@ -403,7 +404,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
@ -483,7 +484,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
@ -530,7 +531,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.11;
MACOSX_DEPLOYMENT_TARGET = 10.14;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -5,18 +5,18 @@ packages:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "405666cd3cf0ee0a48d21ec67e65406aad2c726d9fa58840d3375e7bdcd32a07"
sha256: ae92f5d747aee634b87f89d9946000c2de774be1d6ac3e58268224348cd0101a
url: "https://pub.dev"
source: hosted
version: "60.0.0"
version: "61.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1952250bd005bacb895a01bf1b4dc00e3ba1c526cf47dca54dfe24979c65f5b3"
sha256: ea3d8652bda62982addfd92fdc2d0214e5f82e43325104990d4f4c4a2a313562
url: "https://pub.dev"
source: hosted
version: "5.12.0"
version: "5.13.0"
archive:
dependency: transitive
description:
@ -29,10 +29,10 @@ packages:
dependency: transitive
description:
name: args
sha256: c372bb384f273f0c2a8aaaa226dad84dc27c8519a691b888725dec59518ad53a
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "2.4.1"
version: "2.4.2"
async:
dependency: transitive
description:
@ -77,26 +77,26 @@ packages:
dependency: transitive
description:
name: build_resolvers
sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95
url: "https://pub.dev"
source: hosted
version: "2.0.10"
version: "2.2.0"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "220ae4553e50d7c21a17c051afc7b183d28a24a420502e842f303f8e4e6edced"
sha256: "5e1929ad37d48bd382b124266cb8e521de5548d406a45a5ae6656c13dab73e37"
url: "https://pub.dev"
source: hosted
version: "2.4.4"
version: "2.4.5"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "30859c90e9ddaccc484f56303931f477b1f1ba2bab74aa32ed5d6ce15870f8cf"
sha256: "6d6ee4276b1c5f34f21fdf39425202712d2be82019983d52f351c94aafbc2c41"
url: "https://pub.dev"
source: hosted
version: "7.2.8"
version: "7.2.10"
built_collection:
dependency: transitive
description:
@ -109,10 +109,10 @@ packages:
dependency: transitive
description:
name: built_value
sha256: "2f17434bd5d52a26762043d6b43bb53b3acd029b4d9071a329f46d67ef297e6d"
sha256: "598a2a682e2a7a90f08ba39c0aaa9374c5112340f0a2e275f61b59389543d166"
url: "https://pub.dev"
source: hosted
version: "8.5.0"
version: "8.6.1"
characters:
dependency: transitive
description:
@ -149,10 +149,10 @@ packages:
dependency: transitive
description:
name: code_builder
sha256: "0d43dd1288fd145de1ecc9a3948ad4a6d5a82f0a14c4fdd0892260787d975cbe"
sha256: "4ad01d6e56db961d29661561effde45e519939fdaeb46c351275b182eac70189"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
version: "4.5.0"
collection:
dependency: transitive
description:
@ -211,10 +211,10 @@ packages:
dependency: transitive
description:
name: dart_style
sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
sha256: f4f1f73ab3fd2afcbcca165ee601fe980d966af6a21b5970c6c9376955c528ad
url: "https://pub.dev"
source: hosted
version: "2.2.4"
version: "2.3.1"
dbus:
dependency: "direct main"
description:
@ -278,7 +278,7 @@ packages:
description:
path: "."
ref: main
resolved-ref: "9e89cc0d89770242a3c2e86573f9618a0dd49194"
resolved-ref: cfa8a5f93267e8b544cd705ad015dcc55d7804fa
url: "https://git.openprivacy.ca/openprivacy/flutter_gherkin"
source: git
version: "3.0.0-rc.17"
@ -286,10 +286,10 @@ packages:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: ee6ee56855aa920899b68586b538474d086c149932220b47b92502cbfb5ba5e5
sha256: "812791d43ccfc1b443a0d39fa02a206fc228c597e28ff9337e09e3ca8d370391"
url: "https://pub.dev"
source: hosted
version: "14.0.0+2"
version: "14.1.1"
flutter_local_notifications_linux:
dependency: transitive
description:
@ -315,10 +315,10 @@ packages:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "96af49aa6b57c10a312106ad6f71deed5a754029c24789bbf620ba784f0bd0b0"
sha256: "950e77c2bbe1692bc0874fc7fb491b96a4dc340457f4ea1641443d0a6c1ea360"
url: "https://pub.dev"
source: hosted
version: "2.0.14"
version: "2.0.15"
flutter_test:
dependency: transitive
description: flutter
@ -362,26 +362,26 @@ packages:
dependency: "direct main"
description:
name: glob
sha256: "4515b5b6ddb505ebdd242a5f2cc5d22d3d6a80013789debfbda7777f47ea308c"
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
version: "2.1.2"
graphs:
dependency: transitive
description:
name: graphs
sha256: "772db3d53d23361d4ffcf5a9bb091cf3ee9b22f2be52cd107cd7a2683a89ba0e"
sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19
url: "https://pub.dev"
source: hosted
version: "2.3.0"
version: "2.3.1"
http:
dependency: transitive
description:
name: http
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2"
sha256: "4c3f04bfb64d3efd508d06b41b825542f08122d30bda4933fb95c069d22a4fa3"
url: "https://pub.dev"
source: hosted
version: "0.13.6"
version: "1.0.0"
http_multi_server:
dependency: transitive
description:
@ -447,10 +447,10 @@ packages:
dependency: transitive
description:
name: logging
sha256: "04094f2eb032cbb06c6f6e8d3607edcfcb0455e2bb6cbc010cb01171dcb64e6d"
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.2.0"
matcher:
dependency: transitive
description:
@ -487,10 +487,10 @@ packages:
dependency: "direct dev"
description:
name: msix
sha256: "88ee83949d87dc635ffd51d7c17ef222390e5067693cc248046661b12a353d13"
sha256: bf151b9a1648985e7604672ade54c8df693566099d0731e318548c47d54676e6
url: "https://pub.dev"
source: hosted
version: "3.12.2"
version: "3.14.2"
nested:
dependency: transitive
description:
@ -511,10 +511,10 @@ packages:
dependency: "direct main"
description:
name: package_info_plus
sha256: d39e8fbff4c5aef4592737e25ad6ac500df006ce7a7a8e1f838ce1256e167542
sha256: ceb027f6bc6a60674a233b4a90a7658af1aebdea833da0b5b53c1e9821a78c7b
url: "https://pub.dev"
source: hosted
version: "4.0.0"
version: "4.0.2"
package_info_plus_platform_interface:
dependency: transitive
description:
@ -559,10 +559,10 @@ packages:
dependency: transitive
description:
name: path_provider_linux
sha256: "2ae08f2216225427e64ad224a24354221c2c7907e448e6e0e8b57b1eb9f10ad1"
sha256: ffbb8cc9ed2c9ec0e4b7a541e56fd79b138e8f47d2fb86815f15358a349b3b57
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.1.11"
path_provider_platform_interface:
dependency: transitive
description:
@ -575,10 +575,10 @@ packages:
dependency: transitive
description:
name: path_provider_windows
sha256: d3f80b32e83ec208ac95253e0cd4d298e104fbc63cb29c5c69edaed43b0c69d6
sha256: "1cb68ba4cd3a795033de62ba1b7b4564dace301f952de6bfb3cd91b202b6ee96"
url: "https://pub.dev"
source: hosted
version: "2.1.6"
version: "2.1.7"
petitparser:
dependency: transitive
description:
@ -708,10 +708,10 @@ packages:
dependency: transitive
description:
name: source_gen
sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
sha256: "373f96cf5a8744bc9816c1ff41cf5391bbdbe3d7a96fe98c622b6738a8a7bd33"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
version: "1.3.2"
source_span:
dependency: transitive
description:
@ -812,10 +812,10 @@ packages:
dependency: transitive
description:
name: url_launcher_android
sha256: "7aac14be5f4731b923cc697ae2d42043945076cd0dbb8806baecc92c1dc88891"
sha256: eed4e6a1164aa9794409325c3b707ff424d4d1c2a785e7db67f8bbda00e36e51
url: "https://pub.dev"
source: hosted
version: "6.0.33"
version: "6.0.35"
url_launcher_ios:
dependency: transitive
description:
@ -852,10 +852,10 @@ packages:
dependency: transitive
description:
name: url_launcher_web
sha256: "81fe91b6c4f84f222d186a9d23c73157dc4c8e1c71489c4d08be1ad3b228f1aa"
sha256: "6bb1e5d7fe53daf02a8fee85352432a40b1f868a81880e99ec7440113d5cfcab"
url: "https://pub.dev"
source: hosted
version: "2.0.16"
version: "2.0.17"
url_launcher_windows:
dependency: transitive
description:
@ -924,18 +924,18 @@ packages:
dependency: "direct main"
description:
name: win_toast
sha256: "371d62b17b30489cad41e831e6340c2777202d2b4b2fd55a14ffc566b3df7518"
sha256: "6349ef17a487d2ebf9caa51da1cb5a325f4cd7e5a601935ced456f3c00d1e64f"
url: "https://pub.dev"
source: hosted
version: "0.3.0"
version: "0.0.2"
window_manager:
dependency: "direct main"
description:
name: window_manager
sha256: "2b2572442b2a5178642730442dc625ac088244f5827b1f0811371b1b7485eb62"
sha256: "95096fede562cbb65f30d38b62d819a458f59ba9fe4a317f6cee669710f6676b"
url: "https://pub.dev"
source: hosted
version: "0.3.2"
version: "0.3.4"
xdg_directories:
dependency: transitive
description:

View File

@ -44,7 +44,11 @@ dependencies:
url_launcher: ^6.0.18
window_manager: ^0.3.2
# notification plugins
win_toast: ^0.3.0
## Somewhere between 0.0.2 and 0.3 they introduced a dependancy on a new Windows RT feature
## Which can only be linked to with a new VC 17 compiler and we're suspicious only work on
## Windows 2022 and newer... we can't seem to automate compiling on win server 2019 containers
## So pinning to 0.0.2 for the forseeable future or until we change plugins
win_toast: ^0.0.2
flutter_local_notifications: ^14.0.0+2
dbus: ^0.7.8
connectivity_plus:

View File

@ -7,7 +7,7 @@ flutter pub run build_runner build --delete-conflicting-outputs
PATH=$PATH:$PWD/linux/Tor
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"$PWD/linux/":"$PWD/linux/Tor/"
PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH CWTCH_HOME=$PWD/integration_test/env/temp/ flutter test -d linux --dart-define TEST_MODE=true integration_test/gherkin_suite_test.dart
PATH=$PATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH LOG_FILE=test.log CWTCH_HOME=$PWD/integration_test/env/temp/ flutter test -d linux --dart-define TEST_MODE=true integration_test/gherkin_suite_test.dart
#node index2.js
#if [ "$HEADLESS" = "false" ]; then
# xdg-open integration_test/gherkin/reports/cucumber_report.html

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Some files were not shown because too many files have changed in this diff Show More