Merge branch 'trunk' of git.openprivacy.ca:flutter/flutter_app into dualpane

This commit is contained in:
erinn 2021-06-23 17:35:40 -07:00
commit f3858e5fd7
39 changed files with 1613 additions and 1469 deletions

View File

@ -28,9 +28,7 @@ steps:
- name: deps
path: /root/.pub-cache
commands:
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/tor
- wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/master/tor/torrc
- chmod a+x tor
- ./fetch-tor.sh
- echo `git describe --tags` > VERSION
- echo `date +%G-%m-%d-%H-%M` > BUILDDATE
- flutter pub get
@ -56,17 +54,28 @@ steps:
- flutter build linux --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
- mkdir deploy/linux
- cp -r build/linux/x64/release/bundle/* deploy/linux
- cp linux/cwtch.desktop deploy/linux
- cp linux/cwtch.*.desktop deploy/linux
- cp linux/install-*.sh deploy/linux
- cp linux/cwtch.png deploy/linux
- cp linux/libCwtch.so deploy/linux/lib/
# should not be needed, should be in data/flutter_assets and work from there
#- cp /sdks/flutter/bin/cache/artifacts/engine/linux-x64/icudtl.dat deploy/linux
- cp tor deploy/linux
- cp linux/tor deploy/linux
- cd deploy
- mv linux cwtch
- tar -czf cwtch-`cat ../VERSION`.tar.gz cwtch
- rm -r cwtch
- name: test-build-android
image: cirrusci/flutter:dev
when:
event: pull_request
volumes:
- name: deps
path: /root/.pub-cache
commands:
- flutter build apk --debug
- name: build-android
image: cirrusci/flutter:dev
when:
@ -204,8 +213,8 @@ steps:
- name: fetch
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
commands:
- powershell -command "Invoke-WebRequest -Uri https://www.torproject.org/dist/torbrowser/10.0.16/tor-win32-0.4.5.7.zip -OutFile tor.zip"
- powershell -command "if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '2b7d683f036d0fec149f1d2bdfcf5b7ef4c337005a2b685c056b00047fdb2b57d4c25b8559ad7ef5c7a030b273934be82a9f83ef6e391f5d7d13d8d6c83e8048' ) { Write-Error 'tor.zip sha512sum mismatch' }"
- powershell -command "Invoke-WebRequest -Uri https://dist.torproject.org/torbrowser/10.0.18/tor-win64-0.4.5.9.zip -OutFile tor.zip"
- powershell -command "if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '72764eb07ad8ab511603aba0734951ca003989f5f4686af91ba220217b4a8a4bcc5f571b59f52c847932f6efedf847b111621983050fcddbb8099d43ca66fb07' ) { Write-Error 'tor.zip sha512sum mismatch' }"
- git describe --tags > VERSION
- powershell -command "Get-Date -Format 'yyyy-MM-dd-HH-mm'" > BUILDDATE
- .\fetch-libcwtch-go.ps1
@ -261,4 +270,4 @@ trigger:
repo: flutter/flutter_app
branch: trunk
event:
- push
- push

View File

@ -1 +1 @@
v0.0.2-66-g39187a7-2021-06-15-00-32
v0.0.2-104-gc1b7e4c-2021-06-22-23-59

50
SPEC.md
View File

@ -8,7 +8,7 @@ required - any new Cwtch work is beyond the scope of this initial spec.
# Functional Requirements
- [ ] Kill all processes / isolates on exit (Blocked - P1)
- [ ] Android Service? (P1)
- [X] Android Service? (P1)
# Splash Screen
- [X] Android
@ -16,9 +16,9 @@ required - any new Cwtch work is beyond the scope of this initial spec.
- [ ] Desktop (P2)
# Custom Styled Widgets
- [/] Label Widget
- [X] Label Widget
- [X] Initial
- [ ] With Accessibility / Zoom Integration (P1)
- [X] With Accessibility / Zoom Integration (P1)
- [X] Text Field Widget
- [X] Password Widget
- [X] Text Button Widget (for Copy)
@ -33,10 +33,10 @@ required - any new Cwtch work is beyond the scope of this initial spec.
- [X] Profile Picture
- [X] default images
- [ ] custom images (P3)
- [ ] coloured ring border (P2)
- [X] coloured ring border (P2)
- [X] Profile Name
- [X] Edit Button
- [ ] Unread messages badge (P2)
- [X Unread messages badge (P2)
- [X] Navigate to a specific Profile Contacts Pane (when clicking on a Profile row)
- [X] Navigate to a specific Profile Management Pane (edit Button)
- [X] Navigate to the Settings Pane (Settings Button in Action bar)
@ -54,12 +54,12 @@ required - any new Cwtch work is beyond the scope of this initial spec.
- [X] Update Profile Name
- [X] Update Profile Password
- [ ] Error Message When Attempting to Update Password with Wrong Old Password (P2)
- [X] Error Message When Attempting to Update Password with Wrong Old Password (P2)
- [ ] Easy Transition from Unencrypted Profile -> Encrypted Profile (P3)
- [ ] Delete a Profile (P2)
- [ ] Dialog Acknowledgement (P2)
- [ ] Require Old Password Gate (P2)
- [ ] Async Checking of Password (P2)
- [X] Delete a Profile (P2)
- [X] Dialog Acknowledgement (P2)
- [X] Require Old Password Gate (P2)
- [X] Async Checking of Password (P2)
- [X] Copy Profile Onion Address
## Profile Pane (formally Contacts Pane)
@ -76,11 +76,11 @@ required - any new Cwtch work is beyond the scope of this initial spec.
- [X] Name
- [X] Onion
- [X] Online Status
- [ ] Unread Messages Badge (P1)
- [ ] In Order of Most Recent Message / Activity (P1)
- [ ] With Accept / Reject Heart/Trash Bin Option (P1)
- [ ] Separate list area for Blocked Contacts (P1)
- [ ] Display all Group Contacts (if experiment is enabled)
- [X] Unread Messages Badge (P1)
- [X] In Order of Most Recent Message / Activity (P1)
- [X] With Accept / Reject Heart/Trash Bin Option (P1)
- [X] Separate list area for Blocked Contacts (P1)
- [X] Display all Group Contacts (if experiment is enabled)
- [X] Navigate to a specific Contact or Group Message Pane (Contact Row)
- [X] Pressing Back should go back to the home pane
@ -88,22 +88,22 @@ required - any new Cwtch work is beyond the scope of this initial spec.
- [X] Allowing Copying the Profile Onion Address for Sharing
- [X] Allowing Pasting a Peer Onion Address for adding to Contacts
- [ ] (with optional name field)
- [ ] Allowing Pasting a Group Invite / Server Address
- [X] Allowing Pasting a Group Invite / Server Address
- [X] (if group experiment is enabled)
## Message Overlay
- [X] Display Messages from Contacts
- [ ] Allowing copying the text of a specific message (on mobile) (P2)
- [X] Allowing copying the text of a specific message (on mobile) (P2)
- [X] Send a message to the specific Contact / Group
- [~] Display the Acknowledgement status of a message (P1)
- [ ] Navigate to the specific Contact or Group Settings Pane ( Settings Button in Action bar)
- [X] Navigate to the specific Contact or Group Settings Pane ( Settings Button in Action bar)
- [ ] Emoji Support (P1)
- [ ] Display in-message emoji text labels e.g. `:label:` as emoji. (P1)
- [ ] Functional Emoji Drawer Widget for Selection (P2)
- [ ] Mutant Standard? (P2)
- [ ] Display a warning if Contact / Server is offline (Broken Heart) (P1)
- [ ] Display a warning for configuring peer history (P2)
- [X] Display a warning if Contact / Server is offline (Broken Heart) (P1)
- [X] Display a warning for configuring peer history (P2)
- [X] Pressing Back should go back to the contacts pane
## List Overlay (P3)
@ -123,11 +123,11 @@ required - any new Cwtch work is beyond the scope of this initial spec.
- [X] Pressing Back should go back to the message pane
## Group Settings Pane (experimental - P3)
- [ ] Gated behind group experiment
- [ ] Update local name of group
- [ ] Get Group Invite
- [ ] Leave Group
- [ ] Pressing Back should go back to the message pane for the group
- [X] Gated behind group experiment
- [X] Update local name of group
- [X] Get Group Invite
- [X] Leave Group
- [X] Pressing Back should go back to the message pane for the group

View File

@ -2,7 +2,6 @@ package im.cwtch.flwtch
import android.app.*
import android.content.Context
import android.content.Context.ACTIVITY_SERVICE
import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.Color
@ -24,7 +23,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
NotificationManager
private var notificationID: MutableMap<String, Int> = mutableMapOf()
private var notificationIDnext: Int = 1;
private var notificationIDnext: Int = 1
override suspend fun doWork(): Result {
val method = inputData.getString(KEY_METHOD)
@ -32,7 +31,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
val args = inputData.getString(KEY_ARGS)
?: return Result.failure()
// Mark the Worker as important
val progress = "Trying to do a Flwtch"//todo:translate
val progress = "Cwtch is keeping Tor running in the background"//todo:translate
setForeground(createForegroundInfo(progress))
return handleCwtch(method, args)
}
@ -45,8 +44,8 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
return notificationID[k] ?: -1
}
private suspend fun handleCwtch(method: String, args: String): Result {
val a = JSONObject(args);
private fun handleCwtch(method: String, args: String): Result {
val a = JSONObject(args)
when (method) {
"Start" -> {
Log.i("FlwtchWorker.kt", "handleAppInfo Start")
@ -54,46 +53,47 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
val torPath = (a.get("torPath") as? String) ?: "tor"
Log.i("FlwtchWorker.kt", "appDir: '$appDir' torPath: '$torPath'")
if (Cwtch.startCwtch(appDir, torPath) != 0.toByte()) return Result.failure()
if (Cwtch.startCwtch(appDir, torPath) != 0.toLong()) return Result.failure()
// infinite coroutine :)
Log.i("FlwtchWorker.kt", "startCwtch success, starting coroutine AppbusEvent loop...")
while(true) {
Log.i("FlwtchWorker.kt", "while(true)getAppbusEvent()")
val evt = MainActivity.AppbusEvent(Cwtch.getAppBusEvent())
if (evt.EventType == "NewMessageFromPeer") {
if (evt.EventType == "NewMessageFromPeer" || evt.EventType == "NewMessageFromGroup") {
val data = JSONObject(evt.Data)
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createMessageNotificationChannel(data.getString("RemotePeer"), data.getString("RemotePeer"))
} 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)
""
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createMessageNotificationChannel(data.getString("RemotePeer"), data.getString("RemotePeer"))
} 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 loader = FlutterInjector.instance().flutterLoader()
val key = loader.getLookupKeyForAsset("assets/"+data.getString("Picture"))//"assets/profiles/001-centaur.png")
val fh = applicationContext.assets.open(key)
val clickIntent = Intent(applicationContext, MainActivity::class.java).also { intent ->
intent.setAction(Intent.ACTION_RUN)
intent.action = Intent.ACTION_RUN
intent.putExtra("EventType", "NotificationClicked")
intent.putExtra("ProfileOnion", data.getString("ProfileOnion"))
intent.putExtra("RemotePeer", data.getString("RemotePeer"))
intent.putExtra("RemotePeer", if (evt.EventType == "NewMessageFromPeer") data.getString("RemotePeer") else data.getString("GroupID"))
}
val newNotification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle(data.getString("Nick"))
.setContentText("New message")
.setLargeIcon(BitmapFactory.decodeStream(fh))
.setSmallIcon(R.mipmap.knott)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build()
.setContentTitle(data.getString("Nick"))
.setContentText("New message")//todo: translate
.setLargeIcon(BitmapFactory.decodeStream(fh))
.setSmallIcon(R.mipmap.knott)
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
.setAutoCancel(true)
.build()
notificationManager.notify(getNotificationID(data.getString("ProfileOnion"), data.getString("RemotePeer")), newNotification)
}
Intent().also { intent ->
intent.setAction("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
intent.action = "im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS"
intent.putExtra("EventType", evt.EventType)
intent.putExtra("Data", evt.Data)
intent.putExtra("EventID", evt.EventID)
@ -104,124 +104,104 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
"ReconnectCwtchForeground" -> {
Cwtch.reconnectCwtchForeground()
}
"SelectProfile" -> {
val onion = (a.get("profile") as? String) ?: "";
Cwtch.selectProfile(onion)
}
"CreateProfile" -> {
val nick = (a.get("nick") as? String) ?: "";
val pass = (a.get("pass") as? String) ?: "";
val nick = (a.get("nick") as? String) ?: ""
val pass = (a.get("pass") as? String) ?: ""
Cwtch.createProfile(nick, pass)
}
"LoadProfiles" -> {
val pass = (a.get("pass") as? String) ?: "";
val pass = (a.get("pass") as? String) ?: ""
Cwtch.loadProfiles(pass)
}
"GetProfiles" -> Result.success(Data.Builder().putString("result", Cwtch.getProfiles()).build())
// "ACNEvents" -> result.success(Cwtch.acnEvents())
"ContactEvents" -> Result.success(Data.Builder().putString("result", Cwtch.contactEvents()).build())
"NumMessages" -> {
val profile = (a.get("profile") as? String) ?: "";
val handle = (a.get("contact") as? String) ?: "";
return Result.success(Data.Builder().putLong("result", Cwtch.numMessages(profile, handle)).build())
}
"GetMessage" -> {
val profile = (a.get("profile") as? String) ?: "";
val handle = (a.get("contact") as? String) ?: "";
val indexI = a.getInt("index") ?: 0;
Log.i("FlwtchWorker.kt", "indexI = " + indexI)
val profile = (a.get("profile") as? String) ?: ""
val handle = (a.get("contact") as? String) ?: ""
val indexI = a.getInt("index")
Log.i("FlwtchWorker.kt", "indexI = $indexI")
return Result.success(Data.Builder().putString("result", Cwtch.getMessage(profile, handle, indexI.toLong())).build())
}
"GetMessages" -> {
val profile = (a.get("profile") as? String) ?: "";
val handle = (a.get("contact") as? String) ?: "";
val start = (a.get("start") as? Long) ?: 0;
val end = (a.get("end") as? Long) ?: 0;
return Result.success(Data.Builder().putString("result", Cwtch.getMessages(profile, handle, start, end)).build())
}
"UpdateMessageFlags" -> {
val profile = (a.get("profile") as? String) ?: "";
val handle = (a.get("contact") as? String) ?: "";
val midx = (a.get("midx") as? Long) ?: 0;
val flags = (a.get("flags") as? Long) ?: 0;
Cwtch.updateMessageFlags(profile, handle, midx, flags);
val profile = (a.get("profile") as? String) ?: ""
val handle = (a.get("contact") as? String) ?: ""
val midx = (a.get("midx") as? Long) ?: 0
val flags = (a.get("flags") as? Long) ?: 0
Cwtch.updateMessageFlags(profile, handle, midx, flags)
}
"AcceptContact" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val handle = (a.get("handle") as? String) ?: "";
Cwtch.acceptContact(profile, handle);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val handle = (a.get("handle") as? String) ?: ""
Cwtch.acceptContact(profile, handle)
}
"BlockContact" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val handle = (a.get("handle") as? String) ?: "";
Cwtch.blockContact(profile, handle);
}
"DebugResetContact" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val handle = (a.get("handle") as? String) ?: "";
Cwtch.debugResetContact(profile, handle);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val handle = (a.get("handle") as? String) ?: ""
Cwtch.blockContact(profile, handle)
}
"SendMessage" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val handle = (a.get("handle") as? String) ?: "";
val message = (a.get("message") as? String) ?: "";
Cwtch.sendMessage(profile, handle, message);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val handle = (a.get("handle") as? String) ?: ""
val message = (a.get("message") as? String) ?: ""
Cwtch.sendMessage(profile, handle, message)
}
"SendInvitation" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val handle = (a.get("handle") as? String) ?: "";
val target = (a.get("target") as? String) ?: "";
Cwtch.sendInvitation(profile, handle, target);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val handle = (a.get("handle") as? String) ?: ""
val target = (a.get("target") as? String) ?: ""
Cwtch.sendInvitation(profile, handle, target)
}
"SendProfileEvent" -> {
val onion = (a.get("onion") as? String) ?: "";
val jsonEvent = (a.get("jsonEvent") as? String) ?: "";
Cwtch.sendProfileEvent(onion, jsonEvent);
val onion = (a.get("onion") as? String) ?: ""
val jsonEvent = (a.get("jsonEvent") as? String) ?: ""
Cwtch.sendProfileEvent(onion, jsonEvent)
}
"SendAppEvent" -> {
val jsonEvent = (a.get("jsonEvent") as? String) ?: "";
Cwtch.sendAppEvent(jsonEvent);
val jsonEvent = (a.get("jsonEvent") as? String) ?: ""
Cwtch.sendAppEvent(jsonEvent)
}
"ResetTor" -> {
Cwtch.resetTor();
Cwtch.resetTor()
}
"ImportBundle" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val bundle = (a.get("bundle") as? String) ?: "";
Cwtch.importBundle(profile, bundle);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val bundle = (a.get("bundle") as? String) ?: ""
Cwtch.importBundle(profile, bundle)
}
"SetGroupAttribute" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val groupHandle = (a.get("groupHandle") as? String) ?: "";
val key = (a.get("key") as? String) ?: "";
val value = (a.get("value") as? String) ?: "";
Cwtch.setGroupAttribute(profile, groupHandle, key, value);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val groupHandle = (a.get("groupHandle") as? String) ?: ""
val key = (a.get("key") as? String) ?: ""
val value = (a.get("value") as? String) ?: ""
Cwtch.setGroupAttribute(profile, groupHandle, key, value)
}
"CreateGroup" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val server = (a.get("server") as? String) ?: "";
val groupName = (a.get("groupname") as? String) ?: "";
Cwtch.createGroup(profile, server, groupName);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val server = (a.get("server") as? String) ?: ""
val groupName = (a.get("groupname") as? String) ?: ""
Cwtch.createGroup(profile, server, groupName)
}
"DeleteProfile" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val groupHandle = (a.get("pass") as? String) ?: "";
Cwtch.deleteProfile(profile, groupHandle);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val pass = (a.get("pass") as? String) ?: ""
Cwtch.deleteProfile(profile, pass)
}
"LeaveConversation" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val contactHandle = (a.get("contactHandle") as? String) ?: "";
Cwtch.leaveConversation(profile, contactHandle);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val contactHandle = (a.get("contactHandle") as? String) ?: ""
Cwtch.leaveConversation(profile, contactHandle)
}
"LeaveGroup" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val groupHandle = (a.get("groupHandle") as? String) ?: "";
Cwtch.leaveGroup(profile, groupHandle);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val groupHandle = (a.get("groupHandle") as? String) ?: ""
Cwtch.leaveGroup(profile, groupHandle)
}
"RejectInvite" -> {
val profile = (a.get("ProfileOnion") as? String) ?: "";
val groupHandle = (a.get("groupHandle") as? String) ?: "";
Cwtch.rejectInvite(profile, groupHandle);
val profile = (a.get("ProfileOnion") as? String) ?: ""
val groupHandle = (a.get("groupHandle") as? String) ?: ""
Cwtch.rejectInvite(profile, groupHandle)
}
"Shutdown" -> {
Cwtch.shutdownCwtch();
return Result.success()
}
else -> return Result.failure()
}
@ -233,21 +213,21 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
private fun createForegroundInfo(progress: String): ForegroundInfo {
val id = "flwtch"
val title = "Flwtch"
val cancel = "Nevermind"//todo: translate
// This PendingIntent can be used to cancel the worker
val intent = WorkManager.getInstance(applicationContext)
.createCancelPendingIntent(getId())
val cancel = "Shut down"//todo: translate
val channelId =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(id, id)
createForegroundNotificationChannel(id, id)
} 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 notification = NotificationCompat.Builder(applicationContext, id)
// This PendingIntent can be used to cancel the worker
val intent = WorkManager.getInstance(applicationContext)
.createCancelPendingIntent(getId())
val notification = NotificationCompat.Builder(applicationContext, channelId)
.setContentTitle(title)
.setTicker(title)
.setContentText(progress)
@ -261,22 +241,18 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
return ForegroundInfo(101, notification)
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel(channelId: String, channelName: String): String{
val chan = NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_NONE)
private fun createForegroundNotificationChannel(channelId: String, channelName: String): String{
val chan = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE)
chan.lightColor = Color.MAGENTA
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
notificationManager.createNotificationChannel(chan)
return channelId
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createMessageNotificationChannel(channelId: String, channelName: String): String{
val chan = NotificationChannel(channelId,
channelName, NotificationManager.IMPORTANCE_HIGH)
val chan = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_HIGH)
chan.lightColor = Color.MAGENTA
chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
notificationManager.createNotificationChannel(chan)
@ -287,10 +263,4 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
const val KEY_METHOD = "KEY_METHOD"
const val KEY_ARGS = "KEY_ARGS"
}
class AppbusEvent(json: String) : JSONObject(json) {
val EventType = this.optString("EventType")
val EventID = this.optString("EventID")
val Data = this.optString("Data")
}
}

View File

@ -7,39 +7,23 @@ import android.content.Intent
import android.content.IntentFilter
import androidx.annotation.NonNull
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.Looper
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import android.view.Window
import androidx.lifecycle.Observer
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.work.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import io.flutter.embedding.android.SplashScreen
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import cwtch.Cwtch
import io.flutter.plugin.common.EventChannel
import kotlin.concurrent.thread
import org.json.JSONObject
import java.io.File
import java.time.Duration
import java.util.concurrent.TimeUnit
class MainActivity: FlutterActivity() {
override fun provideSplashScreen(): SplashScreen? = SplashView()
// Channel to get app info
@ -55,6 +39,10 @@ class MainActivity: FlutterActivity() {
// Channel to trigger contactview when an external notification is clicked
private val CHANNEL_NOTIF_CLICK = "im.cwtch.flwtch/notificationClickHandler"
// WorkManager tag applied to all Start() infinite coroutines
val WORKER_TAG = "cwtchEventBusWorker"
private var myReceiver: MyBroadcastReceiver? = null
private var methodChan: MethodChannel? = null
override fun onNewIntent(intent: Intent) {
@ -74,6 +62,8 @@ class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
// Note: this methods are invoked on the main thread.
//note to self: ask someone if this does anything ^ea
requestWindowFeature(Window.FEATURE_NO_TITLE)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_APP_INFO).setMethodCallHandler { call, result -> handleAppInfo(call, result) }
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_CWTCH).setMethodCallHandler { call, result -> handleCwtch(call, result) }
methodChan = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL_NOTIF_CLICK)
@ -103,42 +93,28 @@ class MainActivity: FlutterActivity() {
// in case the ForegroundService is still running. in both cases, however, we *do* want to re-register
// the eventbus listener.
if (call.method == "Start") {
val workerTag = "cwtchEventBusWorker"
val uniqueTag = argmap["torPath"] ?: "nullEventBus"
// note: because the ForegroundService is specified as UniquePeriodicWork, it can't actually get
// accidentally duplicated. however, we still need to manually check if it's running or not, so
// that we can divert this method call to ReconnectCwtchForeground instead if so.
val works = WorkManager.getInstance(this).getWorkInfosByTag(workerTag).get()
var alreadyRunning = false
val works = WorkManager.getInstance(this).getWorkInfosByTag(WORKER_TAG).get()
for (workInfo in works) {
if (workInfo.tags.contains(uniqueTag)) {
if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) {
alreadyRunning = true
}
} else {
Log.i("handleCwtch:WorkManager", "$workInfo")
if (!workInfo.tags.contains(uniqueTag)) {
Log.i("handleCwtch:WorkManager", "canceling ${workInfo.id} bc tags don't include $uniqueTag")
WorkManager.getInstance(this).cancelWorkById(workInfo.id)
}
}
WorkManager.getInstance(this).pruneWork()
// register our eventbus listener. note that we observe any/all work according to its tag, which
// results in an implicit "reconnection" to old service threads even after frontend restarts
val mc = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(MyBroadcastReceiver(mc), filter)
if (alreadyRunning) {
Log.i("MainActivity.kt", "diverting Start -> Reconnect")
method = "ReconnectCwtchForeground"
} else {
Log.i("MainActivity.kt", "Start() launching foregroundservice")
// this is where the eventbus ForegroundService gets launched. WorkManager should keep it alive after this
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, call.method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
// 15 minutes is the shortest interval you can request
val workRequest = PeriodicWorkRequestBuilder<FlwtchWorker>(15, TimeUnit.MINUTES).setInputData(data).addTag(workerTag).addTag(uniqueTag).build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork("req_$uniqueTag", ExistingPeriodicWorkPolicy.KEEP, workRequest)
return
}
Log.i("MainActivity.kt", "Start() launching foregroundservice")
// this is where the eventbus ForegroundService gets launched. WorkManager should keep it alive after this
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, call.method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
// 15 minutes is the shortest interval you can request
val workRequest = PeriodicWorkRequestBuilder<FlwtchWorker>(15, TimeUnit.MINUTES).setInputData(data).addTag(WORKER_TAG).addTag(uniqueTag).build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork("req_$uniqueTag", ExistingPeriodicWorkPolicy.REPLACE, workRequest)
return
}
// ...otherwise fallthru to a normal ffi method call (and return the result using the result callback)
@ -155,6 +131,43 @@ class MainActivity: FlutterActivity() {
)
}
// using onresume/onstop for broadcastreceiver because of extended discussion on https://stackoverflow.com/questions/7439041/how-to-unregister-broadcastreceiver
override fun onResume() {
super.onResume()
Log.i("MainActivity.kt", "onResume")
if (myReceiver == null) {
Log.i("MainActivity.kt", "onResume registering localbroadcastreceiver")
val mc = MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CWTCH_EVENTBUS)
val filter = IntentFilter("im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS")
myReceiver = MyBroadcastReceiver(mc)
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(myReceiver!!, filter)
}
// ReconnectCwtchForeground which will resync counters and settings...
// We need to do this here because after a "pause" flutter is still running
// but we might have lost sync with the background process...
Log.i("MainActivity.kt", "Call ReconnectCwtchForeground")
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, "ReconnectCwtchForeground").putString(FlwtchWorker.KEY_ARGS, "{}").build()
val workRequest = OneTimeWorkRequestBuilder<FlwtchWorker>().setInputData(data).build()
WorkManager.getInstance(applicationContext).enqueue(workRequest)
}
override fun onStop() {
super.onStop()
Log.i("MainActivity.kt", "onStop")
if (myReceiver != null) {
LocalBroadcastManager.getInstance(applicationContext).unregisterReceiver(myReceiver!!);
myReceiver = null;
}
}
override fun onDestroy() {
super.onDestroy()
Log.i("MainActivity.kt", "onDestroy")
WorkManager.getInstance(this).cancelAllWorkByTag(WORKER_TAG)
WorkManager.getInstance(this).pruneWork()
}
// source: https://web.archive.org/web/20210203022531/https://stackoverflow.com/questions/41928803/how-to-parse-json-in-kotlin/50468095
// for reference:
//

View File

@ -11,7 +11,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:lottie_autoPlay="true"
app:lottie_rawRes="@raw/cwtch_animated_logo"
app:lottie_rawRes="@raw/cwtch_animated_logo_op"
app:lottie_loop="true"
app:lottie_speed="1.00" />

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
assets/cwtch_title.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

12
fetch-tor.sh Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.5.9-linux-x86_64 -O linux/tor
chmod a+x linux/tor
mkdir -p android/app/src/main/jniLibs/arm64-v8a
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.4.9-arm64_pie -O android/app/src/main/jniLibs/arm64-v8a/libtor.so
chmod a+x android/app/src/main/jniLibs/arm64-v8a/libtor.so
mkdir -p android/app/src/main/jniLibs/armeabi-v7a
wget https://git.openprivacy.ca/openprivacy/buildfiles/raw/branch/master/tor/tor-0.4.4.9-arm_pie -O android/app/src/main/jniLibs/armeabi-v7a/libtor.so
chmod a+x android/app/src/main/jniLibs/armeabi-v7a/libtor.so

View File

@ -2,7 +2,7 @@ import 'package:flutter/src/services/text_input.dart';
abstract class Cwtch {
// ignore: non_constant_identifier_names
Future<dynamic> Start();
Future<void> Start();
// ignore: non_constant_identifier_names
Future<void> ReconnectCwtchForeground();
@ -28,26 +28,12 @@ abstract class Cwtch {
void AcceptContact(String profileOnion, String contactHandle);
// ignore: non_constant_identifier_names
void BlockContact(String profileOnion, String contactHandle);
// ignore: non_constant_identifier_names
void DebugResetContact(String profileOnion, String contactHandle);
// ignore: non_constant_identifier_names
Future<dynamic> ACNEvents();
// ignore: non_constant_identifier_names
Future<dynamic> ContactEvents();
// ignore: non_constant_identifier_names
Future<dynamic> GetProfiles();
// ignore: non_constant_identifier_names
Future<dynamic> NumMessages(String profile, String handle);
// ignore: non_constant_identifier_names
Future<dynamic> GetMessage(String profile, String handle, int index);
// ignore: non_constant_identifier_names
void UpdateMessageFlags(String profile, String handle, int index, int flags);
// ignore: non_constant_identifier_names
Future<dynamic> GetMessages(String profile, String handle, int start, int end);
// ignore: non_constant_identifier_names
void SendMessage(String profile, String handle, String message);
// ignore: non_constant_identifier_names
void SendInvitation(String profile, String handle, String target);
@ -67,5 +53,8 @@ abstract class Cwtch {
// ignore: non_constant_identifier_names
void RejectInvite(String profileOnion, String groupHandle);
// ignore: non_constant_identifier_names
void Shutdown();
void dispose();
}

View File

@ -17,20 +17,27 @@ class CwtchNotifier {
late ErrorHandler error;
late TorStatus torStatus;
late NotificationsManager notificationManager;
late AppState appState;
CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP) {
CwtchNotifier(ProfileListState pcn, Settings settingsCN, ErrorHandler errorCN, TorStatus torStatusCN, NotificationsManager notificationManagerP, AppState appStateCN) {
profileCN = pcn;
settings = settingsCN;
error = errorCN;
torStatus = torStatusCN;
notificationManager = notificationManagerP;
appState = appStateCN;
}
void handleMessage(String type, dynamic data) {
switch (type) {
case "CwtchStarted":
appState.SetCwtchInit();
break;
case "CwtchStartError":
appState.SetAppError(data["Error"]);
break;
case "NewPeer":
profileCN.add(ProfileInfoState(
onion: data["Identity"], nickname: data["name"], imagePath: data["picture"], contactsJson: data["ContactsJson"], serversJson: data["ServerList"], online: data["Online"] == "true"));
profileCN.add(data["Identity"], data["name"], data["picture"], data["ContactsJson"], data["ServerList"], data["Online"] == "true");
break;
case "PeerCreated":
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(
@ -57,7 +64,6 @@ class CwtchNotifier {
if (serverInfoState != null) {
status = serverInfoState.status;
}
if (profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"]) == null) {
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(data["ProfileOnion"], data["GroupID"],
isInvitation: false, imagePath: data["PicturePath"], nickname: data["GroupName"], status: status, server: data["GroupServer"], isGroup: true, lastMessageTime: DateTime.now()));
@ -133,6 +139,11 @@ class CwtchNotifier {
}
}
break;
case "MessageCounterResync":
var contactHandle = data["RemotePeer"];
if (contactHandle == null || contactHandle == "") contactHandle = data["GroupID"];
profileCN.getProfile(data["Identity"])?.contactList.getContact(contactHandle)!.totalMessages = int.parse(data["Data"]);
break;
case "IndexedFailure":
print("IndexedFailure: $data");
var idx = data["Index"];
@ -185,6 +196,10 @@ class CwtchNotifier {
print("acn status: $data");
torStatus.handleUpdate(int.parse(data["Progress"]), data["Status"]);
break;
case "ACNVersion":
print("acn version: $data");
torStatus.updateVersion(data["Data"]);
break;
case "UpdateServerInfo":
profileCN.getProfile(data["ProfileOnion"])?.replaceServers(data["ServerList"]);
break;

View File

@ -19,6 +19,9 @@ import '../config.dart';
typedef start_cwtch_function = Int8 Function(Pointer<Utf8> str, Int32 length, Pointer<Utf8> str2, Int32 length2);
typedef StartCwtchFn = int Function(Pointer<Utf8> dir, int len, Pointer<Utf8> tor, int torLen);
typedef void_from_void_funtion = Void Function();
typedef VoidFromVoidFunction = void Function();
typedef void_from_string_string_function = Void Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32);
typedef VoidFromStringStringFn = void Function(Pointer<Utf8>, int, Pointer<Utf8>, int);
@ -58,13 +61,14 @@ typedef GetJsonBlobFromStrStrIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int,
typedef get_json_blob_from_str_str_int_int_function = Pointer<Utf8> Function(Pointer<Utf8>, Int32, Pointer<Utf8>, Int32, Int32, Int32);
typedef GetJsonBlobFromStrStrIntIntFn = Pointer<Utf8> Function(Pointer<Utf8>, int, Pointer<Utf8>, int, int, int);
typedef acn_events_function = Pointer<Utf8> Function();
typedef ACNEventsFn = Pointer<Utf8> Function();
typedef appbus_events_function = Pointer<Utf8> Function();
typedef AppbusEventsFn = Pointer<Utf8> Function();
class CwtchFfi implements Cwtch {
late DynamicLibrary library;
late CwtchNotifier cwtchNotifier;
late Isolate cwtchIsolate;
ReceivePort _receivePort = ReceivePort();
CwtchFfi(CwtchNotifier _cwtchNotifier) {
if (Platform.isWindows) {
@ -80,7 +84,7 @@ class CwtchFfi implements Cwtch {
}
// ignore: non_constant_identifier_names
Future<dynamic> Start() async {
Future<void> Start() async {
String home = "";
String bundledTor = "";
Map<String, String> envVars = Platform.environment;
@ -105,7 +109,6 @@ class CwtchFfi implements Cwtch {
StartCwtch(ut8CwtchDir, ut8CwtchDir.length, bundledTor.toNativeUtf8(), bundledTor.length);
// Spawn an isolate to listen to events from libcwtch-go and then dispatch them when received on main thread to cwtchNotifier
var _receivePort = ReceivePort();
cwtchIsolate = await Isolate.spawn(_checkAppbusEvents, _receivePort.sendPort);
_receivePort.listen((message) {
var env = jsonDecode(message);
@ -135,6 +138,7 @@ class CwtchFfi implements Cwtch {
await for (var value in stream) {
sendPort.send(value);
}
print("checkAppBusEvents finished...");
}
// Steam of appbus events. Call blocks in libcwtch-go GetAppbusEvent. Static so the isolate can use it
@ -146,13 +150,18 @@ class CwtchFfi implements Cwtch {
library = DynamicLibrary.open("libCwtch.so");
}
var getAppbusEventC = library.lookup<NativeFunction<acn_events_function>>("c_GetAppBusEvent");
var getAppbusEventC = library.lookup<NativeFunction<appbus_events_function>>("c_GetAppBusEvent");
// ignore: non_constant_identifier_names
final GetAppbusEvent = getAppbusEventC.asFunction<ACNEventsFn>();
final GetAppbusEvent = getAppbusEventC.asFunction<AppbusEventsFn>();
while (true) {
Pointer<Utf8> result = GetAppbusEvent();
String event = result.toDartString();
if (event.startsWith("{\"EventType\":\"Shutdown\"")) {
print("Shutting down isolate thread: $event");
return;
}
yield event;
}
}
@ -185,50 +194,6 @@ class CwtchFfi implements Cwtch {
LoadProfiles(ut8pass, ut8pass.length);
}
// ignore: non_constant_identifier_names
Future<String> ACNEvents() async {
var acnEventsC = library.lookup<NativeFunction<acn_events_function>>("c_ACNEvents");
// ignore: non_constant_identifier_names
final ACNEvents = acnEventsC.asFunction<ACNEventsFn>();
Pointer<Utf8> result = ACNEvents();
String event = result.toDartString();
return event;
}
// ignore: non_constant_identifier_names
Future<String> ContactEvents() async {
var acnEventsC = library.lookup<NativeFunction<acn_events_function>>("c_ContactEvents");
// ignore: non_constant_identifier_names
final ContactEvents = acnEventsC.asFunction<ACNEventsFn>();
Pointer<Utf8> result = ContactEvents();
String event = result.toDartString();
return event;
}
// ignore: non_constant_identifier_names
Future<String> GetProfiles() async {
var getProfilesC = library.lookup<NativeFunction<get_json_blob_void_function>>("c_GetProfiles");
// ignore: non_constant_identifier_names
final GetProfiles = getProfilesC.asFunction<GetJsonBlobVoidFn>();
Pointer<Utf8> jsonProfilesBytes = GetProfiles();
String jsonProfiles = jsonProfilesBytes.toDartString();
return jsonProfiles;
}
// ignore: non_constant_identifier_names
Future<int> NumMessages(String profile, String handle) async {
var numMessagesC = library.lookup<NativeFunction<get_int_from_str_str_function>>("c_NumMessages");
// ignore: non_constant_identifier_names
final NumMessages = numMessagesC.asFunction<GetIntFromStrStrFn>();
final utf8profile = profile.toNativeUtf8();
final utf8handle = handle.toNativeUtf8();
int num = NumMessages(utf8profile, utf8profile.length, utf8handle, utf8handle.length);
return num;
}
// ignore: non_constant_identifier_names
Future<String> GetMessage(String profile, String handle, int index) async {
var getMessageC = library.lookup<NativeFunction<get_json_blob_from_str_str_int_function>>("c_GetMessage");
@ -241,18 +206,6 @@ class CwtchFfi implements Cwtch {
return jsonMessage;
}
// ignore: non_constant_identifier_names
Future<String> GetMessages(String profile, String handle, int start, int end) async {
var getMessagesC = library.lookup<NativeFunction<get_json_blob_from_str_str_int_int_function>>("c_GetMessages");
// ignore: non_constant_identifier_names
final GetMessages = getMessagesC.asFunction<GetJsonBlobFromStrStrIntIntFn>();
final utf8profile = profile.toNativeUtf8();
final utf8handle = handle.toNativeUtf8();
Pointer<Utf8> jsonMessagesBytes = GetMessages(utf8profile, utf8profile.length, utf8handle, utf8handle.length, start, end);
String jsonMessages = jsonMessagesBytes.toDartString();
return jsonMessages;
}
@override
// ignore: non_constant_identifier_names
void SendProfileEvent(String onion, String json) {
@ -296,17 +249,6 @@ class CwtchFfi implements Cwtch {
BlockContact(u1, u1.length, u2, u2.length);
}
@override
// ignore: non_constant_identifier_names
void DebugResetContact(String profileOnion, String contactHandle) {
var debugResetContact = library.lookup<NativeFunction<string_string_to_void_function>>("c_DebugResetContact");
// ignore: non_constant_identifier_names
final DebugResetContact = debugResetContact.asFunction<VoidFromStringStringFn>();
final u1 = profileOnion.toNativeUtf8();
final u2 = contactHandle.toNativeUtf8();
DebugResetContact(u1, u1.length, u2, u2.length);
}
@override
// ignore: non_constant_identifier_names
void SendMessage(String profileOnion, String contactHandle, String message) {
@ -428,4 +370,21 @@ class CwtchFfi implements Cwtch {
final u2 = currentPassword.toNativeUtf8();
DeleteProfile(u1, u1.length, u2, u2.length);
}
@override
Future<void> Shutdown() async {
var shutdown = library.lookup<NativeFunction<void_from_void_funtion>>("c_ShutdownCwtch");
// ignore: non_constant_identifier_names
// Shutdown Cwtch + Tor...
final Shutdown = shutdown.asFunction<VoidFromVoidFunction>();
Shutdown();
// Kill our Isolate
cwtchIsolate.kill(priority: Isolate.immediate);
print("Isolate killed");
_receivePort.close();
print("Receive Port Closed");
}
}

View File

@ -42,7 +42,7 @@ class CwtchGomobile implements Cwtch {
}
// ignore: non_constant_identifier_names
Future<dynamic> Start() async {
Future<void> Start() async {
print("gomobile.dart: Start()...");
var cwtchDir = path.join((await androidHomeDirectory).path, ".cwtch");
if (EnvironmentConfig.BUILD_VER == dev_version) {
@ -83,28 +83,7 @@ class CwtchGomobile implements Cwtch {
// ignore: non_constant_identifier_names
void DeleteProfile(String onion, String pass) {
cwtchPlatform.invokeMethod("DeleteProfile", {"onion": onion, "pass": pass});
}
// ignore: non_constant_identifier_names
Future<dynamic> ACNEvents() {
return cwtchPlatform.invokeMethod("ACNEvents");
}
// ignore: non_constant_identifier_names
Future<dynamic> ContactEvents() {
return cwtchPlatform.invokeMethod("ContactEvents");
}
// ignore: non_constant_identifier_names
Future<dynamic> GetProfiles() {
print("gomobile.dart: GetProfiles()");
return cwtchPlatform.invokeMethod("GetProfiles");
}
// ignore: non_constant_identifier_names
Future<dynamic> NumMessages(String profile, String handle) {
return cwtchPlatform.invokeMethod("NumMessages", {"profile": profile, "contact": handle});
cwtchPlatform.invokeMethod("DeleteProfile", {"ProfileOnion": onion, "pass": pass});
}
// ignore: non_constant_identifier_names
@ -113,11 +92,6 @@ class CwtchGomobile implements Cwtch {
return cwtchPlatform.invokeMethod("GetMessage", {"profile": profile, "contact": handle, "index": index});
}
// ignore: non_constant_identifier_names
Future<dynamic> GetMessages(String profile, String handle, int start, int end) {
return cwtchPlatform.invokeMethod("GetMessage", {"profile": profile, "contact": handle, "start": start, "end": end});
}
@override
// ignore: non_constant_identifier_names
void SendProfileEvent(String onion, String jsonEvent) {
@ -145,12 +119,6 @@ class CwtchGomobile implements Cwtch {
cwtchPlatform.invokeMethod("BlockContact", {"ProfileOnion": profileOnion, "handle": contactHandle});
}
@override
// ignore: non_constant_identifier_names
void DebugResetContact(String profileOnion, String contactHandle) {
cwtchPlatform.invokeMethod("DebugResetContact", {"ProfileOnion": profileOnion, "handle": contactHandle});
}
@override
// ignore: non_constant_identifier_names
void SendMessage(String profileOnion, String contactHandle, String message) {
@ -209,4 +177,10 @@ class CwtchGomobile implements Cwtch {
print("gomobile.dart UpdateMessageFlags " + index.toString());
cwtchPlatform.invokeMethod("UpdateMessageFlags", {"profile": profile, "contact": handle, "index": index, "flags": flags});
}
@override
Future<void> Shutdown() async {
print("gomobile.dart Shutdown");
cwtchPlatform.invokeMethod("Shutdown", {});
}
}

View File

@ -1,186 +1,190 @@
{
"@@locale": "de",
"@@last_modified": "2021-06-15T02:08:49+02:00",
"createGroupTitle": "Gruppe Anlegen",
"serverLabel": "Server",
"groupNameLabel": "Gruppenname",
"defaultGroupName": "Tolle Gruppe",
"createGroupBtn": "Anlegen",
"profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten",
"copyBtn": "Kopieren",
"copiedToClipboardNotification": "in die Zwischenablage kopiert",
"addPeerTab": "Einen Peer hinzufügen",
"createGroupTab": "Eine Gruppe erstellen",
"joinGroupTab": "Einer Gruppe beitreten",
"peerAddress": "Adresse",
"peerName": "Namen",
"groupName": "Gruppenname",
"server": "Server",
"invitation": "Einladung",
"groupAddr": "Adresse",
"addPeer": "Peer hinzufügen",
"createGroup": "Gruppe erstellen",
"joinGroup": "Gruppe beitreten",
"newBulletinLabel": "Neue Meldung",
"postNewBulletinLabel": "Neue Meldung veröffentlichen",
"titlePlaceholder": "Titel...",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"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.",
"leaveGroup": "Leave This Conversation",
"inviteToGroup": "You have been invited to join a group:",
"pasteAddressToAddContact": "Adresse hier hinzufügen, um einen Kontakt aufzunehmen",
"blocked": "Blockiert",
"cycleCatsAndroid": "Click to cycle category.\nLong-press to reset.",
"cycleCatsDesktop": "Click to cycle category.\nRight-click to reset.",
"cycleMorphsAndroid": "Click to cycle morphs.\nLong-press to reset.",
"cycleMorphsDesktop": "Click to cycle morphs.\nRight-click to reset.",
"cycleColoursAndroid": "Click to cycle colours.\nLong-press to reset.",
"cycleColoursDesktop": "Click to cycle colours.\nRight-click to reset.",
"search": "Suche...",
"invitationLabel": "Einladung",
"serverInfo": "Server-Informationen",
"serverConnectivityConnected": "Server verbunden",
"serverConnectivityDisconnected": "Server getrennt",
"serverSynced": "Synced",
"serverNotSynced": "Out of Sync",
"viewServerInfo": "Server Info",
"saveBtn": "speichern",
"inviteToGroupLabel": "In die Gruppe einladen",
"inviteBtn": "Einladen",
"deleteBtn": "löschen",
"update": "Update",
"searchList": "Search List",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"addListItemBtn": "Element hinzufügen",
"membershipDescription": "Unten steht eine Liste der Benutzer, die Nachrichten an die Gruppe gesendet haben. Möglicherweise enthält diese Benutzerzliste nicht alle, die Zugang zur Gruppe haben.",
"dmTooltip": "Klicken, um DM zu senden",
"couldNotSendMsgError": "Nachricht konnte nicht gesendet werden",
"acknowledgedLabel": "bestätigt",
"pendingLabel": "Bestätigung ausstehend",
"peerBlockedMessage": "Peer ist blockiert",
"peerOfflineMessage": "Peer ist offline, Nachrichten können derzeit nicht zugestellt werden",
"copiedClipboardNotification": "in die Zwischenablage kopiert",
"newGroupBtn": "Neue Gruppe anlegen",
"acceptGroupInviteLabel": "Möchtest Du die Einladung annehmen",
"acceptGroupBtn": "Annehmen",
"rejectGroupBtn": "Ablehnen",
"chatBtn": "Chat",
"listsBtn": "Listen",
"bulletinsBtn": "Meldungen",
"puzzleGameBtn": "Puzzlespiel",
"addressLabel": "Adresse",
"displayNameLabel": "Angezeigter Name",
"blockBtn": "Peer blockieren",
"savePeerHistory": "Peer-Verlauf speichern",
"savePeerHistoryDescription": "Legt fest, ob ein mit dem Peer verknüpfter Verlauf gelöscht werden soll oder nicht.",
"dontSavePeerHistory": "Peer-Verlauf löschen",
"unblockBtn": "Peer entblockieren",
"addProfileTitle": "Neues Profil hinzufügen",
"editProfileTitle": "Profil bearbeiten",
"profileName": "Anzeigename",
"defaultProfileName": "Alice",
"newProfile": "Neues Profil",
"editProfile": "Profil bearbeiten",
"radioUsePassword": "Passwort",
"radioNoPassword": "Unverschlüsselt (kein Passwort)",
"noPasswordWarning": "Wenn für dieses Konto kein Passwort verwendet wird, bedeutet dies, dass alle lokal gespeicherten Daten nicht verschlüsselt werden.",
"yourDisplayName": "Ihr Anzeigename",
"currentPasswordLabel": "derzeitiges Passwort",
"password1Label": "Passwort",
"password2Label": "Passwort erneut eingeben",
"passwordErrorEmpty": "Passwort kann nicht leer sein",
"createProfileBtn": "Profil speichern",
"saveProfileBtn": "Profil speichern",
"passwordErrorMatch": "Passwörter stimmen nicht überein",
"passwordChangeError": "Fehler beim Ändern des Passworts: Das Passwort wurde abgelehnt",
"deleteProfileBtn": "Profil löschen",
"deleteConfirmLabel": "Geben Sie LÖSCHEN zur Bestätigung ein",
"deleteProfileConfirmBtn": "Profil wirklich löschen",
"deleteConfirmText": "LÖSCHEN",
"addNewProfileBtn": "Neues Profil hinzufügen",
"enterProfilePassword": "Geben Sie ein Passwort ein, um Ihre Profile anzuzeigen",
"password": "Passwort",
"error0ProfilesLoadedForPassword": "0 Profile mit diesem Passwort geladen",
"yourProfiles": "Ihre Profile",
"yourServers": "Ihre Server",
"unlock": "Entsperren",
"cwtchSettingsTitle": "Cwtch Einstellungen",
"versionBuilddate": "Version: %1 Aufgebaut auf: %2",
"zoomLabel": "Benutzeroberflächen-Zoom (betriftt hauptsächlich Text- und Knopgrößen)",
"blockUnknownLabel": "Unbekannte Peers blockieren",
"settingLanguage": "Sprache",
"localeEn": "English",
"localeFr": "Frances",
"localePt": "Portuguesa",
"localeDe": "Deutsche",
"settingInterfaceZoom": "Zoomstufe",
"largeTextLabel": "Groß",
"settingTheme": "Thema",
"themeLight": "Licht",
"themeDark": "Dunkel",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"titleManageServers": "Manage Servers",
"dateMonthsAgo": "Months Ago",
"dateNever": "Never",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateLastYear": "Last Year",
"dateYesterday": "Yesterday",
"dateLastMonth": "Last Month",
"dateWeeksAgo": "Weeks Ago",
"dateDaysAgo": "Days Ago",
"dateHoursAgo": "Hours Ago",
"dateMinutesAgo": "Minutes Ago",
"dateRightNow": "Right Now",
"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.",
"tooltipOpenSettings": "Open the settings pane",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"conversationSettings": "Conversation Settings",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"enableGroups": "Enable Group Chat",
"experimentsEnabled": "Experimente aktiviert",
"versionTor": "Version %1 mit tor %2",
"version": "Version %1",
"builddate": "Aufgebaut auf: %2",
"defaultScalingText": "defaultmäßige Textgröße (Skalierungsfaktor:",
"smallTextLabel": "Klein",
"loadingTor": "Tor wird geladen...",
"viewGroupMembershipTooltip": "Gruppenmitgliedschaft anzeigen",
"networkStatusDisconnected": "Vom Internet getrennt, überprüfen Sie Ihre Verbindung",
"networkStatusAttemptingTor": "Versuche, eine Verbindung mit dem Tor-Netzwerk herzustellen",
"networkStatusConnecting": "Verbinde zu Netzwerk und Peers ...",
"networkStatusOnline": "Online",
"newConnectionPaneTitle": "Neue Verbindung",
"localeIt": "Italiana",
"localeEs": "Espanol",
"addListItem": "Liste hinzufügen",
"addNewItem": "Ein neues Element zur Liste hinzufügen",
"todoPlaceholder": "noch zu erledigen",
"localeEs": "Espanol",
"localeIt": "Italiana",
"enableGroups": "Enable Group Chat",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"conversationSettings": "Conversation Settings",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"tooltipOpenSettings": "Open the settings pane",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"titleManageProfiles": "Manage Cwtch Profiles",
"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.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"successfullAddedContact": "Successfully added ",
"dateRightNow": "Right Now",
"dateMinutesAgo": "Minutes Ago",
"dateHoursAgo": "Hours Ago",
"dateDaysAgo": "Days Ago",
"dateWeeksAgo": "Weeks Ago",
"dateLastMonth": "Last Month",
"dateYesterday": "Yesterday",
"dateLastYear": "Last Year",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateNever": "Never",
"dateMonthsAgo": "Months Ago",
"titleManageServers": "Manage Servers",
"inviteToGroup": "You have been invited to join a group:",
"leaveGroup": "Leave This Conversation",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"yesLeave": "Yes, Leave This Conversation",
"newPassword": "New Password",
"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.",
"accepted": "Accepted!",
"rejected": "Rejected!",
"contactSuggestion": "This is a contact suggestion for: ",
"sendAnInvitation": "You sent an invitation for: ",
"torVersion": "Tor Version",
"torStatus": "Tor Status",
"resetTor": "Reset",
"cancel": "Cancel",
"sendMessage": "Send Message",
"sendInvite": "Send a contact or group invite",
"deleteProfileSuccess": "Successfully deleted profile",
"addServerFirst": "You need to add a server before you can create a group",
"nickChangeSuccess": "Profile nickname changed successfully",
"createProfileToBegin": "Please create or unlock a profile to begin",
"addContactFirst": "Add or pick a contact to begin chatting.",
"torNetworkStatus": "Tor network status",
"debugLog": "Turn on console debug logging",
"profileDeleteSuccess": "Successfully deleted profile",
"malformedMessage": "Malformed message"
"newConnectionPaneTitle": "Neue Verbindung",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Verbinde zu Netzwerk und Peers ...",
"networkStatusAttemptingTor": "Versuche, eine Verbindung mit dem Tor-Netzwerk herzustellen",
"networkStatusDisconnected": "Vom Internet getrennt, überprüfen Sie Ihre Verbindung",
"viewGroupMembershipTooltip": "Gruppenmitgliedschaft anzeigen",
"loadingTor": "Tor wird geladen...",
"smallTextLabel": "Klein",
"defaultScalingText": "defaultmäßige Textgröße (Skalierungsfaktor:",
"builddate": "Aufgebaut auf: %2",
"version": "Version %1",
"versionTor": "Version %1 mit tor %2",
"themeDark": "Dunkel",
"themeLight": "Licht",
"settingTheme": "Thema",
"largeTextLabel": "Groß",
"settingInterfaceZoom": "Zoomstufe",
"localeDe": "Deutsche",
"localePt": "Portuguesa",
"localeFr": "Frances",
"localeEn": "English",
"settingLanguage": "Sprache",
"blockUnknownLabel": "Unbekannte Peers blockieren",
"zoomLabel": "Benutzeroberflächen-Zoom (betriftt hauptsächlich Text- und Knopgrößen)",
"versionBuilddate": "Version: %1 Aufgebaut auf: %2",
"cwtchSettingsTitle": "Cwtch Einstellungen",
"unlock": "Entsperren",
"yourServers": "Ihre Server",
"yourProfiles": "Ihre Profile",
"error0ProfilesLoadedForPassword": "0 Profile mit diesem Passwort geladen",
"password": "Passwort",
"enterProfilePassword": "Geben Sie ein Passwort ein, um Ihre Profile anzuzeigen",
"addNewProfileBtn": "Neues Profil hinzufügen",
"deleteConfirmText": "LÖSCHEN",
"deleteProfileConfirmBtn": "Profil wirklich löschen",
"deleteConfirmLabel": "Geben Sie LÖSCHEN zur Bestätigung ein",
"deleteProfileBtn": "Profil löschen",
"passwordChangeError": "Fehler beim Ändern des Passworts: Das Passwort wurde abgelehnt",
"passwordErrorMatch": "Passwörter stimmen nicht überein",
"saveProfileBtn": "Profil speichern",
"createProfileBtn": "Profil speichern",
"passwordErrorEmpty": "Passwort kann nicht leer sein",
"password2Label": "Passwort erneut eingeben",
"password1Label": "Passwort",
"currentPasswordLabel": "derzeitiges Passwort",
"yourDisplayName": "Ihr Anzeigename",
"profileOnionLabel": "Senden Sie diese Adresse an Peers, mit denen Sie sich verbinden möchten",
"noPasswordWarning": "Wenn für dieses Konto kein Passwort verwendet wird, bedeutet dies, dass alle lokal gespeicherten Daten nicht verschlüsselt werden.",
"radioNoPassword": "Unverschlüsselt (kein Passwort)",
"radioUsePassword": "Passwort",
"copiedToClipboardNotification": "in die Zwischenablage kopiert",
"copyBtn": "Kopieren",
"editProfile": "Profil bearbeiten",
"newProfile": "Neues Profil",
"defaultProfileName": "Alice",
"profileName": "Anzeigename",
"editProfileTitle": "Profil bearbeiten",
"addProfileTitle": "Neues Profil hinzufügen",
"deleteBtn": "löschen",
"unblockBtn": "Peer entblockieren",
"dontSavePeerHistory": "Peer-Verlauf löschen",
"savePeerHistoryDescription": "Legt fest, ob ein mit dem Peer verknüpfter Verlauf gelöscht werden soll oder nicht.",
"savePeerHistory": "Peer-Verlauf speichern",
"blockBtn": "Peer blockieren",
"saveBtn": "speichern",
"displayNameLabel": "Angezeigter Name",
"addressLabel": "Adresse",
"puzzleGameBtn": "Puzzlespiel",
"bulletinsBtn": "Meldungen",
"listsBtn": "Listen",
"chatBtn": "Chat",
"rejectGroupBtn": "Ablehnen",
"acceptGroupBtn": "Annehmen",
"acceptGroupInviteLabel": "Möchtest Du die Einladung annehmen",
"newGroupBtn": "Neue Gruppe anlegen",
"copiedClipboardNotification": "in die Zwischenablage kopiert",
"peerOfflineMessage": "Peer ist offline, Nachrichten können derzeit nicht zugestellt werden",
"peerBlockedMessage": "Peer ist blockiert",
"pendingLabel": "Bestätigung ausstehend",
"acknowledgedLabel": "bestätigt",
"couldNotSendMsgError": "Nachricht konnte nicht gesendet werden",
"dmTooltip": "Klicken, um DM zu senden",
"membershipDescription": "Unten steht eine Liste der Benutzer, die Nachrichten an die Gruppe gesendet haben. Möglicherweise enthält diese Benutzerzliste nicht alle, die Zugang zur Gruppe haben.",
"addListItemBtn": "Element hinzufügen",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"searchList": "Search List",
"update": "Update",
"inviteBtn": "Einladen",
"inviteToGroupLabel": "In die Gruppe einladen",
"groupNameLabel": "Gruppenname",
"viewServerInfo": "Server Info",
"serverNotSynced": "Out of Sync",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server getrennt",
"serverConnectivityConnected": "Server verbunden",
"serverInfo": "Server-Informationen",
"invitationLabel": "Einladung",
"serverLabel": "Server",
"search": "Suche...",
"cycleColoursDesktop": "Click to cycle colours.\nRight-click to reset.",
"cycleColoursAndroid": "Click to cycle colours.\nLong-press to reset.",
"cycleMorphsDesktop": "Click to cycle morphs.\nRight-click to reset.",
"cycleMorphsAndroid": "Click to cycle morphs.\nLong-press to reset.",
"cycleCatsDesktop": "Click to cycle category.\nRight-click to reset.",
"cycleCatsAndroid": "Click to cycle category.\nLong-press to reset.",
"blocked": "Blockiert",
"titlePlaceholder": "Titel...",
"postNewBulletinLabel": "Neue Meldung veröffentlichen",
"newBulletinLabel": "Neue Meldung",
"joinGroup": "Gruppe beitreten",
"createGroup": "Gruppe erstellen",
"addPeer": "Peer hinzufügen",
"groupAddr": "Adresse",
"invitation": "Einladung",
"server": "Server",
"groupName": "Gruppenname",
"peerName": "Namen",
"peerAddress": "Adresse",
"joinGroupTab": "Einer Gruppe beitreten",
"createGroupTab": "Eine Gruppe erstellen",
"addPeerTab": "Einen Peer hinzufügen",
"createGroupBtn": "Anlegen",
"defaultGroupName": "Tolle Gruppe",
"createGroupTitle": "Gruppe Anlegen"
}

View File

@ -1,186 +1,190 @@
{
"@@locale": "en",
"@@last_modified": "2021-06-15T02:08:49+02:00",
"createGroupTitle": "Create Group",
"serverLabel": "Server",
"groupNameLabel": "Group Name",
"defaultGroupName": "Awesome Group",
"createGroupBtn": "Create",
"profileOnionLabel": "Send this address to peers you want to connect with",
"copyBtn": "Copy",
"copiedToClipboardNotification": "Copied to Clipboard",
"addPeerTab": "Add a peer",
"createGroupTab": "Create a group",
"joinGroupTab": "Join a group",
"peerAddress": "Address",
"peerName": "Name",
"groupName": "Group name",
"server": "Server",
"invitation": "Invitation",
"groupAddr": "Address",
"addPeer": "Add Peer",
"createGroup": "Create group",
"joinGroup": "Join group",
"newBulletinLabel": "New Bulletin",
"postNewBulletinLabel": "Post new bulletin",
"titlePlaceholder": "title...",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"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.",
"leaveGroup": "Leave This Conversation",
"inviteToGroup": "You have been invited to join a group:",
"pasteAddressToAddContact": "Paste a cwtch address, invitation or key bundle here to add a new conversation",
"blocked": "Blocked",
"cycleCatsAndroid": "Click to cycle category.\nLong-press to reset.",
"cycleCatsDesktop": "Click to cycle category.\nRight-click to reset.",
"cycleMorphsAndroid": "Click to cycle morphs.\nLong-press to reset.",
"cycleMorphsDesktop": "Click to cycle morphs.\nRight-click to reset.",
"cycleColoursAndroid": "Click to cycle colours.\nLong-press to reset.",
"cycleColoursDesktop": "Click to cycle colours.\nRight-click to reset.",
"search": "Search...",
"invitationLabel": "Invitation",
"serverInfo": "Server Information",
"serverConnectivityConnected": "Server Connected",
"serverConnectivityDisconnected": "Server Disconnected",
"serverSynced": "Synced",
"serverNotSynced": "Out of Sync",
"viewServerInfo": "Server Info",
"saveBtn": "Save",
"inviteToGroupLabel": "Invite to group",
"inviteBtn": "Invite",
"deleteBtn": "Delete",
"update": "Update",
"searchList": "Search List",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"addListItemBtn": "Add Item",
"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.",
"dmTooltip": "Click to DM",
"couldNotSendMsgError": "Could not send this message",
"acknowledgedLabel": "Acknowledged",
"pendingLabel": "Pending",
"peerBlockedMessage": "Peer is blocked",
"peerOfflineMessage": "Peer is offline, messages can't be delivered right now",
"copiedClipboardNotification": "Copied to clipboard",
"newGroupBtn": "Create new group",
"acceptGroupInviteLabel": "Do you want to accept the invitation to",
"acceptGroupBtn": "Accept",
"rejectGroupBtn": "Reject",
"chatBtn": "Chat",
"listsBtn": "Lists",
"bulletinsBtn": "Bulletins",
"puzzleGameBtn": "Puzzle Game",
"addressLabel": "Address",
"displayNameLabel": "Display Name",
"blockBtn": "Block Peer",
"savePeerHistory": "Save Peer History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.",
"dontSavePeerHistory": "Delete Peer History",
"unblockBtn": "Unblock Peer",
"addProfileTitle": "Add new profile",
"editProfileTitle": "Edit Profile",
"profileName": "Display name",
"defaultProfileName": "Alice",
"newProfile": "New Profile",
"editProfile": "Edit Profille",
"radioUsePassword": "Password",
"radioNoPassword": "Unencrypted (No password)",
"noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted",
"yourDisplayName": "Your Display Name",
"currentPasswordLabel": "Current Password",
"password1Label": "Password",
"password2Label": "Reenter password",
"passwordErrorEmpty": "Password cannot be empty",
"createProfileBtn": "Create Profile",
"saveProfileBtn": "Save Profile",
"passwordErrorMatch": "Passwords do not match",
"passwordChangeError": "Error changing password: Supplied password rejected",
"deleteProfileBtn": "Delete Profile",
"deleteConfirmLabel": "Type DELETE to confirm",
"deleteProfileConfirmBtn": "Really Delete Profile",
"deleteConfirmText": "DELETE",
"addNewProfileBtn": "Add new profile",
"enterProfilePassword": "Enter a password to view your profiles",
"password": "Password",
"error0ProfilesLoadedForPassword": "0 profiles loaded with that password",
"yourProfiles": "Your Profiles",
"yourServers": "Your Servers",
"unlock": "Unlock",
"cwtchSettingsTitle": "Cwtch Settings",
"versionBuilddate": "Version: %1 Built on: %2",
"zoomLabel": "Interface zoom (mostly affects text and button sizes)",
"blockUnknownLabel": "Block Unknown Peers",
"settingLanguage": "Language",
"localeEn": "English",
"localeFr": "Frances",
"localePt": "Portuguesa",
"localeDe": "Deutsche",
"settingInterfaceZoom": "Zoom level",
"largeTextLabel": "Large",
"settingTheme": "Theme",
"themeLight": "Light",
"themeDark": "Dark",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"titleManageServers": "Manage Servers",
"dateMonthsAgo": "Months Ago",
"dateNever": "Never",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateLastYear": "Last Year",
"dateYesterday": "Yesterday",
"dateLastMonth": "Last Month",
"dateWeeksAgo": "Weeks Ago",
"dateDaysAgo": "Days Ago",
"dateHoursAgo": "Hours Ago",
"dateMinutesAgo": "Minutes Ago",
"dateRightNow": "Right Now",
"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.",
"tooltipOpenSettings": "Open the settings pane",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"conversationSettings": "Conversation Settings",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"enableGroups": "Enable Group Chat",
"experimentsEnabled": "Enable Experiments",
"versionTor": "Version %1 with tor %2",
"version": "Version %1",
"builddate": "Built on: %2",
"defaultScalingText": "Default size text (scale factor:",
"smallTextLabel": "Small",
"loadingTor": "Loading tor...",
"viewGroupMembershipTooltip": "View Group Membership",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusConnecting": "Connecting to network and peers...",
"networkStatusOnline": "Online",
"newConnectionPaneTitle": "New Connection",
"localeIt": "Italiana",
"localeEs": "Espanol",
"addListItem": "Add a New List Item",
"addNewItem": "Add a new item to the list",
"todoPlaceholder": "Todo...",
"localeEs": "Espanol",
"localeIt": "Italiana",
"enableGroups": "Enable Group Chat",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"conversationSettings": "Conversation Settings",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"tooltipOpenSettings": "Open the settings pane",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"titleManageProfiles": "Manage Cwtch Profiles",
"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.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"successfullAddedContact": "Successfully added ",
"dateRightNow": "Right Now",
"dateMinutesAgo": "Minutes Ago",
"dateHoursAgo": "Hours Ago",
"dateDaysAgo": "Days Ago",
"dateWeeksAgo": "Weeks Ago",
"dateLastMonth": "Last Month",
"dateYesterday": "Yesterday",
"dateLastYear": "Last Year",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateNever": "Never",
"dateMonthsAgo": "Months Ago",
"titleManageServers": "Manage Servers",
"inviteToGroup": "You have been invited to join a group:",
"leaveGroup": "Leave This Conversation",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"yesLeave": "Yes, Leave This Conversation",
"newPassword": "New Password",
"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.",
"accepted": "Accepted!",
"rejected": "Rejected!",
"contactSuggestion": "This is a contact suggestion for: ",
"sendAnInvitation": "You sent an invitation for: ",
"torVersion": "Tor Version",
"torStatus": "Tor Status",
"resetTor": "Reset",
"cancel": "Cancel",
"sendMessage": "Send Message",
"sendInvite": "Send a contact or group invite",
"deleteProfileSuccess": "Successfully deleted profile",
"addServerFirst": "You need to add a server before you can create a group",
"nickChangeSuccess": "Profile nickname changed successfully",
"createProfileToBegin": "Please create or unlock a profile to begin",
"addContactFirst": "Add or pick a contact to begin chatting.",
"torNetworkStatus": "Tor network status",
"debugLog": "Turn on console debug logging",
"profileDeleteSuccess": "Successfully deleted profile",
"malformedMessage": "Malformed message"
"newConnectionPaneTitle": "New Connection",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connecting to network and peers...",
"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": "Default size text (scale factor:",
"builddate": "Built on: %2",
"version": "Version %1",
"versionTor": "Version %1 with tor %2",
"themeDark": "Dark",
"themeLight": "Light",
"settingTheme": "Theme",
"largeTextLabel": "Large",
"settingInterfaceZoom": "Zoom level",
"localeDe": "Deutsche",
"localePt": "Portuguesa",
"localeFr": "Frances",
"localeEn": "English",
"settingLanguage": "Language",
"blockUnknownLabel": "Block Unknown Peers",
"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 peers 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",
"copiedToClipboardNotification": "Copied to Clipboard",
"copyBtn": "Copy",
"editProfile": "Edit Profille",
"newProfile": "New Profile",
"defaultProfileName": "Alice",
"profileName": "Display name",
"editProfileTitle": "Edit Profile",
"addProfileTitle": "Add new profile",
"deleteBtn": "Delete",
"unblockBtn": "Unblock Peer",
"dontSavePeerHistory": "Delete Peer History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.",
"savePeerHistory": "Save Peer History",
"blockBtn": "Block Peer",
"saveBtn": "Save",
"displayNameLabel": "Display Name",
"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",
"copiedClipboardNotification": "Copied to clipboard",
"peerOfflineMessage": "Peer is offline, messages can't be delivered right now",
"peerBlockedMessage": "Peer 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": "Peer 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": "Out of Sync",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
"serverConnectivityConnected": "Server Connected",
"serverInfo": "Server Information",
"invitationLabel": "Invitation",
"serverLabel": "Server",
"search": "Search...",
"cycleColoursDesktop": "Click to cycle colours.\nRight-click to reset.",
"cycleColoursAndroid": "Click to cycle colours.\nLong-press to reset.",
"cycleMorphsDesktop": "Click to cycle morphs.\nRight-click to reset.",
"cycleMorphsAndroid": "Click to cycle morphs.\nLong-press to reset.",
"cycleCatsDesktop": "Click to cycle category.\nRight-click to reset.",
"cycleCatsAndroid": "Click to cycle category.\nLong-press to reset.",
"blocked": "Blocked",
"titlePlaceholder": "title...",
"postNewBulletinLabel": "Post new bulletin",
"newBulletinLabel": "New Bulletin",
"joinGroup": "Join group",
"createGroup": "Create group",
"addPeer": "Add Peer",
"groupAddr": "Address",
"invitation": "Invitation",
"server": "Server",
"groupName": "Group name",
"peerName": "Name",
"peerAddress": "Address",
"joinGroupTab": "Join a group",
"createGroupTab": "Create a group",
"addPeerTab": "Add a peer",
"createGroupBtn": "Create",
"defaultGroupName": "Awesome Group",
"createGroupTitle": "Create Group"
}

View File

@ -1,186 +1,190 @@
{
"@@locale": "es",
"@@last_modified": "2021-06-15T02:08:49+02:00",
"createGroupTitle": "Crear un grupo",
"serverLabel": "Servidor",
"groupNameLabel": "Nombre del grupo",
"defaultGroupName": "El Grupo Asombroso",
"createGroupBtn": "Crear",
"profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte",
"copyBtn": "Copiar",
"copiedToClipboardNotification": "Copiado al portapapeles",
"addPeerTab": "Agregar Contacto",
"createGroupTab": "Crear un grupo",
"joinGroupTab": "Únete a un grupo",
"peerAddress": "Dirección",
"peerName": "Nombre",
"groupName": "Nombre del grupo",
"server": "Servidor",
"invitation": "Invitación",
"groupAddr": "Dirección",
"addPeer": "Agregar Contacto",
"createGroup": "Crear perfil",
"joinGroup": "Únete al grupo",
"newBulletinLabel": "Nuevo Boletín",
"postNewBulletinLabel": "Publicar nuevo boletín",
"titlePlaceholder": "título...",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"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.",
"leaveGroup": "Leave This Conversation",
"inviteToGroup": "You have been invited to join a group:",
"pasteAddressToAddContact": "...pegar una dirección aquí para añadir contacto...",
"blocked": "Bloqueado",
"cycleCatsAndroid": "Click para cambiar categoría. Mantenga pulsado para reiniciar.",
"cycleCatsDesktop": "Click para cambiar categoría. Click derecho para reiniciar.",
"cycleMorphsAndroid": "Click para cambiar transformaciones. Mantenga pulsado para reiniciar.",
"cycleMorphsDesktop": "Click para cambiar transformaciones. Click derecho para reiniciar.",
"cycleColoursAndroid": "Click para cambiar colores. Mantenga pulsado para reiniciar.",
"cycleColoursDesktop": "Click para cambiar colores. Click derecho para reiniciar.",
"search": "Búsqueda...",
"invitationLabel": "Invitación",
"serverInfo": "Información del servidor",
"serverConnectivityConnected": "Servidor conectado",
"serverConnectivityDisconnected": "Servidor desconectado",
"serverSynced": "Sincronizado",
"serverNotSynced": "Fuera de sincronización con el servidor",
"viewServerInfo": "Información del servidor",
"saveBtn": "Guardar",
"inviteToGroupLabel": "Invitar al grupo",
"inviteBtn": "Invitar",
"deleteBtn": "Eliminar",
"update": "Actualizar",
"searchList": "Buscar en la lista",
"peerNotOnline": "Este contacto no está en línea, la aplicación no puede ser usada en este momento",
"addListItemBtn": "Agregar artículo",
"membershipDescription": "La lista a continuación solo muestra los miembros que han enviado mensajes al grupo, no incluye a todos los usuarios dentro del grupo",
"dmTooltip": "Haz clic para enviar mensaje directo",
"couldNotSendMsgError": "No se pudo enviar este mensaje",
"acknowledgedLabel": "Reconocido",
"pendingLabel": "Pendiente",
"peerBlockedMessage": "Contacto bloqueado",
"peerOfflineMessage": "Este contacto no está en línea, los mensajes no pueden ser entregados en este momento",
"copiedClipboardNotification": "Copiado al portapapeles",
"newGroupBtn": "Crear un nuevo grupo de chat",
"acceptGroupInviteLabel": "¿Quieres aceptar la invitación a ",
"acceptGroupBtn": "Aceptar",
"rejectGroupBtn": "Rechazar",
"chatBtn": "Chat",
"listsBtn": "Listas",
"bulletinsBtn": "Boletines",
"puzzleGameBtn": "Juego de rompecabezas",
"addressLabel": "Dirección",
"displayNameLabel": "Nombre de Usuario",
"blockBtn": "Bloquear contacto",
"savePeerHistory": "Guardar el historial con contacto",
"savePeerHistoryDescription": "Determina si eliminar o no el historial asociado con el contacto.",
"dontSavePeerHistory": "Eliminar historial de contacto",
"unblockBtn": "Desbloquear contacto",
"addProfileTitle": "Agregar nuevo perfil",
"editProfileTitle": "Editar perfil",
"profileName": "Nombre de Usuario",
"defaultProfileName": "Alicia",
"newProfile": "Nuevo perfil",
"editProfile": "Editar perfil",
"radioUsePassword": "Contraseña",
"radioNoPassword": "Sin cifrado (sin contraseña)",
"noPasswordWarning": "No usar una contraseña para esta cuenta significa que los datos almacenados localmente no serán encriptados",
"yourDisplayName": "Tu nombre de usuario",
"currentPasswordLabel": "Contraseña actual",
"password1Label": "Contraseña",
"password2Label": "Vuelve a ingresar tu contraseña",
"passwordErrorEmpty": "El campo de contraseña no puede estar vacío",
"createProfileBtn": "Crear perfil",
"saveProfileBtn": "Guardar perfil",
"passwordErrorMatch": "Las contraseñas no coinciden",
"passwordChangeError": "Hubo un error cambiando tu contraseña: la contraseña ingresada fue rechazada",
"deleteProfileBtn": "Eliminar Perfil",
"deleteConfirmLabel": "Escribe ELIMINAR para confirmar",
"deleteProfileConfirmBtn": "Confirmar eliminar perfil",
"deleteConfirmText": "ELIMINAR",
"addNewProfileBtn": "Agregar nuevo perfil",
"enterProfilePassword": "Ingresa tu contraseña para ver tus perfiles",
"password": "Contraseña",
"error0ProfilesLoadedForPassword": "0 perfiles cargados con esa contraseña",
"yourProfiles": "Tus perfiles",
"yourServers": "Tus servidores",
"unlock": "Desbloquear",
"cwtchSettingsTitle": "Configuración de Cwtch",
"versionBuilddate": "Versión: %1 Basado en %2",
"zoomLabel": "Zoom de la interfaz (afecta principalmente el tamaño del texto y de los botones)",
"blockUnknownLabel": "Bloquear conexiones desconocidas",
"settingLanguage": "Idioma",
"localeEn": "Inglés",
"localeFr": "Francés",
"localePt": "Portugués",
"localeDe": "Alemán",
"settingInterfaceZoom": "Nivel de zoom",
"largeTextLabel": "Grande",
"settingTheme": "Tema",
"themeLight": "Claro",
"themeDark": "Oscuro",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"titleManageServers": "Manage Servers",
"dateMonthsAgo": "Months Ago",
"dateNever": "Never",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateLastYear": "Last Year",
"dateYesterday": "Yesterday",
"dateLastMonth": "Last Month",
"dateWeeksAgo": "Weeks Ago",
"dateDaysAgo": "Days Ago",
"dateHoursAgo": "Hours Ago",
"dateMinutesAgo": "Minutes Ago",
"dateRightNow": "Right Now",
"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.",
"tooltipOpenSettings": "Open the settings pane",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"conversationSettings": "Conversation Settings",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"enableGroups": "Enable Group Chat",
"experimentsEnabled": "Experimentos habilitados",
"versionTor": "Versión %1 con tor %2",
"version": "Versión %1",
"builddate": "Basado en: %2",
"defaultScalingText": "Tamaño predeterminado de texto (factor de escala:",
"smallTextLabel": "Pequeño",
"loadingTor": "Cargando tor...",
"viewGroupMembershipTooltip": "Ver membresía del grupo",
"networkStatusDisconnected": "Sin conexión, comprueba tu conexión",
"networkStatusAttemptingTor": "Intentando conectarse a la red Tor",
"networkStatusConnecting": "Conectando a la red y a los contactos...",
"networkStatusOnline": "En línea",
"newConnectionPaneTitle": "Nueva conexión",
"localeIt": "Italiano",
"localeEs": "Español",
"addListItem": "Añadir un nuevo elemento a la lista",
"addNewItem": "Añadir un nuevo elemento a la lista",
"todoPlaceholder": "Por hacer...",
"localeEs": "Español",
"localeIt": "Italiano",
"enableGroups": "Enable Group Chat",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"conversationSettings": "Conversation Settings",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"tooltipOpenSettings": "Open the settings pane",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"titleManageProfiles": "Manage Cwtch Profiles",
"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.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"successfullAddedContact": "Successfully added ",
"dateRightNow": "Right Now",
"dateMinutesAgo": "Minutes Ago",
"dateHoursAgo": "Hours Ago",
"dateDaysAgo": "Days Ago",
"dateWeeksAgo": "Weeks Ago",
"dateLastMonth": "Last Month",
"dateYesterday": "Yesterday",
"dateLastYear": "Last Year",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateNever": "Never",
"dateMonthsAgo": "Months Ago",
"titleManageServers": "Manage Servers",
"inviteToGroup": "You have been invited to join a group:",
"leaveGroup": "Leave This Conversation",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"yesLeave": "Yes, Leave This Conversation",
"newPassword": "New Password",
"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.",
"accepted": "Accepted!",
"rejected": "Rejected!",
"contactSuggestion": "This is a contact suggestion for: ",
"sendAnInvitation": "You sent an invitation for: ",
"torVersion": "Tor Version",
"torStatus": "Tor Status",
"resetTor": "Reset",
"cancel": "Cancel",
"sendMessage": "Send Message",
"sendInvite": "Send a contact or group invite",
"deleteProfileSuccess": "Successfully deleted profile",
"addServerFirst": "You need to add a server before you can create a group",
"nickChangeSuccess": "Profile nickname changed successfully",
"createProfileToBegin": "Please create or unlock a profile to begin",
"addContactFirst": "Add or pick a contact to begin chatting.",
"torNetworkStatus": "Tor network status",
"debugLog": "Turn on console debug logging",
"profileDeleteSuccess": "Successfully deleted profile",
"malformedMessage": "Malformed message"
"newConnectionPaneTitle": "Nueva conexión",
"networkStatusOnline": "En línea",
"networkStatusConnecting": "Conectando a la red y a los contactos...",
"networkStatusAttemptingTor": "Intentando conectarse a la red Tor",
"networkStatusDisconnected": "Sin conexión, comprueba tu conexión",
"viewGroupMembershipTooltip": "Ver membresía del grupo",
"loadingTor": "Cargando tor...",
"smallTextLabel": "Pequeño",
"defaultScalingText": "Tamaño predeterminado de texto (factor de escala:",
"builddate": "Basado en: %2",
"version": "Versión %1",
"versionTor": "Versión %1 con tor %2",
"themeDark": "Oscuro",
"themeLight": "Claro",
"settingTheme": "Tema",
"largeTextLabel": "Grande",
"settingInterfaceZoom": "Nivel de zoom",
"localeDe": "Alemán",
"localePt": "Portugués",
"localeFr": "Francés",
"localeEn": "Inglés",
"settingLanguage": "Idioma",
"blockUnknownLabel": "Bloquear conexiones desconocidas",
"zoomLabel": "Zoom de la interfaz (afecta principalmente el tamaño del texto y de los botones)",
"versionBuilddate": "Versión: %1 Basado en %2",
"cwtchSettingsTitle": "Configuración de Cwtch",
"unlock": "Desbloquear",
"yourServers": "Tus servidores",
"yourProfiles": "Tus perfiles",
"error0ProfilesLoadedForPassword": "0 perfiles cargados con esa contraseña",
"password": "Contraseña",
"enterProfilePassword": "Ingresa tu contraseña para ver tus perfiles",
"addNewProfileBtn": "Agregar nuevo perfil",
"deleteConfirmText": "ELIMINAR",
"deleteProfileConfirmBtn": "Confirmar eliminar perfil",
"deleteConfirmLabel": "Escribe ELIMINAR para confirmar",
"deleteProfileBtn": "Eliminar Perfil",
"passwordChangeError": "Hubo un error cambiando tu contraseña: la contraseña ingresada fue rechazada",
"passwordErrorMatch": "Las contraseñas no coinciden",
"saveProfileBtn": "Guardar perfil",
"createProfileBtn": "Crear perfil",
"passwordErrorEmpty": "El campo de contraseña no puede estar vacío",
"password2Label": "Vuelve a ingresar tu contraseña",
"password1Label": "Contraseña",
"currentPasswordLabel": "Contraseña actual",
"yourDisplayName": "Tu nombre de usuario",
"profileOnionLabel": "Envía esta dirección a los contactos con los que quieras conectarte",
"noPasswordWarning": "No usar una contraseña para esta cuenta significa que los datos almacenados localmente no serán encriptados",
"radioNoPassword": "Sin cifrado (sin contraseña)",
"radioUsePassword": "Contraseña",
"copiedToClipboardNotification": "Copiado al portapapeles",
"copyBtn": "Copiar",
"editProfile": "Editar perfil",
"newProfile": "Nuevo perfil",
"defaultProfileName": "Alicia",
"profileName": "Nombre de Usuario",
"editProfileTitle": "Editar perfil",
"addProfileTitle": "Agregar nuevo perfil",
"deleteBtn": "Eliminar",
"unblockBtn": "Desbloquear contacto",
"dontSavePeerHistory": "Eliminar historial de contacto",
"savePeerHistoryDescription": "Determina si eliminar o no el historial asociado con el contacto.",
"savePeerHistory": "Guardar el historial con contacto",
"blockBtn": "Bloquear contacto",
"saveBtn": "Guardar",
"displayNameLabel": "Nombre de Usuario",
"addressLabel": "Dirección",
"puzzleGameBtn": "Juego de rompecabezas",
"bulletinsBtn": "Boletines",
"listsBtn": "Listas",
"chatBtn": "Chat",
"rejectGroupBtn": "Rechazar",
"acceptGroupBtn": "Aceptar",
"acceptGroupInviteLabel": "¿Quieres aceptar la invitación a ",
"newGroupBtn": "Crear un nuevo grupo de chat",
"copiedClipboardNotification": "Copiado al portapapeles",
"peerOfflineMessage": "Este contacto no está en línea, los mensajes no pueden ser entregados en este momento",
"peerBlockedMessage": "Contacto bloqueado",
"pendingLabel": "Pendiente",
"acknowledgedLabel": "Reconocido",
"couldNotSendMsgError": "No se pudo enviar este mensaje",
"dmTooltip": "Haz clic para enviar mensaje directo",
"membershipDescription": "La lista a continuación solo muestra los miembros que han enviado mensajes al grupo, no incluye a todos los usuarios dentro del grupo",
"addListItemBtn": "Agregar artículo",
"peerNotOnline": "Este contacto no está en línea, la aplicación no puede ser usada en este momento",
"searchList": "Buscar en la lista",
"update": "Actualizar",
"inviteBtn": "Invitar",
"inviteToGroupLabel": "Invitar al grupo",
"groupNameLabel": "Nombre del grupo",
"viewServerInfo": "Información del servidor",
"serverNotSynced": "Fuera de sincronización con el servidor",
"serverSynced": "Sincronizado",
"serverConnectivityDisconnected": "Servidor desconectado",
"serverConnectivityConnected": "Servidor conectado",
"serverInfo": "Información del servidor",
"invitationLabel": "Invitación",
"serverLabel": "Servidor",
"search": "Búsqueda...",
"cycleColoursDesktop": "Click para cambiar colores. Click derecho para reiniciar.",
"cycleColoursAndroid": "Click para cambiar colores. Mantenga pulsado para reiniciar.",
"cycleMorphsDesktop": "Click para cambiar transformaciones. Click derecho para reiniciar.",
"cycleMorphsAndroid": "Click para cambiar transformaciones. Mantenga pulsado para reiniciar.",
"cycleCatsDesktop": "Click para cambiar categoría. Click derecho para reiniciar.",
"cycleCatsAndroid": "Click para cambiar categoría. Mantenga pulsado para reiniciar.",
"blocked": "Bloqueado",
"titlePlaceholder": "título...",
"postNewBulletinLabel": "Publicar nuevo boletín",
"newBulletinLabel": "Nuevo Boletín",
"joinGroup": "Únete al grupo",
"createGroup": "Crear perfil",
"addPeer": "Agregar Contacto",
"groupAddr": "Dirección",
"invitation": "Invitación",
"server": "Servidor",
"groupName": "Nombre del grupo",
"peerName": "Nombre",
"peerAddress": "Dirección",
"joinGroupTab": "Únete a un grupo",
"createGroupTab": "Crear un grupo",
"addPeerTab": "Agregar Contacto",
"createGroupBtn": "Crear",
"defaultGroupName": "El Grupo Asombroso",
"createGroupTitle": "Crear un grupo"
}

View File

@ -1,186 +1,190 @@
{
"@@locale": "fr",
"@@last_modified": "2021-06-15T02:08:49+02:00",
"createGroupTitle": "Créer un groupe",
"serverLabel": "Serveur",
"groupNameLabel": "Nom du groupe",
"defaultGroupName": "Un super groupe",
"createGroupBtn": "Créer",
"profileOnionLabel": "Send this address to peers you want to connect with",
"copyBtn": "Copier",
"copiedToClipboardNotification": "Copié dans le presse-papier",
"addPeerTab": "Add a peer",
"createGroupTab": "Create a group",
"joinGroupTab": "Join a group",
"peerAddress": "Address",
"peerName": "Name",
"groupName": "Group name",
"server": "Server",
"invitation": "Invitation",
"groupAddr": "Address",
"addPeer": "Add Peer",
"createGroup": "Create group",
"joinGroup": "Join group",
"newBulletinLabel": "Nouveau bulletin",
"postNewBulletinLabel": "Envoyer un nouveau bulletin",
"titlePlaceholder": "titre...",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"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.",
"leaveGroup": "Leave This Conversation",
"inviteToGroup": "You have been invited to join a group:",
"pasteAddressToAddContact": "... coller une adresse ici pour ajouter un contact...",
"blocked": "Blocked",
"cycleCatsAndroid": "Click to cycle category.\nLong-press to reset.",
"cycleCatsDesktop": "Click to cycle category.\nRight-click to reset.",
"cycleMorphsAndroid": "Click to cycle morphs.\nLong-press to reset.",
"cycleMorphsDesktop": "Click to cycle morphs.\nRight-click to reset.",
"cycleColoursAndroid": "Click to cycle colours.\nLong-press to reset.",
"cycleColoursDesktop": "Click to cycle colours.\nRight-click to reset.",
"search": "Search...",
"invitationLabel": "Invitation",
"serverInfo": "Server Information",
"serverConnectivityConnected": "Server Connected",
"serverConnectivityDisconnected": "Server Disconnected",
"serverSynced": "Synced",
"serverNotSynced": "Out of Sync",
"viewServerInfo": "Server Info",
"saveBtn": "Sauvegarder",
"inviteToGroupLabel": "Inviter quelqu'un",
"inviteBtn": "Invitation",
"deleteBtn": "Effacer",
"update": "Update",
"searchList": "Search List",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"addListItemBtn": "Add Item",
"membershipDescription": "Liste des utilisateurs ayant envoyés un ou plusieurs messages au groupe. Cette liste peut ne pas être representatives de l'ensemble des membres du groupe.",
"dmTooltip": "Envoyer un message privé",
"couldNotSendMsgError": "Impossible d'envoyer ce message",
"acknowledgedLabel": "Confirmé",
"pendingLabel": "En attente",
"peerBlockedMessage": "Peer is blocked",
"peerOfflineMessage": "Peer is offline, messages can't be delivered right now",
"copiedClipboardNotification": "Copié dans le presse-papier",
"newGroupBtn": "Créer un nouveau groupe",
"acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe",
"acceptGroupBtn": "Accepter",
"rejectGroupBtn": "Refuser",
"chatBtn": "Discuter",
"listsBtn": "Listes",
"bulletinsBtn": "Bulletins",
"puzzleGameBtn": "Puzzle",
"addressLabel": "Adresse",
"displayNameLabel": "Pseudo",
"blockBtn": "Block Peer",
"savePeerHistory": "Save Peer History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.",
"dontSavePeerHistory": "Delete Peer History",
"unblockBtn": "Unblock Peer",
"addProfileTitle": "Add new profile",
"editProfileTitle": "Edit Profile",
"profileName": "Display name",
"defaultProfileName": "Alice",
"newProfile": "New Profile",
"editProfile": "Edit Profille",
"radioUsePassword": "Password",
"radioNoPassword": "Unencrypted (No password)",
"noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted",
"yourDisplayName": "Your Display Name",
"currentPasswordLabel": "Current Password",
"password1Label": "Password",
"password2Label": "Reenter password",
"passwordErrorEmpty": "Password cannot be empty",
"createProfileBtn": "Create Profile",
"saveProfileBtn": "Save Profile",
"passwordErrorMatch": "Passwords do not match",
"passwordChangeError": "Error changing password: Supplied password rejected",
"deleteProfileBtn": "Delete Profile",
"deleteConfirmLabel": "Type DELETE to confirm",
"deleteProfileConfirmBtn": "Really Delete Profile",
"deleteConfirmText": "DELETE",
"addNewProfileBtn": "Add new profile",
"enterProfilePassword": "Enter a password to view your profiles",
"password": "Password",
"error0ProfilesLoadedForPassword": "0 profiles loaded with that password",
"yourProfiles": "Your Profiles",
"yourServers": "Your Servers",
"unlock": "Unlock",
"cwtchSettingsTitle": "Préférences Cwtch",
"versionBuilddate": "Version: %1 Built on: %2",
"zoomLabel": "Interface zoom (essentiellement la taille du texte et des composants de l'interface)",
"blockUnknownLabel": "Block Unknown Peers",
"settingLanguage": "Language",
"localeEn": "English",
"localeFr": "Frances",
"localePt": "Portuguesa",
"localeDe": "Deutsche",
"settingInterfaceZoom": "Zoom level",
"largeTextLabel": "Large",
"settingTheme": "Theme",
"themeLight": "Light",
"themeDark": "Dark",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"titleManageServers": "Manage Servers",
"dateMonthsAgo": "Months Ago",
"dateNever": "Never",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateLastYear": "Last Year",
"dateYesterday": "Yesterday",
"dateLastMonth": "Last Month",
"dateWeeksAgo": "Weeks Ago",
"dateDaysAgo": "Days Ago",
"dateHoursAgo": "Hours Ago",
"dateMinutesAgo": "Minutes Ago",
"dateRightNow": "Right Now",
"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.",
"tooltipOpenSettings": "Open the settings pane",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"conversationSettings": "Conversation Settings",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"enableGroups": "Enable Group Chat",
"experimentsEnabled": "Enable Experiments",
"versionTor": "Version %1 with tor %2",
"version": "Version %1",
"builddate": "Built on: %2",
"defaultScalingText": "Taille par défaut du texte (échelle:",
"smallTextLabel": "Petit",
"loadingTor": "Loading tor...",
"viewGroupMembershipTooltip": "View Group Membership",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusConnecting": "Connecting to network and peers...",
"networkStatusOnline": "Online",
"newConnectionPaneTitle": "New Connection",
"localeIt": "Italiana",
"localeEs": "Espanol",
"addListItem": "Ajouter un nouvel élément",
"addNewItem": "Ajouter un nouvel élément à la liste",
"todoPlaceholder": "A faire...",
"localeEs": "Espanol",
"localeIt": "Italiana",
"enableGroups": "Enable Group Chat",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"conversationSettings": "Conversation Settings",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"tooltipOpenSettings": "Open the settings pane",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"titleManageProfiles": "Manage Cwtch Profiles",
"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.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"successfullAddedContact": "Successfully added ",
"dateRightNow": "Right Now",
"dateMinutesAgo": "Minutes Ago",
"dateHoursAgo": "Hours Ago",
"dateDaysAgo": "Days Ago",
"dateWeeksAgo": "Weeks Ago",
"dateLastMonth": "Last Month",
"dateYesterday": "Yesterday",
"dateLastYear": "Last Year",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateNever": "Never",
"dateMonthsAgo": "Months Ago",
"titleManageServers": "Manage Servers",
"inviteToGroup": "You have been invited to join a group:",
"leaveGroup": "Leave This Conversation",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"yesLeave": "Yes, Leave This Conversation",
"newPassword": "New Password",
"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.",
"accepted": "Accepted!",
"rejected": "Rejected!",
"contactSuggestion": "This is a contact suggestion for: ",
"sendAnInvitation": "You sent an invitation for: ",
"torVersion": "Tor Version",
"torStatus": "Tor Status",
"resetTor": "Reset",
"cancel": "Cancel",
"sendMessage": "Send Message",
"sendInvite": "Send a contact or group invite",
"deleteProfileSuccess": "Successfully deleted profile",
"addServerFirst": "You need to add a server before you can create a group",
"nickChangeSuccess": "Profile nickname changed successfully",
"createProfileToBegin": "Please create or unlock a profile to begin",
"addContactFirst": "Add or pick a contact to begin chatting.",
"torNetworkStatus": "Tor network status",
"debugLog": "Turn on console debug logging",
"profileDeleteSuccess": "Successfully deleted profile",
"malformedMessage": "Malformed message"
"newConnectionPaneTitle": "New Connection",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connecting to network and peers...",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"viewGroupMembershipTooltip": "View Group Membership",
"loadingTor": "Loading tor...",
"smallTextLabel": "Petit",
"defaultScalingText": "Taille par défaut du texte (échelle:",
"builddate": "Built on: %2",
"version": "Version %1",
"versionTor": "Version %1 with tor %2",
"themeDark": "Dark",
"themeLight": "Light",
"settingTheme": "Theme",
"largeTextLabel": "Large",
"settingInterfaceZoom": "Zoom level",
"localeDe": "Deutsche",
"localePt": "Portuguesa",
"localeFr": "Frances",
"localeEn": "English",
"settingLanguage": "Language",
"blockUnknownLabel": "Block Unknown Peers",
"zoomLabel": "Interface zoom (essentiellement la taille du texte et des composants de l'interface)",
"versionBuilddate": "Version: %1 Built on: %2",
"cwtchSettingsTitle": "Préférences Cwtch",
"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 peers 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",
"copiedToClipboardNotification": "Copié dans le presse-papier",
"copyBtn": "Copier",
"editProfile": "Edit Profille",
"newProfile": "New Profile",
"defaultProfileName": "Alice",
"profileName": "Display name",
"editProfileTitle": "Edit Profile",
"addProfileTitle": "Add new profile",
"deleteBtn": "Effacer",
"unblockBtn": "Unblock Peer",
"dontSavePeerHistory": "Delete Peer History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.",
"savePeerHistory": "Save Peer History",
"blockBtn": "Block Peer",
"saveBtn": "Sauvegarder",
"displayNameLabel": "Pseudo",
"addressLabel": "Adresse",
"puzzleGameBtn": "Puzzle",
"bulletinsBtn": "Bulletins",
"listsBtn": "Listes",
"chatBtn": "Discuter",
"rejectGroupBtn": "Refuser",
"acceptGroupBtn": "Accepter",
"acceptGroupInviteLabel": "Voulez-vous accepter l'invitation au groupe",
"newGroupBtn": "Créer un nouveau groupe",
"copiedClipboardNotification": "Copié dans le presse-papier",
"peerOfflineMessage": "Peer is offline, messages can't be delivered right now",
"peerBlockedMessage": "Peer is blocked",
"pendingLabel": "En attente",
"acknowledgedLabel": "Confirmé",
"couldNotSendMsgError": "Impossible d'envoyer ce message",
"dmTooltip": "Envoyer un message privé",
"membershipDescription": "Liste des utilisateurs ayant envoyés un ou plusieurs messages au groupe. Cette liste peut ne pas être representatives de l'ensemble des membres du groupe.",
"addListItemBtn": "Add Item",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"searchList": "Search List",
"update": "Update",
"inviteBtn": "Invitation",
"inviteToGroupLabel": "Inviter quelqu'un",
"groupNameLabel": "Nom du groupe",
"viewServerInfo": "Server Info",
"serverNotSynced": "Out of Sync",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
"serverConnectivityConnected": "Server Connected",
"serverInfo": "Server Information",
"invitationLabel": "Invitation",
"serverLabel": "Serveur",
"search": "Search...",
"cycleColoursDesktop": "Click to cycle colours.\nRight-click to reset.",
"cycleColoursAndroid": "Click to cycle colours.\nLong-press to reset.",
"cycleMorphsDesktop": "Click to cycle morphs.\nRight-click to reset.",
"cycleMorphsAndroid": "Click to cycle morphs.\nLong-press to reset.",
"cycleCatsDesktop": "Click to cycle category.\nRight-click to reset.",
"cycleCatsAndroid": "Click to cycle category.\nLong-press to reset.",
"blocked": "Blocked",
"titlePlaceholder": "titre...",
"postNewBulletinLabel": "Envoyer un nouveau bulletin",
"newBulletinLabel": "Nouveau bulletin",
"joinGroup": "Join group",
"createGroup": "Create group",
"addPeer": "Add Peer",
"groupAddr": "Address",
"invitation": "Invitation",
"server": "Server",
"groupName": "Group name",
"peerName": "Name",
"peerAddress": "Address",
"joinGroupTab": "Join a group",
"createGroupTab": "Create a group",
"addPeerTab": "Add a peer",
"createGroupBtn": "Créer",
"defaultGroupName": "Un super groupe",
"createGroupTitle": "Créer un groupe"
}

View File

@ -1,186 +1,190 @@
{
"@@locale": "it",
"@@last_modified": "2021-06-15T02:08:49+02:00",
"createGroupTitle": "Crea un gruppo",
"serverLabel": "Server",
"groupNameLabel": "Nome del gruppo",
"defaultGroupName": "Gruppo fantastico",
"createGroupBtn": "Crea",
"profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi",
"copyBtn": "Copia",
"copiedToClipboardNotification": "Copiato negli appunti",
"addPeerTab": "Aggiungi un peer",
"createGroupTab": "Crea un gruppo",
"joinGroupTab": "Unisciti a un gruppo",
"peerAddress": "Indirizzo",
"peerName": "Nome",
"groupName": "Nome del gruppo",
"server": "Server",
"invitation": "Invito",
"groupAddr": "Indirizzo",
"addPeer": "Aggiungi peer",
"createGroup": "Crea un gruppo",
"joinGroup": "Unisciti al gruppo",
"newBulletinLabel": "Nuovo bollettino",
"postNewBulletinLabel": "Pubblica un nuovo bollettino",
"titlePlaceholder": "titolo...",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"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.",
"leaveGroup": "Leave This Conversation",
"inviteToGroup": "You have been invited to join a group:",
"pasteAddressToAddContact": "... incolla qui un indirizzo per aggiungere un contatto...",
"blocked": "Bloccato",
"cycleCatsAndroid": "Fare clic per scorrere le categorie.\nPressione lunga per resettare.",
"cycleCatsDesktop": "Fare clic per scorrere le categorie.\nCliccare con il tasto destro per resettare.",
"cycleMorphsAndroid": "Fare clic per scorrere i morph.\nPressione lunga per resettare.",
"cycleMorphsDesktop": "Fare clic per scorrere i morph.\nCliccare con il tasto destro per resettare.",
"cycleColoursAndroid": "Fare clic per scorrere i colori.\nPressione lunga per resettare.",
"cycleColoursDesktop": "Fare clic per scorrere i colori.\nCliccare con il tasto destro per resettare.",
"search": "Ricerca...",
"invitationLabel": "Invito",
"serverInfo": "Informazioni sul server",
"serverConnectivityConnected": "Server connesso",
"serverConnectivityDisconnected": "Server disconnesso",
"serverSynced": "Sincronizzato",
"serverNotSynced": "Non sincronizzato",
"viewServerInfo": "Informazioni sul server",
"saveBtn": "Salva",
"inviteToGroupLabel": "Invitare nel gruppo",
"inviteBtn": "Invitare",
"deleteBtn": "Elimina",
"update": "Aggiornamento",
"searchList": "Cerca nella lista",
"peerNotOnline": "Il peer è offline. Le applicazioni non possono essere utilizzate in questo momento.",
"addListItemBtn": "Aggiungi elemento",
"membershipDescription": "Di seguito è riportato un elenco di utenti che hanno inviato messaggi al gruppo. Questo elenco potrebbe non corrispondere a tutti gli utenti che hanno accesso al gruppo.",
"dmTooltip": "Clicca per inviare un Messagio Diretto",
"couldNotSendMsgError": "Impossibile inviare questo messaggio",
"acknowledgedLabel": "Riconosciuto",
"pendingLabel": "In corso",
"peerBlockedMessage": "Il peer è bloccato",
"peerOfflineMessage": "Il peer è offline, i messaggi non possono essere recapitati in questo momento",
"copiedClipboardNotification": "Copiato negli Appunti",
"newGroupBtn": "Crea un nuovo gruppo",
"acceptGroupInviteLabel": "Vuoi accettare l'invito a",
"acceptGroupBtn": "Accetta",
"rejectGroupBtn": "Rifiuta",
"chatBtn": "Chat",
"listsBtn": "Liste",
"bulletinsBtn": "Bollettini",
"puzzleGameBtn": "Gioco di puzzle",
"addressLabel": "Indirizzo",
"displayNameLabel": "Nome visualizzato",
"blockBtn": "Blocca il peer",
"savePeerHistory": "Salva cronologia peer",
"savePeerHistoryDescription": "Determina se eliminare o meno ogni cronologia eventualmente associata al peer.",
"dontSavePeerHistory": "Elimina cronologia dei peer",
"unblockBtn": "Sblocca il peer",
"addProfileTitle": "Aggiungi nuovo profilo",
"editProfileTitle": "Modifica profilo",
"profileName": "Nome visualizzato",
"defaultProfileName": "Alice",
"newProfile": "Nuovo profilo",
"editProfile": "Modifica profilo",
"radioUsePassword": "Password",
"radioNoPassword": "Non criptato (senza password)",
"noPasswordWarning": "Non utilizzare una password su questo account significa che tutti i dati archiviati localmente non verranno criptati",
"yourDisplayName": "Il tuo nome visualizzato",
"currentPasswordLabel": "Password corrente",
"password1Label": "Password",
"password2Label": "Reinserire la password",
"passwordErrorEmpty": "La password non può essere vuota",
"createProfileBtn": "Crea un profilo",
"saveProfileBtn": "Salva il profilo",
"passwordErrorMatch": "Le password non corrispondono",
"passwordChangeError": "Errore durante la modifica della password: password fornita rifiutata",
"deleteProfileBtn": "Elimina profilo",
"deleteConfirmLabel": "Digita ELIMINA per confermare",
"deleteProfileConfirmBtn": "Elimina realmente il profilo",
"deleteConfirmText": "ELIMINA",
"addNewProfileBtn": "Aggiungi nuovo profilo",
"enterProfilePassword": "Inserisci una password per visualizzare i tuoi profili",
"password": "Password",
"error0ProfilesLoadedForPassword": "0 profili caricati con quella password",
"yourProfiles": "I tuoi profili",
"yourServers": "I tuoi server",
"unlock": "Sblocca",
"cwtchSettingsTitle": "Impostazioni di Cwtch",
"versionBuilddate": "Versione: %1 Costruito il: %2",
"zoomLabel": "Zoom dell'interfaccia (influisce principalmente sulle dimensioni del testo e dei pulsanti)",
"blockUnknownLabel": "Blocca peer sconosciuti",
"settingLanguage": "Lingua",
"localeEn": "Inglese",
"localeFr": "Francese",
"localePt": "Portoghese",
"localeDe": "Tedesco",
"settingInterfaceZoom": "Livello di zoom",
"largeTextLabel": "Grande",
"settingTheme": "Tema",
"themeLight": "Chiaro",
"themeDark": "Scuro",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"titleManageServers": "Manage Servers",
"dateMonthsAgo": "Months Ago",
"dateNever": "Never",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateLastYear": "Last Year",
"dateYesterday": "Yesterday",
"dateLastMonth": "Last Month",
"dateWeeksAgo": "Weeks Ago",
"dateDaysAgo": "Days Ago",
"dateHoursAgo": "Hours Ago",
"dateMinutesAgo": "Minutes Ago",
"dateRightNow": "Right Now",
"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.",
"tooltipOpenSettings": "Open the settings pane",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"conversationSettings": "Conversation Settings",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"enableGroups": "Enable Group Chat",
"experimentsEnabled": "Esperimenti abilitati",
"versionTor": "Versione %1 con tor %2",
"version": "Versione %1",
"builddate": "Costruito il: %2",
"defaultScalingText": "Testo di dimensioni predefinite (fattore di scala:",
"smallTextLabel": "Piccolo",
"loadingTor": "Caricamento di tor...",
"viewGroupMembershipTooltip": "Visualizza i membri del gruppo",
"networkStatusDisconnected": "Disconnesso da Internet, controlla la tua connessione",
"networkStatusAttemptingTor": "Tentativo di connessione alla rete Tor",
"networkStatusConnecting": "Connessione alla rete e ai peer ...",
"networkStatusOnline": "Online",
"newConnectionPaneTitle": "Nuova connessione",
"localeIt": "Italiano",
"localeEs": "Spagnolo",
"addListItem": "Aggiungi un nuovo elemento alla lista",
"addNewItem": "Aggiungi un nuovo elemento alla lista",
"todoPlaceholder": "Da fare...",
"localeEs": "Spagnolo",
"localeIt": "Italiano",
"enableGroups": "Enable Group Chat",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"conversationSettings": "Conversation Settings",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"tooltipOpenSettings": "Open the settings pane",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"titleManageProfiles": "Manage Cwtch Profiles",
"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.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"successfullAddedContact": "Successfully added ",
"dateRightNow": "Right Now",
"dateMinutesAgo": "Minutes Ago",
"dateHoursAgo": "Hours Ago",
"dateDaysAgo": "Days Ago",
"dateWeeksAgo": "Weeks Ago",
"dateLastMonth": "Last Month",
"dateYesterday": "Yesterday",
"dateLastYear": "Last Year",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateNever": "Never",
"dateMonthsAgo": "Months Ago",
"titleManageServers": "Manage Servers",
"inviteToGroup": "You have been invited to join a group:",
"leaveGroup": "Leave This Conversation",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"yesLeave": "Yes, Leave This Conversation",
"newPassword": "New Password",
"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.",
"accepted": "Accepted!",
"rejected": "Rejected!",
"contactSuggestion": "This is a contact suggestion for: ",
"sendAnInvitation": "You sent an invitation for: ",
"torVersion": "Tor Version",
"torStatus": "Tor Status",
"resetTor": "Reset",
"cancel": "Cancel",
"sendMessage": "Send Message",
"sendInvite": "Send a contact or group invite",
"deleteProfileSuccess": "Successfully deleted profile",
"addServerFirst": "You need to add a server before you can create a group",
"nickChangeSuccess": "Profile nickname changed successfully",
"createProfileToBegin": "Please create or unlock a profile to begin",
"addContactFirst": "Add or pick a contact to begin chatting.",
"torNetworkStatus": "Tor network status",
"debugLog": "Turn on console debug logging",
"profileDeleteSuccess": "Successfully deleted profile",
"malformedMessage": "Malformed message"
"newConnectionPaneTitle": "Nuova connessione",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connessione alla rete e ai peer ...",
"networkStatusAttemptingTor": "Tentativo di connessione alla rete Tor",
"networkStatusDisconnected": "Disconnesso da Internet, controlla la tua connessione",
"viewGroupMembershipTooltip": "Visualizza i membri del gruppo",
"loadingTor": "Caricamento di tor...",
"smallTextLabel": "Piccolo",
"defaultScalingText": "Testo di dimensioni predefinite (fattore di scala:",
"builddate": "Costruito il: %2",
"version": "Versione %1",
"versionTor": "Versione %1 con tor %2",
"themeDark": "Scuro",
"themeLight": "Chiaro",
"settingTheme": "Tema",
"largeTextLabel": "Grande",
"settingInterfaceZoom": "Livello di zoom",
"localeDe": "Tedesco",
"localePt": "Portoghese",
"localeFr": "Francese",
"localeEn": "Inglese",
"settingLanguage": "Lingua",
"blockUnknownLabel": "Blocca peer sconosciuti",
"zoomLabel": "Zoom dell'interfaccia (influisce principalmente sulle dimensioni del testo e dei pulsanti)",
"versionBuilddate": "Versione: %1 Costruito il: %2",
"cwtchSettingsTitle": "Impostazioni di Cwtch",
"unlock": "Sblocca",
"yourServers": "I tuoi server",
"yourProfiles": "I tuoi profili",
"error0ProfilesLoadedForPassword": "0 profili caricati con quella password",
"password": "Password",
"enterProfilePassword": "Inserisci una password per visualizzare i tuoi profili",
"addNewProfileBtn": "Aggiungi nuovo profilo",
"deleteConfirmText": "ELIMINA",
"deleteProfileConfirmBtn": "Elimina realmente il profilo",
"deleteConfirmLabel": "Digita ELIMINA per confermare",
"deleteProfileBtn": "Elimina profilo",
"passwordChangeError": "Errore durante la modifica della password: password fornita rifiutata",
"passwordErrorMatch": "Le password non corrispondono",
"saveProfileBtn": "Salva il profilo",
"createProfileBtn": "Crea un profilo",
"passwordErrorEmpty": "La password non può essere vuota",
"password2Label": "Reinserire la password",
"password1Label": "Password",
"currentPasswordLabel": "Password corrente",
"yourDisplayName": "Il tuo nome visualizzato",
"profileOnionLabel": "Inviare questo indirizzo ai peer con cui si desidera connettersi",
"noPasswordWarning": "Non utilizzare una password su questo account significa che tutti i dati archiviati localmente non verranno criptati",
"radioNoPassword": "Non criptato (senza password)",
"radioUsePassword": "Password",
"copiedToClipboardNotification": "Copiato negli appunti",
"copyBtn": "Copia",
"editProfile": "Modifica profilo",
"newProfile": "Nuovo profilo",
"defaultProfileName": "Alice",
"profileName": "Nome visualizzato",
"editProfileTitle": "Modifica profilo",
"addProfileTitle": "Aggiungi nuovo profilo",
"deleteBtn": "Elimina",
"unblockBtn": "Sblocca il peer",
"dontSavePeerHistory": "Elimina cronologia dei peer",
"savePeerHistoryDescription": "Determina se eliminare o meno ogni cronologia eventualmente associata al peer.",
"savePeerHistory": "Salva cronologia peer",
"blockBtn": "Blocca il peer",
"saveBtn": "Salva",
"displayNameLabel": "Nome visualizzato",
"addressLabel": "Indirizzo",
"puzzleGameBtn": "Gioco di puzzle",
"bulletinsBtn": "Bollettini",
"listsBtn": "Liste",
"chatBtn": "Chat",
"rejectGroupBtn": "Rifiuta",
"acceptGroupBtn": "Accetta",
"acceptGroupInviteLabel": "Vuoi accettare l'invito a",
"newGroupBtn": "Crea un nuovo gruppo",
"copiedClipboardNotification": "Copiato negli Appunti",
"peerOfflineMessage": "Il peer è offline, i messaggi non possono essere recapitati in questo momento",
"peerBlockedMessage": "Il peer è bloccato",
"pendingLabel": "In corso",
"acknowledgedLabel": "Riconosciuto",
"couldNotSendMsgError": "Impossibile inviare questo messaggio",
"dmTooltip": "Clicca per inviare un Messagio Diretto",
"membershipDescription": "Di seguito è riportato un elenco di utenti che hanno inviato messaggi al gruppo. Questo elenco potrebbe non corrispondere a tutti gli utenti che hanno accesso al gruppo.",
"addListItemBtn": "Aggiungi elemento",
"peerNotOnline": "Il peer è offline. Le applicazioni non possono essere utilizzate in questo momento.",
"searchList": "Cerca nella lista",
"update": "Aggiornamento",
"inviteBtn": "Invitare",
"inviteToGroupLabel": "Invitare nel gruppo",
"groupNameLabel": "Nome del gruppo",
"viewServerInfo": "Informazioni sul server",
"serverNotSynced": "Non sincronizzato",
"serverSynced": "Sincronizzato",
"serverConnectivityDisconnected": "Server disconnesso",
"serverConnectivityConnected": "Server connesso",
"serverInfo": "Informazioni sul server",
"invitationLabel": "Invito",
"serverLabel": "Server",
"search": "Ricerca...",
"cycleColoursDesktop": "Fare clic per scorrere i colori.\nCliccare con il tasto destro per resettare.",
"cycleColoursAndroid": "Fare clic per scorrere i colori.\nPressione lunga per resettare.",
"cycleMorphsDesktop": "Fare clic per scorrere i morph.\nCliccare con il tasto destro per resettare.",
"cycleMorphsAndroid": "Fare clic per scorrere i morph.\nPressione lunga per resettare.",
"cycleCatsDesktop": "Fare clic per scorrere le categorie.\nCliccare con il tasto destro per resettare.",
"cycleCatsAndroid": "Fare clic per scorrere le categorie.\nPressione lunga per resettare.",
"blocked": "Bloccato",
"titlePlaceholder": "titolo...",
"postNewBulletinLabel": "Pubblica un nuovo bollettino",
"newBulletinLabel": "Nuovo bollettino",
"joinGroup": "Unisciti al gruppo",
"createGroup": "Crea un gruppo",
"addPeer": "Aggiungi peer",
"groupAddr": "Indirizzo",
"invitation": "Invito",
"server": "Server",
"groupName": "Nome del gruppo",
"peerName": "Nome",
"peerAddress": "Indirizzo",
"joinGroupTab": "Unisciti a un gruppo",
"createGroupTab": "Crea un gruppo",
"addPeerTab": "Aggiungi un peer",
"createGroupBtn": "Crea",
"defaultGroupName": "Gruppo fantastico",
"createGroupTitle": "Crea un gruppo"
}

View File

@ -1,186 +1,190 @@
{
"@@locale": "pt",
"@@last_modified": "2021-06-15T02:08:49+02:00",
"createGroupTitle": "Criar Grupo",
"serverLabel": "Servidor",
"groupNameLabel": "Nome do Grupo",
"defaultGroupName": "Grupo incrível",
"createGroupBtn": "Criar",
"profileOnionLabel": "Send this address to peers you want to connect with",
"copyBtn": "Copiar",
"copiedToClipboardNotification": "Copiado",
"addPeerTab": "Add a peer",
"createGroupTab": "Create a group",
"joinGroupTab": "Join a group",
"peerAddress": "Address",
"peerName": "Name",
"groupName": "Group name",
"server": "Server",
"invitation": "Invitation",
"groupAddr": "Address",
"addPeer": "Add Peer",
"createGroup": "Create group",
"joinGroup": "Join group",
"newBulletinLabel": "Novo Boletim",
"postNewBulletinLabel": "Postar novo boletim",
"titlePlaceholder": "título…",
"@@last_modified": "2021-06-16T23:15:48+02:00",
"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.",
"leaveGroup": "Leave This Conversation",
"inviteToGroup": "You have been invited to join a group:",
"pasteAddressToAddContact": "… cole um endereço aqui para adicionar um contato…",
"blocked": "Blocked",
"cycleCatsAndroid": "Click to cycle category.\nLong-press to reset.",
"cycleCatsDesktop": "Click to cycle category.\nRight-click to reset.",
"cycleMorphsAndroid": "Click to cycle morphs.\nLong-press to reset.",
"cycleMorphsDesktop": "Click to cycle morphs.\nRight-click to reset.",
"cycleColoursAndroid": "Click to cycle colours.\nLong-press to reset.",
"cycleColoursDesktop": "Click to cycle colours.\nRight-click to reset.",
"search": "Search...",
"invitationLabel": "Convite",
"serverInfo": "Server Information",
"serverConnectivityConnected": "Server Connected",
"serverConnectivityDisconnected": "Server Disconnected",
"serverSynced": "Synced",
"serverNotSynced": "Out of Sync",
"viewServerInfo": "Server Info",
"saveBtn": "Salvar",
"inviteToGroupLabel": "Convidar ao grupo",
"inviteBtn": "Convidar",
"deleteBtn": "Deletar",
"update": "Update",
"searchList": "Search List",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"addListItemBtn": "Add Item",
"membershipDescription": "A lista abaixo é de usuários que enviaram mensagens ao grupo. Essa lista pode não refletir todos os usuários que têm acesso ao grupo.",
"dmTooltip": "Clique para DM",
"couldNotSendMsgError": "Não deu para enviar esta mensagem",
"acknowledgedLabel": "Confirmada",
"pendingLabel": "Pendente",
"peerBlockedMessage": "Peer is blocked",
"peerOfflineMessage": "Peer is offline, messages can't be delivered right now",
"copiedClipboardNotification": "Copiado",
"newGroupBtn": "Criar novo grupo",
"acceptGroupInviteLabel": "Você quer aceitar o convite para",
"acceptGroupBtn": "Aceitar",
"rejectGroupBtn": "Recusar",
"chatBtn": "Chat",
"listsBtn": "Listas",
"bulletinsBtn": "Boletins",
"puzzleGameBtn": "Jogo de Adivinhação",
"addressLabel": "Endereço",
"displayNameLabel": "Nome de Exibição",
"blockBtn": "Block Peer",
"savePeerHistory": "Save Peer History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.",
"dontSavePeerHistory": "Delete Peer History",
"unblockBtn": "Unblock Peer",
"addProfileTitle": "Add new profile",
"editProfileTitle": "Edit Profile",
"profileName": "Display name",
"defaultProfileName": "Alice",
"newProfile": "New Profile",
"editProfile": "Edit Profille",
"radioUsePassword": "Password",
"radioNoPassword": "Unencrypted (No password)",
"noPasswordWarning": "Not using a password on this account means that all data stored locally will not be encrypted",
"yourDisplayName": "Your Display Name",
"currentPasswordLabel": "Current Password",
"password1Label": "Password",
"password2Label": "Reenter password",
"passwordErrorEmpty": "Password cannot be empty",
"createProfileBtn": "Create Profile",
"saveProfileBtn": "Save Profile",
"passwordErrorMatch": "Passwords do not match",
"passwordChangeError": "Error changing password: Supplied password rejected",
"deleteProfileBtn": "Delete Profile",
"deleteConfirmLabel": "Type DELETE to confirm",
"deleteProfileConfirmBtn": "Really Delete Profile",
"deleteConfirmText": "DELETE",
"addNewProfileBtn": "Add new profile",
"enterProfilePassword": "Enter a password to view your profiles",
"password": "Password",
"error0ProfilesLoadedForPassword": "0 profiles loaded with that password",
"yourProfiles": "Your Profiles",
"yourServers": "Your Servers",
"unlock": "Unlock",
"cwtchSettingsTitle": "Configurações do Cwtch",
"versionBuilddate": "Version: %1 Built on: %2",
"zoomLabel": "Zoom da interface (afeta principalmente tamanho de texto e botões)",
"blockUnknownLabel": "Block Unknown Peers",
"settingLanguage": "Language",
"localeEn": "English",
"localeFr": "Frances",
"localePt": "Portuguesa",
"localeDe": "Deutsche",
"settingInterfaceZoom": "Zoom level",
"largeTextLabel": "Grande",
"settingTheme": "Theme",
"themeLight": "Light",
"themeDark": "Dark",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"titleManageServers": "Manage Servers",
"dateMonthsAgo": "Months Ago",
"dateNever": "Never",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateLastYear": "Last Year",
"dateYesterday": "Yesterday",
"dateLastMonth": "Last Month",
"dateWeeksAgo": "Weeks Ago",
"dateDaysAgo": "Days Ago",
"dateHoursAgo": "Hours Ago",
"dateMinutesAgo": "Minutes Ago",
"dateRightNow": "Right Now",
"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.",
"tooltipOpenSettings": "Open the settings pane",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"conversationSettings": "Conversation Settings",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"enableGroups": "Enable Group Chat",
"experimentsEnabled": "Enable Experiments",
"versionTor": "Version %1 with tor %2",
"version": "Version %1",
"builddate": "Built on: %2",
"defaultScalingText": "Texto tamanho padrão (fator de escala: ",
"smallTextLabel": "Pequeno",
"loadingTor": "Loading tor...",
"viewGroupMembershipTooltip": "View Group Membership",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusConnecting": "Connecting to network and peers...",
"networkStatusOnline": "Online",
"newConnectionPaneTitle": "New Connection",
"localeIt": "Italiana",
"localeEs": "Espanol",
"addListItem": "Adicionar Item à Lista",
"addNewItem": "Adicionar novo item à lista",
"todoPlaceholder": "Afazer…",
"localeEs": "Espanol",
"localeIt": "Italiana",
"enableGroups": "Enable Group Chat",
"enterCurrentPasswordForDelete": "Please enter current password to delete this profile.",
"conversationSettings": "Conversation Settings",
"invalidImportString": "Invalid import string",
"contactAlreadyExists": "Contact Already Exists",
"tooltipOpenSettings": "Open the settings pane",
"tooltipAddContact": "Add a new contact or conversation",
"titleManageContacts": "Conversations",
"tooltipUnlockProfiles": "Unlock encrypted profiles by entering their password.",
"titleManageProfiles": "Manage Cwtch Profiles",
"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.",
"descriptionExperimentsGroups": "The group experiment allows Cwtch to connect with untrusted server infrastructure to facilitate communication with more than one contact.",
"descriptionBlockUnknownConnections": "If turned on, this option will automatically close connections from Cwtch users that have not been added to your contact list.",
"successfullAddedContact": "Successfully added ",
"dateRightNow": "Right Now",
"dateMinutesAgo": "Minutes Ago",
"dateHoursAgo": "Hours Ago",
"dateDaysAgo": "Days Ago",
"dateWeeksAgo": "Weeks Ago",
"dateLastMonth": "Last Month",
"dateYesterday": "Yesterday",
"dateLastYear": "Last Year",
"dateYearsAgo": "X Years Ago (displayed next to a contact row to indicate time of last action)",
"dateNever": "Never",
"dateMonthsAgo": "Months Ago",
"titleManageServers": "Manage Servers",
"inviteToGroup": "You have been invited to join a group:",
"leaveGroup": "Leave This Conversation",
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
"yesLeave": "Yes, Leave This Conversation",
"newPassword": "New Password",
"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.",
"accepted": "Accepted!",
"rejected": "Rejected!",
"contactSuggestion": "This is a contact suggestion for: ",
"sendAnInvitation": "You sent an invitation for: ",
"torVersion": "Tor Version",
"torStatus": "Tor Status",
"resetTor": "Reset",
"cancel": "Cancel",
"sendMessage": "Send Message",
"sendInvite": "Send a contact or group invite",
"deleteProfileSuccess": "Successfully deleted profile",
"addServerFirst": "You need to add a server before you can create a group",
"nickChangeSuccess": "Profile nickname changed successfully",
"createProfileToBegin": "Please create or unlock a profile to begin",
"addContactFirst": "Add or pick a contact to begin chatting.",
"torNetworkStatus": "Tor network status",
"debugLog": "Turn on console debug logging",
"profileDeleteSuccess": "Successfully deleted profile",
"malformedMessage": "Malformed message"
"newConnectionPaneTitle": "New Connection",
"networkStatusOnline": "Online",
"networkStatusConnecting": "Connecting to network and peers...",
"networkStatusAttemptingTor": "Attempting to connect to Tor network",
"networkStatusDisconnected": "Disconnected from the internet, check your connection",
"viewGroupMembershipTooltip": "View Group Membership",
"loadingTor": "Loading tor...",
"smallTextLabel": "Pequeno",
"defaultScalingText": "Texto tamanho padrão (fator de escala: ",
"builddate": "Built on: %2",
"version": "Version %1",
"versionTor": "Version %1 with tor %2",
"themeDark": "Dark",
"themeLight": "Light",
"settingTheme": "Theme",
"largeTextLabel": "Grande",
"settingInterfaceZoom": "Zoom level",
"localeDe": "Deutsche",
"localePt": "Portuguesa",
"localeFr": "Frances",
"localeEn": "English",
"settingLanguage": "Language",
"blockUnknownLabel": "Block Unknown Peers",
"zoomLabel": "Zoom da interface (afeta principalmente tamanho de texto e botões)",
"versionBuilddate": "Version: %1 Built on: %2",
"cwtchSettingsTitle": "Configurações do Cwtch",
"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 peers 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",
"copiedToClipboardNotification": "Copiado",
"copyBtn": "Copiar",
"editProfile": "Edit Profille",
"newProfile": "New Profile",
"defaultProfileName": "Alice",
"profileName": "Display name",
"editProfileTitle": "Edit Profile",
"addProfileTitle": "Add new profile",
"deleteBtn": "Deletar",
"unblockBtn": "Unblock Peer",
"dontSavePeerHistory": "Delete Peer History",
"savePeerHistoryDescription": "Determines whether or not to delete any history associated with the peer.",
"savePeerHistory": "Save Peer History",
"blockBtn": "Block Peer",
"saveBtn": "Salvar",
"displayNameLabel": "Nome de Exibição",
"addressLabel": "Endereço",
"puzzleGameBtn": "Jogo de Adivinhação",
"bulletinsBtn": "Boletins",
"listsBtn": "Listas",
"chatBtn": "Chat",
"rejectGroupBtn": "Recusar",
"acceptGroupBtn": "Aceitar",
"acceptGroupInviteLabel": "Você quer aceitar o convite para",
"newGroupBtn": "Criar novo grupo",
"copiedClipboardNotification": "Copiado",
"peerOfflineMessage": "Peer is offline, messages can't be delivered right now",
"peerBlockedMessage": "Peer is blocked",
"pendingLabel": "Pendente",
"acknowledgedLabel": "Confirmada",
"couldNotSendMsgError": "Não deu para enviar esta mensagem",
"dmTooltip": "Clique para DM",
"membershipDescription": "A lista abaixo é de usuários que enviaram mensagens ao grupo. Essa lista pode não refletir todos os usuários que têm acesso ao grupo.",
"addListItemBtn": "Add Item",
"peerNotOnline": "Peer is Offline. Applications cannot be used right now.",
"searchList": "Search List",
"update": "Update",
"inviteBtn": "Convidar",
"inviteToGroupLabel": "Convidar ao grupo",
"groupNameLabel": "Nome do Grupo",
"viewServerInfo": "Server Info",
"serverNotSynced": "Out of Sync",
"serverSynced": "Synced",
"serverConnectivityDisconnected": "Server Disconnected",
"serverConnectivityConnected": "Server Connected",
"serverInfo": "Server Information",
"invitationLabel": "Convite",
"serverLabel": "Servidor",
"search": "Search...",
"cycleColoursDesktop": "Click to cycle colours.\nRight-click to reset.",
"cycleColoursAndroid": "Click to cycle colours.\nLong-press to reset.",
"cycleMorphsDesktop": "Click to cycle morphs.\nRight-click to reset.",
"cycleMorphsAndroid": "Click to cycle morphs.\nLong-press to reset.",
"cycleCatsDesktop": "Click to cycle category.\nRight-click to reset.",
"cycleCatsAndroid": "Click to cycle category.\nLong-press to reset.",
"blocked": "Blocked",
"titlePlaceholder": "título…",
"postNewBulletinLabel": "Postar novo boletim",
"newBulletinLabel": "Novo Boletim",
"joinGroup": "Join group",
"createGroup": "Create group",
"addPeer": "Add Peer",
"groupAddr": "Address",
"invitation": "Invitation",
"server": "Server",
"groupName": "Group name",
"peerName": "Name",
"peerAddress": "Address",
"joinGroupTab": "Join a group",
"createGroupTab": "Create a group",
"addPeerTab": "Add a peer",
"createGroupBtn": "Criar",
"defaultGroupName": "Grupo incrível",
"createGroupTitle": "Criar Grupo"
}

View File

@ -1,5 +1,4 @@
import 'dart:convert';
import 'package:cwtch/notification_manager.dart';
import 'package:cwtch/views/messageview.dart';
import 'package:cwtch/widgets/rightshiftfixer.dart';
@ -19,16 +18,20 @@ import 'licenses.dart';
import 'model.dart';
import 'views/profilemgrview.dart';
import 'views/splashView.dart';
import 'dart:io' show Platform;
import 'dart:io' show Platform, exit;
import 'opaque.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
var globalSettings = Settings(Locale("en", ''), Opaque.dark);
var globalErrorHandler = ErrorHandler();
var globalTorStatus = TorStatus();
var globalAppState = AppState();
void main() {
print("main()");
LicenseRegistry.addLicense(() => licenses());
WidgetsFlutterBinding.ensureInitialized();
print("runApp()");
runApp(Flwtch());
}
@ -42,49 +45,49 @@ class Flwtch extends StatefulWidget {
class FlwtchState extends State<Flwtch> {
final TextStyle biggerFont = const TextStyle(fontSize: 18);
late Cwtch cwtch;
bool cwtchInit = false;
late ProfileInfoState selectedProfile;
String selectedConversation = "";
var columns = [1]; // default or 'single column' mode
//var columns = [1, 1, 2];
late ProfileListState profs;
final MethodChannel notificationClickChannel = MethodChannel('im.cwtch.flwtch/notificationClickHandler');
final MethodChannel shutdownMethodChannel = MethodChannel('im.cwtch.flwtch/shutdown');
final GlobalKey<NavigatorState> navKey = GlobalKey<NavigatorState>();
@override
initState() {
print("initState: running...");
super.initState();
cwtchInit = false;
print("initState: registering notification, shutdown handlers...");
profs = ProfileListState();
notificationClickChannel.setMethodCallHandler(_externalNotificationClicked);
shutdownMethodChannel.setMethodCallHandler(shutdown);
print("initState: creating cwtchnotifier, ffi");
if (Platform.isAndroid) {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager());
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState);
cwtch = CwtchGomobile(cwtchNotifier);
} else if (Platform.isLinux) {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, LinuxNotificationsManager());
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, LinuxNotificationsManager(), globalAppState);
cwtch = CwtchFfi(cwtchNotifier);
} else {
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager());
var cwtchNotifier = new CwtchNotifier(profs, globalSettings, globalErrorHandler, globalTorStatus, NullNotificationsManager(), globalAppState);
cwtch = CwtchFfi(cwtchNotifier);
}
print("initState: invoking cwtch.Start()");
cwtch.Start();
setState(() {
cwtchInit = true;
});
print("initState: done!");
}
ChangeNotifierProvider<TorStatus> getTorStatusProvider() => ChangeNotifierProvider.value(value: globalTorStatus);
ChangeNotifierProvider<ErrorHandler> getErrorHandlerProvider() => ChangeNotifierProvider.value(value: globalErrorHandler);
ChangeNotifierProvider<Settings> getSettingsProvider() => ChangeNotifierProvider.value(value: globalSettings);
ChangeNotifierProvider<AppState> getAppStateProvider() => ChangeNotifierProvider.value(value: globalAppState);
Provider<FlwtchState> getFlwtchStateProvider() => Provider<FlwtchState>(create: (_) => this);
ChangeNotifierProvider<ProfileListState> getProfileListProvider() => ChangeNotifierProvider(create: (context) => profs);
@override
Widget build(BuildContext context) {
//appStatus = AppModel(cwtch: cwtch);
globalSettings.initPackageInfo();
return MultiProvider(
providers: [
@ -93,10 +96,11 @@ class FlwtchState extends State<Flwtch> {
getSettingsProvider(),
getErrorHandlerProvider(),
getTorStatusProvider(),
getAppStateProvider(),
],
builder: (context, widget) {
return Consumer<Settings>(
builder: (context, settings, child) => MaterialApp(
return Consumer2<Settings, AppState>(
builder: (context, settings, appState, child) => MaterialApp(
key: Key('app'),
navigatorKey: navKey,
locale: settings.locale,
@ -104,13 +108,26 @@ class FlwtchState extends State<Flwtch> {
supportedLocales: AppLocalizations.supportedLocales,
title: 'Cwtch',
theme: mkThemeData(settings),
home: cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ShiftRightFixer(child: ProfileMgrView())) : SplashView(),
home: appState.cwtchInit == true ? (columns.length == 3 ? TripleColumnView() : ShiftRightFixer(child: ProfileMgrView())) : SplashView(),
),
);
},
);
}
Future<void> shutdown(MethodCall call) async {
cwtch.Shutdown();
// Wait a few seconds as shutting down things takes a little time..
Future.delayed(Duration(seconds: 2)).then((value) {
if (Platform.isAndroid) {
SystemNavigator.pop();
} else if (Platform.isLinux || Platform.isWindows) {
print("Exiting...");
exit(0);
}
});
}
Future<void> _externalNotificationClicked(MethodCall call) async {
var args = jsonDecode(call.arguments);
var profile = profs.getProfile(args["ProfileOnion"])!;

View File

@ -38,13 +38,13 @@ class ProfileListState extends ChangeNotifier {
List<ProfileInfoState> _profiles = [];
int get num => _profiles.length;
void addAll(Iterable<ProfileInfoState> newProfiles) {
_profiles.addAll(newProfiles);
notifyListeners();
}
void add(ProfileInfoState newProfile) {
_profiles.add(newProfile);
void add(String onion, String name, String picture, String contactsJson, String serverJson, bool online) {
var idx = _profiles.indexWhere((element) => element.onion == onion);
if (idx == -1) {
_profiles.add(ProfileInfoState(onion: onion, nickname: name, imagePath: picture, contactsJson: contactsJson, serversJson: serverJson, online: online));
} else {
_profiles[idx].updateFrom(onion, name, picture, contactsJson, serverJson, online);
}
notifyListeners();
}
@ -61,6 +61,21 @@ class ProfileListState extends ChangeNotifier {
}
}
class AppState extends ChangeNotifier {
bool cwtchInit = false;
String appError = "";
void SetCwtchInit() {
cwtchInit = true;
notifyListeners();
}
void SetAppError(String error) {
appError = error;
notifyListeners();
}
}
class ContactListState extends ChangeNotifier {
List<ContactInfoState> _contacts = [];
String _filter = "";
@ -246,6 +261,41 @@ class ProfileInfoState extends ChangeNotifier {
super.dispose();
print("profileinfostate.dispose()");
}
void updateFrom(String onion, String name, String picture, String contactsJson, String serverJson, bool online) {
this._nickname = name;
this._imagePath = picture;
this._online = online;
this.replaceServers(serverJson);
if (contactsJson != null && contactsJson != "" && contactsJson != "null") {
List<dynamic> contacts = jsonDecode(contactsJson);
contacts.forEach((contact) {
var profileContact = this._contacts.getContact(contact["onion"]);
if (profileContact != null) {
profileContact.status = contact["status"];
profileContact.totalMessages = contact["numMessages"];
profileContact.lastMessageTime = DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"]));
} else {
this._contacts.add(ContactInfoState(
this.onion,
contact["onion"],
nickname: contact["name"],
status: contact["status"],
imagePath: contact["picture"],
isBlocked: contact["authorization"] == "blocked",
isInvitation: contact["authorization"] == "unknown",
savePeerHistory: contact["saveConversationHistory"],
numMessages: contact["numMessages"],
numUnread: contact["numUnread"],
isGroup: contact["isGroup"],
server: contact["groupServer"],
lastMessageTime: DateTime.fromMillisecondsSinceEpoch(1000 * int.parse(contact["lastMsgTime"])),
));
}
});
}
}
}
class ContactInfoState extends ChangeNotifier {

View File

@ -22,7 +22,7 @@ class Settings extends ChangeNotifier {
String _uiColumnModePortrait = "Single";
String _uiColumnModeLandscape = "Same";
late bool blockUnknownConnections;
bool blockUnknownConnections = false;
/// Set the dark theme.
void setDark() {

View File

@ -4,8 +4,9 @@ class TorStatus extends ChangeNotifier {
int progress;
String status;
bool connected;
String version;
TorStatus({this.connected = false, this.progress = 0, this.status = ""});
TorStatus({this.connected = false, this.progress = 0, this.status = "", this.version = ""});
/// Called by the event bus.
handleUpdate(int new_progress, String new_status) {
@ -20,4 +21,9 @@ class TorStatus extends ChangeNotifier {
notifyListeners();
}
updateVersion(String new_version) {
version = new_version;
notifyListeners();
}
}

View File

@ -67,12 +67,6 @@ class _MessageViewState extends State<MessageView> {
return true;
}
void _debugResetContact() {
Provider.of<FlwtchState>(context, listen: false)
.cwtch
.DebugResetContact(Provider.of<ContactInfoState>(context, listen: false).profileOnion, Provider.of<ContactInfoState>(context, listen: false).onion);
}
void _pushContactSettings() {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext bcontext) {
@ -193,12 +187,12 @@ class _MessageViewState extends State<MessageView> {
),
ElevatedButton(
child: Text(AppLocalizations.of(bcontext)!.inviteBtn, semanticsLabel: AppLocalizations.of(bcontext)!.inviteBtn),
onPressed: this.selectedContact == ""
? null
: () {
this._sendInvitation();
Navigator.pop(bcontext);
},
onPressed: () {
if (this.selectedContact != "") {
this._sendInvitation();
}
Navigator.pop(bcontext);
},
),
],
)),

View File

@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'package:cwtch/cwtch_icons_icons.dart';
import 'package:flutter/material.dart';
@ -6,6 +7,7 @@ import 'package:cwtch/settings.dart';
import 'package:cwtch/views/torstatusview.dart';
import 'package:cwtch/widgets/passwordfield.dart';
import 'package:cwtch/widgets/tor_icon.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:cwtch/widgets/profilerow.dart';
import 'package:provider/provider.dart';
@ -26,6 +28,8 @@ class ProfileMgrView extends StatefulWidget {
class _ProfileMgrViewState extends State<ProfileMgrView> {
final ctrlrPassword = TextEditingController();
bool closeApp = false;
@override
void dispose() {
ctrlrPassword.dispose();
@ -39,7 +43,10 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
// (which would shutdown connections and all kinds of other expensive to generate things)
// TODO pop up a dialogue regarding closing the app?
builder: (context, settings, child) => WillPopScope(
onWillPop: () async => false,
onWillPop: () async {
_showShutdown();
return closeApp;
},
child: Scaffold(
backgroundColor: settings.theme.backgroundMainColor(),
appBar: AppBar(
@ -86,9 +93,6 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
));
// Only show debug button on development builds
if (EnvironmentConfig.BUILD_VER == dev_version) {
actions.add(IconButton(icon: Icon(Icons.bug_report_outlined), tooltip: "Turn on Debug Logging", onPressed: _setLoggingLevelDebug));
}
// Unlock Profiles
actions.add(IconButton(
@ -100,16 +104,45 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
// Global Settings
actions.add(IconButton(icon: Icon(Icons.settings), tooltip: AppLocalizations.of(context)!.tooltipOpenSettings, onPressed: _pushGlobalSettings));
actions.add(IconButton(icon: Icon(Icons.close), tooltip: AppLocalizations.of(context)!.shutdownCwtchTooltip, onPressed: _showShutdown));
return actions;
}
void _setLoggingLevelDebug() {
final setLoggingLevel = {
"EventType": "SetLoggingLevel",
"Data": {"Debug": "true"},
};
final setLoggingLevelJson = jsonEncode(setLoggingLevel);
Provider.of<FlwtchState>(context, listen: false).cwtch.SendAppEvent(setLoggingLevelJson);
_showShutdown() {
// set up the buttons
Widget cancelButton = TextButton(
child: Text(AppLocalizations.of(context)!.cancel),
onPressed: () {
Navigator.of(context).pop(); // dismiss dialog
},
);
Widget continueButton = TextButton(
child: Text(AppLocalizations.of(context)!.shutdownCwtchAction),
onPressed: () {
// Directly call the shutdown command, Android will do this for us...
Provider.of<FlwtchState>(context, listen: false).shutdown(MethodCall(""));
closeApp = true;
});
// set up the AlertDialog
AlertDialog alert = AlertDialog(
title: Text(AppLocalizations.of(context)!.shutdownCwtchDialogTitle),
content: Text(AppLocalizations.of(context)!.shutdownCwtchDialog),
actions: [
cancelButton,
continueButton,
],
);
// show the dialog
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return alert;
},
);
}
void _pushGlobalSettings() {

View File

@ -1,11 +1,37 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../model.dart';
import '../settings.dart';
class SplashView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const Scaffold(
body: const Center(child: const Text("Loading Cwtch...")),
);
return Consumer<AppState>(
builder: (context, appState, child) => Scaffold(
body: Center(
child: Column(mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [
Image(
image: AssetImage("assets/knott.png"),
filterQuality: FilterQuality.medium,
isAntiAlias: true,
width: 200,
height: 200,
),
Image(
image: AssetImage("assets/cwtch_title.png"),
filterQuality: FilterQuality.medium,
isAntiAlias: true,
),
Padding(
padding: const EdgeInsets.all(20.0),
child: Text(appState.appError == "" ? "Loading Cwtch..." : appState.appError,
style: TextStyle(
fontSize: 16.0, color: appState.appError == "" ? Provider.of<Settings>(context).theme.mainTextColor() : Provider.of<Settings>(context).theme.textfieldErrorColor())),
),
Image(image: AssetImage("assets/Open_Privacy_Logo_lightoutline.png")),
])),
));
}
}

View File

@ -51,7 +51,11 @@ class _TorStatusView extends State<TorStatusView> {
Provider.of<FlwtchState>(context, listen: false).cwtch.ResetTor();
},
),
)
),
ListTile(
title: Text(AppLocalizations.of(context)!.torVersion),
subtitle: Text(torStatus.version),
),
]))));
});
});

View File

@ -66,7 +66,6 @@ class _ProfileRowState extends State<ProfileRow> {
onTap: () {
setState(() {
var flwtch = Provider.of<FlwtchState>(context, listen: false);
flwtch.cwtch.SelectProfile(profile.onion);
flwtch.setState(() {
flwtch.selectedProfile = profile;
flwtch.selectedConversation = "";

10
linux/cwtch.home.desktop Executable file
View File

@ -0,0 +1,10 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Cwtch
Comment=Metadata Resistant Chat
Exec=env LD_LIBRARY_PATH=~/.local/lib/cwtch/ ~/.local/bin/cwtch
Icon=cwtch
Terminal=false
Categories=Network;InstantMessaging;
Keywords=Internet;IM;Instant Messaging;Messaging;Chat;

View File

@ -1,9 +1,10 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=cwtch
Name=Cwtch
Comment=Metadata Resistant Chat
Exec=env LD_LIBRARY_PATH=./lib/ ./cwtch
Icon=cwtch
Terminal=false
Categories=Internet;Chat;
Categories=Network;InstantMessaging;
Keywords=Internet;IM;Instant Messaging;Messaging;Chat

10
linux/cwtch.sys.desktop Executable file
View File

@ -0,0 +1,10 @@
[Desktop Entry]
Version=1.0
Type=Application
Name=Cwtch
Comment=Metadata Resistant Chat
Exec=env LD_LIBRARY_PATH=/usr/lib/cwtch /usr/bin/cwtch
Icon=cwtch
Terminal=false
Categories=Network;InstantMessaging;
Keywords=Internet;IM;Instant Messaging;Messaging;Chat

17
linux/install-home.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh
mkdir -p ~/.local/bin
cp cwtch ~/.local/bin/
mkdir -p ~/.local/share/icons
cp cwtch.png ~/.local/share/icons
mkdir -p ~/.local/share/cwtch
cp -r data ~/.local/share/cwtch
mkdir -p ~/.local/lib/cwtch
cp -r lib/* ~/.local/lib/cwtch
mkdir -p ~/.local/share/applications
sed "s|~|$HOME|g" cwtch.home.desktop > $HOME/.local/share/applications/cwtch.desktop

13
linux/install-sys.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/sh
cp cwtch /usr/bin/
cp cwtch.png /usr/share/icons
mkdir -p /usr/share/cwtch
cp -r data /usr/share/cwtch
mkdir -p /usr/lib/cwtch
cp -r lib/* /usr/lib/cwtch
cp cwtch.sys.desktop /usr/share/applications/cwtch.desktop

View File

@ -69,7 +69,6 @@ static void my_application_activate(GApplication* application) {
gtk_window_set_title(window, "cwtch");
}
gtk_window_set_icon_from_file(window, "./cwtch.png", NULL);
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
@ -83,20 +82,23 @@ static void my_application_activate(GApplication* application) {
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);
// /home/$USER/bin/cwtch/lib
project->aot_library_path = g_build_filename(homedir, "bin", "cwtch", "lib", 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", nullptr);
project->icu_data_path = 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 {
// /usr/share/cwtch/data/flutter_assets
project->assets_path = g_build_filename("usr", "share", "cwtch", "data", "flutter_assets", nullptr);
project->assets_path = g_build_filename("/", "usr", "share", "cwtch", "data", "flutter_assets", nullptr);
// /usr/lib/cwtch
project->aot_library_path = g_build_filename("usr", "lib", "cwtch", nullptr);
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", nullptr);
project->icu_data_path = g_build_filename("/", "usr", "share", "cwtch", "data", "icudtl.dat", nullptr);
gtk_window_set_icon_from_file(window, "/usr/share/icons/cwtch.png", NULL);
}
} else {
gtk_window_set_icon_from_file(window, "./cwtch.png", NULL);
}
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);
FlView* view = fl_view_new(project);

View File

@ -15,7 +15,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+11
version: 1.0.0+14
environment:
sdk: ">=2.12.0 <3.0.0"
@ -51,16 +51,13 @@ dependencies:
path: plugins/window_size
ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
# Uncomment to update lokalise translations (see README for list of deps to comment out bc incompatibilities)
#dev_dependencies:
# flutter_lokalise: any
# alternatively: flutter pub run intl_translation:generate_from_arb --output-dir=lib/l10n --no-use-deferred-loading lib/intl/app_localizations.dart lib/l10n/intl_*.arb --api-token X --project-id Y
#flutter_lokalise:
# project_id: ""
# api_token: ""
# include_tags:
# - tag1
# - tag2
# project_id: "737094205fceda35c50aa2.60364948"
# api_token: "0407300fe4aa1edf1c1818e56234589e74c83c59" # Read only api Token from Dan
flutter_intl:
enabled: true