Compare commits
115 Commits
Author | SHA1 | Date |
---|---|---|
Dan Ballard | bfae3b577f | |
Dan Ballard | b20a82fb1a | |
Dan Ballard | 302e504d76 | |
Dan Ballard | 00c77c97d2 | |
Dan Ballard | d12efa2204 | |
Dan Ballard | ee10637261 | |
Sarah Jamie Lewis | 3cf87e825b | |
Dan Ballard | 96d9090b1d | |
Dan Ballard | ca58e063b6 | |
Dan Ballard | 2d537df810 | |
Dan Ballard | 94117e88d2 | |
Dan Ballard | 034a856c05 | |
Dan Ballard | 69c748dcc4 | |
Dan Ballard | 9ae38f6870 | |
Dan Ballard | 97c4deb4bf | |
Sarah Jamie Lewis | 8d6d5a23b9 | |
Sarah Jamie Lewis | 9a6dbae0c4 | |
erinn | 6eccece360 | |
Sarah Jamie Lewis | d538d8442e | |
Sarah Jamie Lewis | 62ef3549b5 | |
Dan Ballard | e33a543e66 | |
Sarah Jamie Lewis | 0090e86ade | |
Dan Ballard | 82fe74937c | |
Dan Ballard | 23b0af77f1 | |
Sarah Jamie Lewis | dad995cd3c | |
Sarah Jamie Lewis | 17518b1e6d | |
Sarah Jamie Lewis | 0c80577f72 | |
Sarah Jamie Lewis | a47a17b3a9 | |
Dan Ballard | 5ca0712d5e | |
Dan Ballard | b61f59643f | |
erinn | 82ee06d840 | |
erinn | eb12f57135 | |
Sarah Jamie Lewis | ddb671cf6a | |
Sarah Jamie Lewis | 93dbba5f15 | |
Dan Ballard | 1c961c2c4c | |
Dan Ballard | 040bb585a1 | |
Dan Ballard | e89c4bdb0e | |
Dan Ballard | f8e5bd29f3 | |
Dan Ballard | a18a522f81 | |
Dan Ballard | 16ce38da75 | |
Dan Ballard | 126c68e185 | |
Dan Ballard | c533580b70 | |
Dan Ballard | ca303ba41c | |
Dan Ballard | ff4a8a02ab | |
Dan Ballard | 6c0c31a74f | |
Dan Ballard | 4e0be20930 | |
Dan Ballard | 0a93dd40b6 | |
Dan Ballard | 5f6008a152 | |
Dan Ballard | 901293377e | |
Dan Ballard | a54d50a642 | |
Dan Ballard | f7c30755d0 | |
Dan Ballard | 66568a7983 | |
Dan Ballard | da8a35458f | |
Dan Ballard | ae2ae51d2d | |
Dan Ballard | b7a4486536 | |
Dan Ballard | 3e56f8c917 | |
erinn | c4203477de | |
erinn | 1c1a1ec68d | |
Dan Ballard | 225301bda6 | |
Sarah Jamie Lewis | 47394f5183 | |
Sarah Jamie Lewis | 0c0a8501c9 | |
Dan Ballard | 7268fc362f | |
Dan Ballard | e0d1e1cb2b | |
Dan Ballard | f0e0af3537 | |
Sarah Jamie Lewis | e266d6328a | |
Sarah Jamie Lewis | 94014ede01 | |
Dan Ballard | 547c2de725 | |
Sarah Jamie Lewis | 066c1965e6 | |
Sarah Jamie Lewis | ee2fd2ae98 | |
Dan Ballard | 607802d4e4 | |
Sarah Jamie Lewis | ecd1b71a8c | |
Sarah Jamie Lewis | 4e63694ebc | |
erinn | a7fe5f0f80 | |
erinn | 4b67879f64 | |
erinn | d0ba17b0e9 | |
Dan Ballard | c5154a91d1 | |
erinn | 886e0956f6 | |
Sarah Jamie Lewis | 4671a15c27 | |
erinn | a03a665a66 | |
erinn | b85f1464d3 | |
erinn | 09d9771845 | |
erinn | 24139a4683 | |
Dan Ballard | 398d7a286a | |
Dan Ballard | bb739027ea | |
Dan Ballard | 506ddeeb10 | |
Sarah Jamie Lewis | 219cd3889a | |
Dan Ballard | d3a6ec85cf | |
erinn | 8b8b20e7b6 | |
Sarah Jamie Lewis | 0cbc4298e2 | |
erinn | 4377118629 | |
erinn | 2d49c50de5 | |
erinn | 4d3777be81 | |
erinn | 88c470016b | |
Dan Ballard | a10da52767 | |
Sarah Jamie Lewis | 4b6f022a92 | |
Dan Ballard | 92577aef07 | |
erinn | cc4ec567fb | |
erinn | 0b7f2b13bd | |
erinn | 24854c24f5 | |
erinn | 564556d8fa | |
erinn | ed621e7795 | |
erinn | 30ae6ca331 | |
Sarah Jamie Lewis | 5b96d1c794 | |
Sarah Jamie Lewis | 87732a2ba2 | |
Sarah Jamie Lewis | 213a29c691 | |
Sarah Jamie Lewis | 6b93f059d7 | |
Sarah Jamie Lewis | 052649ef9d | |
erinn | 7940c27709 | |
Dan Ballard | 1888d3f032 | |
Dan Ballard | 650148d731 | |
Dan Ballard | f08062c0fb | |
Sarah Jamie Lewis | e55a7e940e | |
Sarah Jamie Lewis | 1e2c69eb4c | |
Sarah Jamie Lewis | 2a3945639d | |
Sarah Jamie Lewis | 16f9928275 |
239
.drone.yml
239
.drone.yml
|
@ -1,175 +1,3 @@
|
|||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: linux-android-test
|
||||
|
||||
clone:
|
||||
disable: true
|
||||
|
||||
steps:
|
||||
- name: clone
|
||||
image: cirrusci/flutter:dev
|
||||
environment:
|
||||
buildbot_key_b64:
|
||||
from_secret: buildbot_key_b64
|
||||
commands:
|
||||
- mkdir ~/.ssh
|
||||
- echo $buildbot_key_b64 > ~/.ssh/id_rsa.b64
|
||||
- base64 -d ~/.ssh/id_rsa.b64 > ~/.ssh/id_rsa
|
||||
- chmod 400 ~/.ssh/id_rsa
|
||||
# force by pass of ssh host key check, less secure
|
||||
- ssh-keyscan -H git.openprivacy.ca >> ~/.ssh/known_hosts
|
||||
- git clone gogs@git.openprivacy.ca:flutter/flutter_app.git .
|
||||
- git checkout $DRONE_COMMIT
|
||||
|
||||
- name: fetch
|
||||
image: cirrusci/flutter:dev
|
||||
volumes:
|
||||
- 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
|
||||
- echo `git describe --tags` > VERSION
|
||||
- echo `date +%G-%m-%d-%H-%M` > BUILDDATE
|
||||
- flutter pub get
|
||||
- mkdir deploy
|
||||
- ./fetch-libcwtch-go.sh
|
||||
|
||||
#- name: quality
|
||||
# image: golang
|
||||
# volumes:
|
||||
# - name: deps
|
||||
# path: /go
|
||||
# commands:
|
||||
# - go list ./... | xargs go vet
|
||||
# - go list ./... | xargs golint
|
||||
# #Todo: fix all the lint errors and add `-set_exit_status` above to enforce linting
|
||||
|
||||
- name: build-linux
|
||||
image: openpriv/flutter-desktop:linux-dev
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /root/.pub-cache
|
||||
commands:
|
||||
- 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.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
|
||||
- cd deploy
|
||||
- mv linux cwtch
|
||||
- tar -czf cwtch-`cat ../VERSION`.tar.gz cwtch
|
||||
- rm -r cwtch
|
||||
|
||||
- name: build-android
|
||||
image: cirrusci/flutter:dev
|
||||
when:
|
||||
event: push
|
||||
environment:
|
||||
upload_jks_file_b64:
|
||||
from_secret: upload_jks_file_b64
|
||||
upload_jks_pass:
|
||||
from_secret: upload_jks_pass
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /root/.pub-cache
|
||||
commands:
|
||||
- echo $upload_jks_file_b64 > upload-keystore.jks.b64
|
||||
- base64 -i --decode upload-keystore.jks.b64 > android/app/upload-keystore.jks
|
||||
- sed -i "s/%jks-password%/$upload_jks_pass/g" android/key.properties
|
||||
- flutter build appbundle --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
|
||||
# cant do debug for final release, this is just a stop gap
|
||||
- flutter build apk --dart-define BUILD_VER=`cat VERSION` --dart-define BUILD_DATE=`cat BUILDDATE`
|
||||
# or build apk --split-per-abi ?
|
||||
- cp build/app/outputs/bundle/release/app-release.aab deploy/
|
||||
- cp build/app/outputs/apk/release/app-release.apk deploy/
|
||||
#- cp build/app/outputs/flutter-apk/app-debug.apk deploy/android
|
||||
|
||||
- name: widget-tests
|
||||
image: cirrusci/flutter:dev
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /root/.pub-cache
|
||||
commands:
|
||||
# - flutter config --enable-linux-desktop
|
||||
- flutter test --coverage
|
||||
- genhtml coverage/lcov.info -o coverage/html
|
||||
|
||||
# Todo: gonna need more work on container
|
||||
# https://flutter.dev/desktop
|
||||
# requirements: Visual Studio 2019 (not to be confused with Visual Studio Code) with the “Desktop development with C++” workload installed, including all of its default components
|
||||
#- name: build-windows
|
||||
# image: cirrusci/flutter:dev
|
||||
#- volumes:
|
||||
# - name: deps
|
||||
# path: /root/.pub-cache
|
||||
# commands:
|
||||
# - flutter config --enable-windows-desktop
|
||||
# - flutter build windows
|
||||
|
||||
- name: deploy-buildfiles
|
||||
image: kroniak/ssh-client
|
||||
environment:
|
||||
BUILDFILES_KEY:
|
||||
from_secret: buildfiles_key
|
||||
secrets: [gogs_account_token]
|
||||
when:
|
||||
event: push
|
||||
status: [ success ]
|
||||
commands:
|
||||
- echo $BUILDFILES_KEY > ~/id_rsab64
|
||||
- base64 -d ~/id_rsab64 > ~/id_rsa
|
||||
- chmod 400 ~/id_rsa
|
||||
- export DIR=flwtch-`cat VERSION`-`cat BUILDDATE`
|
||||
- mv deploy $DIR
|
||||
- cp -r coverage/html $DIR/coverage-tests
|
||||
- cp -r test/failures $DIR/test-failures || true
|
||||
- cd $DIR
|
||||
- find . -type f -exec sha256sum {} \; > ./../sha256s.txt
|
||||
- mv ./../sha256s.txt .
|
||||
- cd ..
|
||||
# TODO: do deployment once files actaully compile
|
||||
- scp -r -o StrictHostKeyChecking=no -i ~/id_rsa $DIR buildfiles@openprivacy.ca:/home/buildfiles/buildfiles/
|
||||
|
||||
- name: notify-email
|
||||
image: drillster/drone-email
|
||||
settings:
|
||||
host: build.openprivacy.ca
|
||||
port: 25
|
||||
skip_verify: true
|
||||
from: drone@openprivacy.ca
|
||||
when:
|
||||
status: [ failure ]
|
||||
|
||||
- name: notify-gogs
|
||||
image: openpriv/drone-gogs
|
||||
when:
|
||||
event: pull_request
|
||||
status: [ success, changed, failure ]
|
||||
environment:
|
||||
GOGS_ACCOUNT_TOKEN:
|
||||
from_secret: gogs_account_token
|
||||
settings:
|
||||
gogs_url: https://git.openprivacy.ca
|
||||
|
||||
volumes:
|
||||
- name: deps
|
||||
temp: {}
|
||||
|
||||
trigger:
|
||||
repo: flutter/flutter_app
|
||||
branch: trunk
|
||||
event:
|
||||
- push
|
||||
- pull_request
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: windows
|
||||
|
@ -204,45 +32,62 @@ 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
|
||||
-
|
||||
|
||||
- name: build-windows
|
||||
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
|
||||
commands:
|
||||
- flutter pub get
|
||||
# flwtch-`cat VERSION`-`cat BUILDDATE`
|
||||
- $Env:buildname = 'flwtch-win-'
|
||||
- $Env:version += type .\VERSION
|
||||
- $Env:buildname += $Env:version
|
||||
- $Env:buildname += '-'
|
||||
- $Env:builddate += type .\BUILDDATE
|
||||
- $Env:buildname += $Env:builddate
|
||||
- $Env:builddir += $Env:buildname
|
||||
- $Env:zip = 'cwtch-'
|
||||
- $Env:zip += $Env:version
|
||||
- $Env:zip += '.zip'
|
||||
- $Env:sha = $Env:zip
|
||||
- $Env:sha += '.sha512'
|
||||
- $Env:releasedir = "build\\windows\\runner\\Release\\"
|
||||
- flutter build windows --dart-define BUILD_VER=$Env:version --dart-define BUILD_DATE=$Env:builddate
|
||||
- mkdir deploy
|
||||
- move build\\windows\\runner\\Release $Env:builddir
|
||||
- copy windows\libCwtch.dll $Env:builddir
|
||||
- copy windows\libCwtch.dll $Env:releasedir
|
||||
# flutter hasn't worked out it's packaging of required dll's so we have to resort to this manual nonsense
|
||||
# https://github.com/google/flutter-desktop-embedding/issues/587
|
||||
# https://github.com/flutter/flutter/issues/53167
|
||||
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140.dll $Env:builddir
|
||||
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $Env:builddir
|
||||
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\msvcp140.dll $Env:builddir
|
||||
- powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:builddir\Tor"
|
||||
- powershell -command "Compress-Archive -Path $Env:builddir -DestinationPath $Env:zip"
|
||||
- powershell -command "(Get-FileHash *.zip -Algorithm sha512).Hash" > $Env:sha
|
||||
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140.dll $Env:releasedir
|
||||
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\vcruntime140_1.dll $Env:releasedir
|
||||
- copy C:\BuildTools\VC\Redist\MSVC\14.29.30036\x64\Microsoft.VC142.CRT\msvcp140.dll $Env:releasedir
|
||||
- powershell -command "Expand-Archive -Path tor.zip -DestinationPath $Env:releasedir\Tor"
|
||||
|
||||
- name: package-windows
|
||||
image: openpriv/nsis:latest
|
||||
environment:
|
||||
pfx:
|
||||
from_secret: pfx
|
||||
pfx_pass:
|
||||
from_secret: pfx_pass
|
||||
commands:
|
||||
- $Env:version += type .\VERSION
|
||||
- $Env:builddate += type .\BUILDDATE
|
||||
- $Env:releasedir = "build\\windows\\runner\\Release\\"
|
||||
- $Env:zip = 'cwtch-' + $Env:version + '.zip'
|
||||
- $Env:zipsha = $Env:zip + '.sha512'
|
||||
- $Env:msix = 'cwtch-install-' + $Env:version + '.msix'
|
||||
- $Env:msixsha = $Env:msix + '.sha512'
|
||||
- $Env:buildname = 'flwtch-win-' + $Env:version + '-' + $Env:builddate
|
||||
- $Env:builddir = $Env:buildname
|
||||
- echo $Env:pfx > codesign.pfx.b64
|
||||
- certutil -decode codesign.pfx.b64 codesign.pfx
|
||||
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com $Env:releasedir\cwtch.exe
|
||||
- copy windows\runner\resources\knot_128.ico $Env:releasedir\cwtch.ico
|
||||
- makensis windows\nsis\cwtch-installer.nsi
|
||||
- move windows\nsis\cwtch-installer.exe cwtch-installer.exe
|
||||
- signtool sign /v /fd sha256 /a /f codesign.pfx /p $Env:pfx_pass /tr http://timestamp.digicert.com cwtch-installer.exe
|
||||
- powershell -command "(Get-FileHash cwtch-installer.exe -Algorithm sha512).Hash" > cwtch-installer.sha512
|
||||
- mkdir deploy
|
||||
- mkdir deploy\$Env:builddir
|
||||
- move $Env:zip deploy\$Env:builddir
|
||||
- move $Env:sha deploy\$Env:builddir
|
||||
- move $Env:releasedir $Env:builddir
|
||||
- powershell -command "Compress-Archive -Path $Env:builddir -DestinationPath cwtch.zip"
|
||||
- powershell -command "(Get-FileHash cwtch.zip -Algorithm sha512).Hash" > $Env:zipsha
|
||||
- move cwtch-installer.exe deploy\$Env:builddir\cwtch-installer.exe
|
||||
- move cwtch.zip deploy\$Env:builddir\$Env:zip
|
||||
- move *.sha512 deploy\$Env:builddir
|
||||
|
||||
- name: deploy-windows
|
||||
image: openpriv/flutter-desktop:windows-sdk30-fdev2.3rc
|
||||
|
|
|
@ -1 +1 @@
|
|||
v0.0.2-63-g033de73-2021-06-11-21-41
|
||||
v0.0.2-108-g3964348-2021-06-24-17-42
|
|
@ -0,0 +1,9 @@
|
|||
MIT License
|
||||
Copyright (c) 2021 Open Privacy Research Society
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
50
SPEC.md
50
SPEC.md
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -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,12 +53,13 @@ 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) {
|
||||
|
@ -75,15 +75,15 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
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")
|
||||
.setContentText("New message")//todo: translate
|
||||
.setLargeIcon(BitmapFactory.decodeStream(fh))
|
||||
.setSmallIcon(R.mipmap.knott)
|
||||
.setContentIntent(PendingIntent.getActivity(applicationContext, 1, clickIntent, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
|
@ -93,7 +93,7 @@ class FlwtchWorker(context: Context, parameters: WorkerParameters) :
|
|||
}
|
||||
|
||||
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,115 +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 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)
|
||||
}
|
||||
"LeaveGroup" -> {
|
||||
val profile = (a.get("ProfileOnion") as? String) ?: "";
|
||||
val groupHandle = (a.get("groupHandle") as? String) ?: "";
|
||||
Log.i("FlwtchWorker.kt", "LeaveGroup: need to recompile aar and uncomment this line")//todo
|
||||
//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()
|
||||
}
|
||||
|
@ -224,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)
|
||||
|
@ -252,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)
|
||||
|
@ -278,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")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,43 +93,29 @@ 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)
|
||||
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)
|
||||
val data: Data = Data.Builder().putString(FlwtchWorker.KEY_METHOD, method).putString(FlwtchWorker.KEY_ARGS, JSONObject(argmap).toString()).build()
|
||||
|
@ -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 local broadcast receiver / event bus forwarder")
|
||||
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 - cancelling all WORKER_TAG and pruning old work")
|
||||
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:
|
||||
//
|
||||
|
@ -175,6 +188,8 @@ class MainActivity: FlutterActivity() {
|
|||
val Data = this.optString("Data")
|
||||
}
|
||||
|
||||
// MainActivity.MyBroadcastReceiver receives events from the Cwtch service via im.cwtch.flwtch.broadcast.SERVICE_EVENT_BUS Android local broadcast intents
|
||||
// then it forwards them to the flutter ui engine using the CWTCH_EVENTBUS methodchannel
|
||||
class MyBroadcastReceiver(mc: MethodChannel) : BroadcastReceiver() {
|
||||
val eventBus: MethodChannel = mc
|
||||
|
||||
|
@ -185,5 +200,4 @@ class MainActivity: FlutterActivity() {
|
|||
eventBus.invokeMethod(evtType, evtData)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 |
Binary file not shown.
After Width: | Height: | Size: 5.0 KiB |
|
@ -1,6 +1,6 @@
|
|||
|
||||
Invoke-WebRequest -Uri https://www.torproject.org/dist/torbrowser/10.0.16/tor-win32-0.4.5.7.zip -OutFile tor.zip
|
||||
Invoke-WebRequest -Uri https://dist.torproject.org/torbrowser/10.0.18/tor-win64-0.4.5.9.zip -OutFile tor.zip
|
||||
|
||||
if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '2b7d683f036d0fec149f1d2bdfcf5b7ef4c337005a2b685c056b00047fdb2b57d4c25b8559ad7ef5c7a030b273934be82a9f83ef6e391f5d7d13d8d6c83e8048' ) { Write-Error 'tor.zip sha512sum mismatch' }
|
||||
if ((Get-FileHash tor.zip -Algorithm sha512).Hash -ne '72764eb07ad8ab511603aba0734951ca003989f5f4686af91ba220217b4a8a4bcc5f571b59f52c847932f6efedf847b111621983050fcddbb8099d43ca66fb07' ) { Write-Error 'tor.zip sha512sum mismatch' }
|
||||
|
||||
Expand-Archive -Path tor.zip -DestinationPath Tor
|
||||
|
|
|
@ -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
|
|
@ -1,6 +1,8 @@
|
|||
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();
|
||||
|
||||
|
@ -10,6 +12,8 @@ abstract class Cwtch {
|
|||
void CreateProfile(String nick, String pass);
|
||||
// ignore: non_constant_identifier_names
|
||||
void LoadProfiles(String pass);
|
||||
// ignore: non_constant_identifier_names
|
||||
void DeleteProfile(String onion, String pass);
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
void ResetTor();
|
||||
|
@ -24,30 +28,19 @@ 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);
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
void LeaveConversation(String profile, String handle);
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
void CreateGroup(String profile, String server, String groupName);
|
||||
// ignore: non_constant_identifier_names
|
||||
|
@ -60,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();
|
||||
}
|
||||
|
|
|
@ -17,20 +17,28 @@ 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"));
|
||||
// if tag != v1-defaultPassword then it is either encrypted OR it is an unencrypted account created during pre-beta...
|
||||
profileCN.add(data["Identity"], data["name"], data["picture"], data["ContactsJson"], data["ServerList"], data["Online"] == "true", data["tag"] != "v1-defaultPassword");
|
||||
break;
|
||||
case "PeerCreated":
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.add(ContactInfoState(
|
||||
|
@ -57,13 +65,20 @@ 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()));
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
|
||||
}
|
||||
break;
|
||||
case "PeerDeleted":
|
||||
profileCN.delete(data["Identity"]);
|
||||
// todo standarize
|
||||
error.handleUpdate("deleteprofile.success");
|
||||
break;
|
||||
case "DeleteContact":
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.removeContact(data["RemotePeer"]);
|
||||
break;
|
||||
case "DeleteGroup":
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.removeContact(data["GroupID"]);
|
||||
break;
|
||||
|
@ -83,7 +98,9 @@ class CwtchNotifier {
|
|||
break;
|
||||
case "NewMessageFromPeer":
|
||||
notificationManager.notify("New Message From Peer!");
|
||||
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["RemotePeer"]) {
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.unreadMessages++;
|
||||
}
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["RemotePeer"])!.totalMessages++;
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["RemotePeer"], DateTime.now());
|
||||
break;
|
||||
|
@ -108,7 +125,9 @@ class CwtchNotifier {
|
|||
case "NewMessageFromGroup":
|
||||
if (data["ProfileOnion"] != data["RemotePeer"]) {
|
||||
//not from me
|
||||
if (appState.selectedProfile != data["ProfileOnion"] || appState.selectedConversation != data["GroupID"]) {
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.unreadMessages++;
|
||||
}
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.getContact(data["GroupID"])!.totalMessages++;
|
||||
profileCN.getProfile(data["ProfileOnion"])?.contactList.updateLastMessageTime(data["GroupID"], DateTime.now());
|
||||
} else {
|
||||
|
@ -125,6 +144,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"];
|
||||
|
@ -152,7 +176,10 @@ class CwtchNotifier {
|
|||
break;
|
||||
case "AppError":
|
||||
print("New App Error: $data");
|
||||
if (data["Data"] != null) {
|
||||
// special case for delete error (todo: standardize cwtch errors)
|
||||
if (data["Error"] == "Password did not match") {
|
||||
error.handleUpdate("deleteprofile.error");
|
||||
} else if (data["Data"] != null) {
|
||||
error.handleUpdate(data["Data"]);
|
||||
}
|
||||
break;
|
||||
|
@ -174,6 +201,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;
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:io';
|
|||
import 'dart:isolate';
|
||||
import 'dart:io' show Platform;
|
||||
import 'package:cwtch/cwtch/cwtchNotifier.dart';
|
||||
import 'package:flutter/src/services/text_input.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
@ -18,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);
|
||||
|
||||
|
@ -57,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) {
|
||||
|
@ -79,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;
|
||||
|
@ -104,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);
|
||||
|
@ -134,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
|
||||
|
@ -145,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;
|
||||
}
|
||||
}
|
||||
|
@ -184,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");
|
||||
|
@ -240,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) {
|
||||
|
@ -295,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) {
|
||||
|
@ -385,15 +328,26 @@ class CwtchFfi implements Cwtch {
|
|||
CreateGroup(u1, u1.length, u2, u2.length, u3, u3.length);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: non_constant_identifier_names
|
||||
void LeaveConversation(String profileOnion, String handle) {
|
||||
var leaveConversation = library.lookup<NativeFunction<string_string_to_void_function>>("c_LeaveConversation");
|
||||
// ignore: non_constant_identifier_names
|
||||
final LeaveConversation = leaveConversation.asFunction<VoidFromStringStringFn>();
|
||||
final u1 = profileOnion.toNativeUtf8();
|
||||
final u2 = handle.toNativeUtf8();
|
||||
LeaveConversation(u1, u1.length, u2, u2.length);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: non_constant_identifier_names
|
||||
void LeaveGroup(String profileOnion, String groupHandle) {
|
||||
var leaveGroup = library.lookup<NativeFunction<string_string_to_void_function>>("c_LeaveGroup");
|
||||
// ignore: non_constant_identifier_names
|
||||
final RejectInvite = leaveGroup.asFunction<VoidFromStringStringFn>();
|
||||
final LeaveGroup = leaveGroup.asFunction<VoidFromStringStringFn>();
|
||||
final u1 = profileOnion.toNativeUtf8();
|
||||
final u2 = groupHandle.toNativeUtf8();
|
||||
RejectInvite(u1, u1.length, u2, u2.length);
|
||||
LeaveGroup(u1, u1.length, u2, u2.length);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -405,4 +359,32 @@ class CwtchFfi implements Cwtch {
|
|||
final utf8handle = handle.toNativeUtf8();
|
||||
updateMessageFlags(utf8profile, utf8profile.length, utf8handle, utf8handle.length, index, flags);
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: non_constant_identifier_names
|
||||
void DeleteProfile(String onion, String currentPassword) {
|
||||
var deleteprofile = library.lookup<NativeFunction<string_string_to_void_function>>("c_DeleteProfile");
|
||||
// ignore: non_constant_identifier_names
|
||||
final DeleteProfile = deleteprofile.asFunction<VoidFromStringStringFn>();
|
||||
final u1 = onion.toNativeUtf8();
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
@ -82,24 +82,8 @@ class CwtchGomobile implements Cwtch {
|
|||
}
|
||||
|
||||
// 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});
|
||||
void DeleteProfile(String onion, String pass) {
|
||||
cwtchPlatform.invokeMethod("DeleteProfile", {"ProfileOnion": onion, "pass": pass});
|
||||
}
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
|
@ -108,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) {
|
||||
|
@ -140,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) {
|
||||
|
@ -179,7 +152,7 @@ class CwtchGomobile implements Cwtch {
|
|||
@override
|
||||
// ignore: non_constant_identifier_names
|
||||
void RejectInvite(String profileOnion, String groupHandle) {
|
||||
cwtchPlatform.invokeMethod("RejectInvite", {"ProfileOnion": profileOnion, "handle": groupHandle});
|
||||
cwtchPlatform.invokeMethod("RejectInvite", {"ProfileOnion": profileOnion, "groupHandle": groupHandle});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -190,7 +163,13 @@ class CwtchGomobile implements Cwtch {
|
|||
@override
|
||||
// ignore: non_constant_identifier_names
|
||||
void LeaveGroup(String profileOnion, String groupHandle) {
|
||||
cwtchPlatform.invokeMethod("LeaveGroup", {"ProfileOnion": profileOnion, "handle": groupHandle});
|
||||
cwtchPlatform.invokeMethod("LeaveGroup", {"ProfileOnion": profileOnion, "groupHandle": groupHandle});
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: non_constant_identifier_names
|
||||
void LeaveConversation(String profileOnion, String contactHandle) {
|
||||
cwtchPlatform.invokeMethod("LeaveConversation", {"ProfileOnion": profileOnion, "contactHandle": contactHandle});
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -198,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", {});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,10 @@ class ErrorHandler extends ChangeNotifier {
|
|||
bool importBundleError = false;
|
||||
bool importBundleSuccess = false;
|
||||
|
||||
static const String deleteProfileErrorPrefix = "deleteprofile";
|
||||
bool deleteProfileError = false;
|
||||
bool deleteProfileSuccess = false;
|
||||
|
||||
/// Called by the event bus.
|
||||
handleUpdate(String error) {
|
||||
var parts = error.split(".");
|
||||
|
@ -30,6 +34,9 @@ class ErrorHandler extends ChangeNotifier {
|
|||
case importBundleErrorPrefix:
|
||||
handleImportBundleError(errorType);
|
||||
break;
|
||||
case deleteProfileErrorPrefix:
|
||||
handleDeleteProfileError(errorType);
|
||||
break;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
|
@ -69,4 +76,19 @@ class ErrorHandler extends ChangeNotifier {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleDeleteProfileError(String errorType) {
|
||||
// Reset add contact errors
|
||||
deleteProfileError = false;
|
||||
deleteProfileSuccess = false;
|
||||
|
||||
switch (errorType) {
|
||||
case successErrorType:
|
||||
deleteProfileSuccess = true;
|
||||
break;
|
||||
default:
|
||||
deleteProfileError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,186 +1,193 @@
|
|||
{
|
||||
"@@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-24T23:32:06+02:00",
|
||||
"tooltipHidePassword": "Hide Password",
|
||||
"tooltipShowPassword": "Show Password",
|
||||
"serverNotSynced": "Syncing New Messages (This can take some time)...",
|
||||
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
|
||||
"shutdownCwtchAction": "Shutdown Cwtch",
|
||||
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
|
||||
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
|
||||
"shutdownCwtchTooltip": "Shutdown Cwtch",
|
||||
"malformedMessage": "Malformed message",
|
||||
"profileDeleteSuccess": "Successfully deleted profile",
|
||||
"debugLog": "Turn on console debug logging",
|
||||
"torNetworkStatus": "Tor network status",
|
||||
"addContactFirst": "Add or pick a contact to begin chatting.",
|
||||
"createProfileToBegin": "Please create or unlock a profile to begin",
|
||||
"nickChangeSuccess": "Profile nickname changed successfully",
|
||||
"addServerFirst": "You need to add a server before you can create a group",
|
||||
"deleteProfileSuccess": "Successfully deleted profile",
|
||||
"sendInvite": "Send a contact or group invite",
|
||||
"sendMessage": "Send Message",
|
||||
"cancel": "Cancel",
|
||||
"resetTor": "Reset",
|
||||
"torStatus": "Tor Status",
|
||||
"torVersion": "Tor Version",
|
||||
"sendAnInvitation": "You sent an invitation for: ",
|
||||
"contactSuggestion": "This is a contact suggestion for: ",
|
||||
"rejected": "Rejected!",
|
||||
"accepted": "Accepted!",
|
||||
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
|
||||
"newPassword": "New Password",
|
||||
"yesLeave": "Yes, Leave This Conversation",
|
||||
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
|
||||
"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",
|
||||
"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"
|
||||
}
|
|
@ -1,186 +1,193 @@
|
|||
{
|
||||
"@@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-24T23:32:06+02:00",
|
||||
"tooltipHidePassword": "Hide Password",
|
||||
"tooltipShowPassword": "Show Password",
|
||||
"serverNotSynced": "Syncing New Messages (This can take some time)...",
|
||||
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
|
||||
"shutdownCwtchAction": "Shutdown Cwtch",
|
||||
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
|
||||
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
|
||||
"shutdownCwtchTooltip": "Shutdown Cwtch",
|
||||
"malformedMessage": "Malformed message",
|
||||
"profileDeleteSuccess": "Successfully deleted profile",
|
||||
"debugLog": "Turn on console debug logging",
|
||||
"torNetworkStatus": "Tor network status",
|
||||
"addContactFirst": "Add or pick a contact to begin chatting.",
|
||||
"createProfileToBegin": "Please create or unlock a profile to begin",
|
||||
"nickChangeSuccess": "Profile nickname changed successfully",
|
||||
"addServerFirst": "You need to add a server before you can create a group",
|
||||
"deleteProfileSuccess": "Successfully deleted profile",
|
||||
"sendInvite": "Send a contact or group invite",
|
||||
"sendMessage": "Send Message",
|
||||
"cancel": "Cancel",
|
||||
"resetTor": "Reset",
|
||||
"torStatus": "Tor Status",
|
||||
"torVersion": "Tor Version",
|
||||
"sendAnInvitation": "You sent an invitation for: ",
|
||||
"contactSuggestion": "This is a contact suggestion for: ",
|
||||
"rejected": "Rejected!",
|
||||
"accepted": "Accepted!",
|
||||
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
|
||||
"newPassword": "New Password",
|
||||
"yesLeave": "Yes, Leave This Conversation",
|
||||
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
|
||||
"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",
|
||||
"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"
|
||||
}
|
|
@ -1,186 +1,193 @@
|
|||
{
|
||||
"@@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...",
|
||||
"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",
|
||||
"@@last_modified": "2021-06-24T23:32:06+02:00",
|
||||
"tooltipHidePassword": "Hide Password",
|
||||
"tooltipShowPassword": "Show Password",
|
||||
"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",
|
||||
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
|
||||
"shutdownCwtchAction": "Shutdown Cwtch",
|
||||
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
|
||||
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
|
||||
"shutdownCwtchTooltip": "Shutdown Cwtch",
|
||||
"malformedMessage": "Malformed message",
|
||||
"profileDeleteSuccess": "Successfully deleted profile",
|
||||
"debugLog": "Turn on console debug logging",
|
||||
"torNetworkStatus": "Tor network status",
|
||||
"addContactFirst": "Add or pick a contact to begin chatting.",
|
||||
"createProfileToBegin": "Please create or unlock a profile to begin",
|
||||
"nickChangeSuccess": "Profile nickname changed successfully",
|
||||
"addServerFirst": "You need to add a server before you can create a group",
|
||||
"deleteProfileSuccess": "Successfully deleted profile",
|
||||
"sendInvite": "Send a contact or group invite",
|
||||
"sendMessage": "Send Message",
|
||||
"cancel": "Cancel",
|
||||
"resetTor": "Reset",
|
||||
"torStatus": "Tor Status",
|
||||
"torVersion": "Tor Version",
|
||||
"sendAnInvitation": "You sent an invitation for: ",
|
||||
"contactSuggestion": "This is a contact suggestion for: ",
|
||||
"rejected": "Rejected!",
|
||||
"accepted": "Accepted!",
|
||||
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
|
||||
"newPassword": "New Password",
|
||||
"yesLeave": "Yes, Leave This Conversation",
|
||||
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
|
||||
"leaveGroup": "Leave This Conversation",
|
||||
"inviteToGroup": "You have been invited to join a group:",
|
||||
"pasteAddressToAddContact": "...pegar una dirección aquí para añadir contacto...",
|
||||
"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",
|
||||
"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"
|
||||
}
|
|
@ -1,186 +1,193 @@
|
|||
{
|
||||
"@@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-24T23:32:06+02:00",
|
||||
"tooltipHidePassword": "Hide Password",
|
||||
"tooltipShowPassword": "Show Password",
|
||||
"serverNotSynced": "Syncing New Messages (This can take some time)...",
|
||||
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
|
||||
"shutdownCwtchAction": "Shutdown Cwtch",
|
||||
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
|
||||
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
|
||||
"shutdownCwtchTooltip": "Shutdown Cwtch",
|
||||
"malformedMessage": "Malformed message",
|
||||
"profileDeleteSuccess": "Successfully deleted profile",
|
||||
"debugLog": "Turn on console debug logging",
|
||||
"torNetworkStatus": "Tor network status",
|
||||
"addContactFirst": "Add or pick a contact to begin chatting.",
|
||||
"createProfileToBegin": "Please create or unlock a profile to begin",
|
||||
"nickChangeSuccess": "Profile nickname changed successfully",
|
||||
"addServerFirst": "You need to add a server before you can create a group",
|
||||
"deleteProfileSuccess": "Successfully deleted profile",
|
||||
"sendInvite": "Send a contact or group invite",
|
||||
"sendMessage": "Send Message",
|
||||
"cancel": "Cancel",
|
||||
"resetTor": "Reset",
|
||||
"torStatus": "Tor Status",
|
||||
"torVersion": "Tor Version",
|
||||
"sendAnInvitation": "You sent an invitation for: ",
|
||||
"contactSuggestion": "This is a contact suggestion for: ",
|
||||
"rejected": "Rejected!",
|
||||
"accepted": "Accepted!",
|
||||
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
|
||||
"newPassword": "New Password",
|
||||
"yesLeave": "Yes, Leave This Conversation",
|
||||
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
|
||||
"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",
|
||||
"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"
|
||||
}
|
|
@ -1,186 +1,193 @@
|
|||
{
|
||||
"@@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...",
|
||||
"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",
|
||||
"@@last_modified": "2021-06-24T23:32:06+02:00",
|
||||
"tooltipHidePassword": "Hide Password",
|
||||
"tooltipShowPassword": "Show Password",
|
||||
"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",
|
||||
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
|
||||
"shutdownCwtchAction": "Shutdown Cwtch",
|
||||
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
|
||||
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
|
||||
"shutdownCwtchTooltip": "Shutdown Cwtch",
|
||||
"malformedMessage": "Malformed message",
|
||||
"profileDeleteSuccess": "Successfully deleted profile",
|
||||
"debugLog": "Turn on console debug logging",
|
||||
"torNetworkStatus": "Tor network status",
|
||||
"addContactFirst": "Add or pick a contact to begin chatting.",
|
||||
"createProfileToBegin": "Please create or unlock a profile to begin",
|
||||
"nickChangeSuccess": "Profile nickname changed successfully",
|
||||
"addServerFirst": "You need to add a server before you can create a group",
|
||||
"deleteProfileSuccess": "Successfully deleted profile",
|
||||
"sendInvite": "Send a contact or group invite",
|
||||
"sendMessage": "Send Message",
|
||||
"cancel": "Cancel",
|
||||
"resetTor": "Reset",
|
||||
"torStatus": "Tor Status",
|
||||
"torVersion": "Tor Version",
|
||||
"sendAnInvitation": "You sent an invitation for: ",
|
||||
"contactSuggestion": "This is a contact suggestion for: ",
|
||||
"rejected": "Rejected!",
|
||||
"accepted": "Accepted!",
|
||||
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
|
||||
"newPassword": "New Password",
|
||||
"yesLeave": "Yes, Leave This Conversation",
|
||||
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
|
||||
"leaveGroup": "Leave This Conversation",
|
||||
"inviteToGroup": "You have been invited to join a group:",
|
||||
"pasteAddressToAddContact": "... incolla qui un indirizzo per aggiungere un contatto...",
|
||||
"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",
|
||||
"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"
|
||||
}
|
|
@ -1,186 +1,193 @@
|
|||
{
|
||||
"@@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-24T23:32:06+02:00",
|
||||
"tooltipHidePassword": "Hide Password",
|
||||
"tooltipShowPassword": "Show Password",
|
||||
"serverNotSynced": "Syncing New Messages (This can take some time)...",
|
||||
"groupInviteSettingsWarning": "You have been invited to join a group! Please enable the Group Chat Experiment in Settings to view this Invitation.",
|
||||
"shutdownCwtchAction": "Shutdown Cwtch",
|
||||
"shutdownCwtchDialog": "Are you sure you want to shutdown Cwtch? This will close all connections, and exit the application.",
|
||||
"shutdownCwtchDialogTitle": "Shutdown Cwtch?",
|
||||
"shutdownCwtchTooltip": "Shutdown Cwtch",
|
||||
"malformedMessage": "Malformed message",
|
||||
"profileDeleteSuccess": "Successfully deleted profile",
|
||||
"debugLog": "Turn on console debug logging",
|
||||
"torNetworkStatus": "Tor network status",
|
||||
"addContactFirst": "Add or pick a contact to begin chatting.",
|
||||
"createProfileToBegin": "Please create or unlock a profile to begin",
|
||||
"nickChangeSuccess": "Profile nickname changed successfully",
|
||||
"addServerFirst": "You need to add a server before you can create a group",
|
||||
"deleteProfileSuccess": "Successfully deleted profile",
|
||||
"sendInvite": "Send a contact or group invite",
|
||||
"sendMessage": "Send Message",
|
||||
"cancel": "Cancel",
|
||||
"resetTor": "Reset",
|
||||
"torStatus": "Tor Status",
|
||||
"torVersion": "Tor Version",
|
||||
"sendAnInvitation": "You sent an invitation for: ",
|
||||
"contactSuggestion": "This is a contact suggestion for: ",
|
||||
"rejected": "Rejected!",
|
||||
"accepted": "Accepted!",
|
||||
"chatHistoryDefault": "This conversation will be deleted when Cwtch is closed! Message history can be enabled per-conversation via the Settings menu in the upper right.",
|
||||
"newPassword": "New Password",
|
||||
"yesLeave": "Yes, Leave This Conversation",
|
||||
"reallyLeaveThisGroupPrompt": "Are you sure you want to leave this conversation? All messages and attributes will be deleted.",
|
||||
"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",
|
||||
"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"
|
||||
}
|
|
@ -114,4 +114,7 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''');
|
||||
|
||||
|
||||
yield LicenseEntryWithLineBreaks(["flaticons"], "Icons made by Freepik (https://www.freepik.com) from Flaticon (www.flaticon.com)");
|
||||
}
|
||||
|
|
|
@ -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 globalSettings = Settings(Locale("en", ''), OpaqueDark());
|
||||
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,45 @@ 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 +92,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,18 +104,41 @@ 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 ? 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Invoked via notificationClickChannel by MyBroadcastReceiver in MainActivity.kt
|
||||
// coder beware: args["RemotePeer"] is actually a handle, and could be eg a groupID
|
||||
Future<void> _externalNotificationClicked(MethodCall call) async {
|
||||
var args = jsonDecode(call.arguments);
|
||||
var profile = profs.getProfile(args["ProfileOnion"])!;
|
||||
var contact = profile.contactList.getContact(args["RemotePeer"])!;
|
||||
contact.unreadMessages = 0;
|
||||
|
||||
// single pane mode pushes; double pane mode reads AppState.selectedProfile/Conversation
|
||||
var isLandscape = Provider.of<AppState>(navKey.currentContext!, listen: false).isLandscape(navKey.currentContext!);
|
||||
if (Provider.of<Settings>(navKey.currentContext!, listen: false).uiColumns(isLandscape).length == 1) {
|
||||
if (navKey.currentContext?.findAncestorWidgetOfExactType<MessageView>() != null) {
|
||||
print("messageview already open; popping before pushing replacement");
|
||||
navKey.currentState?.pop();
|
||||
}
|
||||
navKey.currentState?.push(
|
||||
MaterialPageRoute<void>(
|
||||
builder: (BuildContext builderContext) {
|
||||
|
@ -129,6 +152,10 @@ class FlwtchState extends State<Flwtch> {
|
|||
},
|
||||
),
|
||||
);
|
||||
} else { //dual pane
|
||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedProfile = args["ProfileOnion"];
|
||||
Provider.of<AppState>(navKey.currentContext!, listen: false).selectedConversation = args["RemotePeer"];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -13,9 +13,8 @@ import 'dart:typed_data';
|
|||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:glob/glob.dart';
|
||||
import 'package:glob/list_local_fs.dart';
|
||||
|
||||
var globalSettings = Settings(Locale("en", ''), Opaque.dark);
|
||||
var globalSettings = Settings(Locale("en", ''), OpaqueDark());
|
||||
var globalErrorHandler = ErrorHandler();
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -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(String onion, String name, String picture, String contactsJson, String serverJson, bool online, bool encrypted) {
|
||||
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, encrypted: encrypted));
|
||||
} else {
|
||||
_profiles[idx].updateFrom(onion, name, picture, contactsJson, serverJson, online);
|
||||
}
|
||||
|
||||
void add(ProfileInfoState newProfile) {
|
||||
_profiles.add(newProfile);
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,42 @@ class ProfileListState extends ChangeNotifier {
|
|||
int idx = _profiles.indexWhere((element) => element.onion == onion);
|
||||
return idx >= 0 ? _profiles[idx] : null;
|
||||
}
|
||||
|
||||
void delete(String onion) {
|
||||
_profiles.removeWhere((element) => element.onion == onion);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
class AppState extends ChangeNotifier {
|
||||
bool cwtchInit = false;
|
||||
String appError = "";
|
||||
String? _selectedProfile;
|
||||
String? _selectedConversation;
|
||||
|
||||
void SetCwtchInit() {
|
||||
cwtchInit = true;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void SetAppError(String error) {
|
||||
appError = error;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
String? get selectedProfile => _selectedProfile;
|
||||
set selectedProfile(String? newVal) {
|
||||
this._selectedProfile = newVal;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
String? get selectedConversation => _selectedConversation;
|
||||
set selectedConversation(String? newVal) {
|
||||
this._selectedConversation = newVal;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
bool isLandscape(BuildContext c) => MediaQuery.of(c).size.width > MediaQuery.of(c).size.height;
|
||||
}
|
||||
|
||||
class ContactListState extends ChangeNotifier {
|
||||
|
@ -141,6 +177,10 @@ class ProfileInfoState extends ChangeNotifier {
|
|||
int _unreadMessages = 0;
|
||||
bool _online = false;
|
||||
|
||||
// assume profiles are encrypted...this will be set to false
|
||||
// in the constructor if the profile is encrypted with the defacto password.
|
||||
bool _encrypted = true;
|
||||
|
||||
ProfileInfoState({
|
||||
required this.onion,
|
||||
nickname = "",
|
||||
|
@ -149,11 +189,13 @@ class ProfileInfoState extends ChangeNotifier {
|
|||
contactsJson = "",
|
||||
serversJson = "",
|
||||
online = false,
|
||||
encrypted = true,
|
||||
}) {
|
||||
this._nickname = nickname;
|
||||
this._imagePath = imagePath;
|
||||
this._unreadMessages = unreadMessages;
|
||||
this._online = online;
|
||||
this._encrypted = encrypted;
|
||||
|
||||
if (contactsJson != null && contactsJson != "" && contactsJson != "null") {
|
||||
List<dynamic> contacts = jsonDecode(contactsJson);
|
||||
|
@ -207,6 +249,9 @@ class ProfileInfoState extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
// Check encrypted status for profile info screen
|
||||
bool get isEncrypted => this._encrypted;
|
||||
|
||||
String get nickname => this._nickname;
|
||||
set nickname(String newValue) {
|
||||
this._nickname = newValue;
|
||||
|
@ -241,6 +286,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 {
|
||||
|
@ -357,7 +437,8 @@ class ContactInfoState extends ChangeNotifier {
|
|||
|
||||
bool isOnline() {
|
||||
if (this.isGroup == true) {
|
||||
return this.status == "Synced";
|
||||
// We now have an out of sync warning so we will mark these as online...
|
||||
return this.status == "Authenticated" || this.status == "Synced";
|
||||
} else {
|
||||
return this.status == "Authenticated";
|
||||
}
|
||||
|
@ -436,6 +517,8 @@ class MessageState extends ChangeNotifier {
|
|||
}
|
||||
|
||||
set loaded(bool newVal) {
|
||||
// quickly-arriving messages get discarded before loading sometimes
|
||||
if (!hasListeners) return;
|
||||
this._loaded = newVal;
|
||||
notifyListeners();
|
||||
}
|
||||
|
|
207
lib/opaque.dart
207
lib/opaque.dart
|
@ -10,6 +10,11 @@ import 'package:cwtch/settings.dart';
|
|||
|
||||
abstract class OpaqueThemeType {
|
||||
static final Color red = Color(0xFFFF0000);
|
||||
|
||||
String identifier() {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
Color backgroundMainColor() {
|
||||
return red;
|
||||
}
|
||||
|
@ -304,9 +309,15 @@ abstract class OpaqueThemeType {
|
|||
|
||||
// ... more to come
|
||||
|
||||
// Sizes
|
||||
|
||||
double contactOnionTextSize() {
|
||||
return 18;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CwtchDark extends OpaqueThemeType {
|
||||
class OpaqueDark extends OpaqueThemeType {
|
||||
static final Color darkGreyPurple = Color(0xFF281831);
|
||||
static final Color deepPurple = Color(0xFF422850);
|
||||
static final Color mauvePurple = Color(0xFF8E64A5);
|
||||
|
@ -319,6 +330,10 @@ class CwtchDark extends OpaqueThemeType {
|
|||
static final Color softGreen = Color(0xFFA0FFB0);
|
||||
static final Color softRed = Color(0xFFFFA0B0);
|
||||
|
||||
String identifier() {
|
||||
return "dark";
|
||||
}
|
||||
|
||||
Color backgroundMainColor() {
|
||||
return darkGreyPurple;
|
||||
}
|
||||
|
@ -612,7 +627,7 @@ class CwtchDark extends OpaqueThemeType {
|
|||
}
|
||||
}
|
||||
|
||||
class CwtchLight extends OpaqueThemeType {
|
||||
class OpaqueLight extends OpaqueThemeType {
|
||||
static final Color whitePurple = Color(0xFFFFFDFF);
|
||||
static final Color softPurple = Color(0xFFFDF3FC);
|
||||
static final Color purple = Color(0xFFDFB9DE);
|
||||
|
@ -625,6 +640,10 @@ class CwtchLight extends OpaqueThemeType {
|
|||
static final Color softGreen = Color(0xFFA0FFB0);
|
||||
static final Color softRed = Color(0xFFFFA0B0);
|
||||
|
||||
String identifier() {
|
||||
return "light";
|
||||
}
|
||||
|
||||
Color backgroundMainColor() {
|
||||
return whitePurple;
|
||||
}
|
||||
|
@ -918,6 +937,99 @@ class CwtchLight extends OpaqueThemeType {
|
|||
}
|
||||
}
|
||||
|
||||
ThemeData mkThemeData(Settings opaque) {
|
||||
return ThemeData(
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
primarySwatch: Colors.red,
|
||||
primaryIconTheme: IconThemeData(
|
||||
color: opaque.current().mainTextColor(),
|
||||
),
|
||||
primaryColor: opaque.current().backgroundMainColor(),
|
||||
canvasColor: opaque.current().backgroundPaneColor(),
|
||||
backgroundColor: opaque.current().backgroundMainColor(),
|
||||
highlightColor: opaque.current().hilightElementTextColor(),
|
||||
iconTheme: IconThemeData(
|
||||
color: opaque.current().mainTextColor(),
|
||||
),
|
||||
cardColor: opaque.current().backgroundMainColor(),
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: opaque.current().backgroundPaneColor(),
|
||||
titleTextStyle: TextStyle(
|
||||
color: opaque.current().mainTextColor(),
|
||||
),
|
||||
actionsIconTheme: IconThemeData(
|
||||
color: opaque.current().mainTextColor(),
|
||||
)),
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed, backgroundColor: opaque.current().backgroundHilightElementColor()),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()),
|
||||
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
|
||||
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
|
||||
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()),
|
||||
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
|
||||
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
|
||||
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
)),
|
||||
),
|
||||
),
|
||||
scrollbarTheme: ScrollbarThemeData(
|
||||
isAlwaysShown: false, thumbColor: MaterialStateProperty.all(opaque.current().scrollbarActiveColor()), trackColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor())),
|
||||
tabBarTheme: TabBarTheme(indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor()))),
|
||||
dialogTheme: DialogTheme(
|
||||
backgroundColor: opaque.current().backgroundPaneColor(),
|
||||
titleTextStyle: TextStyle(color: opaque.current().mainTextColor()),
|
||||
contentTextStyle: TextStyle(color: opaque.current().mainTextColor())),
|
||||
textTheme: TextTheme(
|
||||
headline1: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline2: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline3: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline4: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline5: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline6: TextStyle(color: opaque.current().mainTextColor()),
|
||||
bodyText1: TextStyle(color: opaque.current().mainTextColor()),
|
||||
bodyText2: TextStyle(color: opaque.current().mainTextColor()),
|
||||
subtitle1: TextStyle(color: opaque.current().mainTextColor()),
|
||||
subtitle2: TextStyle(color: opaque.current().mainTextColor()),
|
||||
caption: TextStyle(color: opaque.current().mainTextColor()),
|
||||
button: TextStyle(color: opaque.current().mainTextColor()),
|
||||
overline: TextStyle(color: opaque.current().mainTextColor())),
|
||||
switchTheme: SwitchThemeData(
|
||||
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
|
||||
thumbColor: MaterialStateProperty.all(opaque.current().mainTextColor()),
|
||||
trackColor: MaterialStateProperty.all(opaque.current().dropShadowColor()),
|
||||
),
|
||||
floatingActionButtonTheme: FloatingActionButtonThemeData(backgroundColor: opaque.current().defaultButtonColor(), hoverColor: opaque.current().defaultButtonActiveColor()),
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
cursorColor: opaque.current().defaultButtonActiveColor(), selectionColor: opaque.current().defaultButtonActiveColor(), selectionHandleColor: opaque.current().defaultButtonActiveColor()),
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
OpaqueThemeType _current = CwtchDark();
|
||||
|
||||
void setDark() {
|
||||
_current = CwtchDark();
|
||||
}
|
||||
|
||||
void setLight() {
|
||||
_current = CwtchLight();
|
||||
}
|
||||
|
||||
OpaqueThemeType current() {
|
||||
if (_current == null) {
|
||||
setDark();
|
||||
}
|
||||
return _current;
|
||||
}
|
||||
|
||||
class Opaque extends OpaqueThemeType {
|
||||
Color backgroundMainColor() {
|
||||
return current().backgroundMainColor();
|
||||
|
@ -1226,22 +1338,9 @@ class Opaque extends OpaqueThemeType {
|
|||
}
|
||||
|
||||
static late OpaqueThemeType _current;
|
||||
static final OpaqueThemeType dark = CwtchDark();
|
||||
static final OpaqueThemeType light = CwtchLight();
|
||||
static void setDark() {
|
||||
_current = dark;
|
||||
}
|
||||
//static final OpaqueThemeType dark = CwtchDark();
|
||||
//static final OpaqueThemeType light = CwtchLight();
|
||||
|
||||
static void setLight() {
|
||||
_current = light;
|
||||
}
|
||||
|
||||
static OpaqueThemeType current() {
|
||||
if (_current == null) {
|
||||
setDark();
|
||||
}
|
||||
return _current;
|
||||
}
|
||||
|
||||
int scale = 2;
|
||||
static final String gcdOS = "linux";
|
||||
|
@ -1341,76 +1440,4 @@ class Opaque extends OpaqueThemeType {
|
|||
}
|
||||
}
|
||||
|
||||
ThemeData mkThemeData(Settings opaque) {
|
||||
return ThemeData(
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
primarySwatch: Colors.red,
|
||||
primaryIconTheme: IconThemeData(
|
||||
color: opaque.current().mainTextColor(),
|
||||
),
|
||||
primaryColor: opaque.current().backgroundMainColor(),
|
||||
canvasColor: opaque.current().backgroundPaneColor(),
|
||||
backgroundColor: opaque.current().backgroundMainColor(),
|
||||
highlightColor: opaque.current().hilightElementTextColor(),
|
||||
iconTheme: IconThemeData(
|
||||
color: opaque.current().mainTextColor(),
|
||||
),
|
||||
cardColor: opaque.current().backgroundMainColor(),
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: opaque.current().backgroundPaneColor(),
|
||||
titleTextStyle: TextStyle(
|
||||
color: opaque.current().mainTextColor(),
|
||||
),
|
||||
actionsIconTheme: IconThemeData(
|
||||
color: opaque.current().mainTextColor(),
|
||||
)),
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed, backgroundColor: opaque.current().backgroundHilightElementColor()),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()),
|
||||
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
|
||||
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
|
||||
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(opaque.current().defaultButtonColor()),
|
||||
foregroundColor: MaterialStateProperty.all(opaque.current().defaultButtonTextColor()),
|
||||
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
|
||||
padding: MaterialStateProperty.all(EdgeInsets.all(20)),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(18.0),
|
||||
)),
|
||||
),
|
||||
),
|
||||
scrollbarTheme: ScrollbarThemeData(
|
||||
isAlwaysShown: false, thumbColor: MaterialStateProperty.all(opaque.current().scrollbarActiveColor()), trackColor: MaterialStateProperty.all(opaque.current().scrollbarDefaultColor())),
|
||||
tabBarTheme: TabBarTheme(indicator: UnderlineTabIndicator(borderSide: BorderSide(color: opaque.current().defaultButtonActiveColor()))),
|
||||
dialogTheme: DialogTheme(
|
||||
backgroundColor: opaque.current().backgroundPaneColor(),
|
||||
titleTextStyle: TextStyle(color: opaque.current().mainTextColor()),
|
||||
contentTextStyle: TextStyle(color: opaque.current().mainTextColor())),
|
||||
textTheme: TextTheme(
|
||||
headline1: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline2: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline3: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline4: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline5: TextStyle(color: opaque.current().mainTextColor()),
|
||||
headline6: TextStyle(color: opaque.current().mainTextColor()),
|
||||
bodyText1: TextStyle(color: opaque.current().mainTextColor()),
|
||||
bodyText2: TextStyle(color: opaque.current().mainTextColor()),
|
||||
subtitle1: TextStyle(color: opaque.current().mainTextColor()),
|
||||
subtitle2: TextStyle(color: opaque.current().mainTextColor()),
|
||||
caption: TextStyle(color: opaque.current().mainTextColor()),
|
||||
button: TextStyle(color: opaque.current().mainTextColor()),
|
||||
overline: TextStyle(color: opaque.current().mainTextColor())),
|
||||
switchTheme: SwitchThemeData(
|
||||
overlayColor: MaterialStateProperty.all(opaque.current().defaultButtonActiveColor()),
|
||||
thumbColor: MaterialStateProperty.all(opaque.current().mainTextColor()),
|
||||
trackColor: MaterialStateProperty.all(opaque.current().dropShadowColor()),
|
||||
),
|
||||
floatingActionButtonTheme: FloatingActionButtonThemeData(backgroundColor: opaque.current().defaultButtonColor(), hoverColor: opaque.current().defaultButtonActiveColor()),
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
cursorColor: opaque.current().defaultButtonActiveColor(), selectionColor: opaque.current().defaultButtonActiveColor(), selectionHandleColor: opaque.current().defaultButtonActiveColor()),
|
||||
);
|
||||
}
|
||||
*/
|
|
@ -9,6 +9,13 @@ import 'opaque.dart';
|
|||
|
||||
const TapirGroupsExperiment = "tapir-groups-experiment";
|
||||
|
||||
enum DualpaneMode {
|
||||
Single,
|
||||
Dual1to2,
|
||||
Dual1to4,
|
||||
CopyPortrait,
|
||||
}
|
||||
|
||||
/// Settings govern the *Globally* relevant settings like Locale, Theme and Experiments.
|
||||
/// We also provide access to the version information here as it is also accessed from the
|
||||
/// Settings Pane.
|
||||
|
@ -19,18 +26,20 @@ class Settings extends ChangeNotifier {
|
|||
// explicitly set experiments to false until told otherwise...
|
||||
bool experimentsEnabled = false;
|
||||
HashMap<String, bool> experiments = HashMap.identity();
|
||||
DualpaneMode _uiColumnModePortrait = DualpaneMode.Single;
|
||||
DualpaneMode _uiColumnModeLandscape = DualpaneMode.CopyPortrait;
|
||||
|
||||
late bool blockUnknownConnections;
|
||||
bool blockUnknownConnections = false;
|
||||
|
||||
/// Set the dark theme.
|
||||
void setDark() {
|
||||
theme = Opaque.dark;
|
||||
theme = OpaqueDark();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/// Set the Light theme.
|
||||
void setLight() {
|
||||
theme = Opaque.light;
|
||||
theme = OpaqueLight();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -73,6 +82,10 @@ class Settings extends ChangeNotifier {
|
|||
// Set the internal experiments map. Casting from the Map<dynamic, dynamic> that we get from JSON
|
||||
experiments = new HashMap<String, bool>.from(settings["Experiments"]);
|
||||
|
||||
// single pane vs dual pane preferences
|
||||
_uiColumnModePortrait = uiColumnModeFromString(settings["UIColumnModePortrait"]);
|
||||
_uiColumnModeLandscape = uiColumnModeFromString(settings["UIColumnModeLandscape"]);
|
||||
|
||||
// Push the experimental settings to Consumers of Settings
|
||||
notifyListeners();
|
||||
}
|
||||
|
@ -134,16 +147,62 @@ class Settings extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
|
||||
DualpaneMode get uiColumnModePortrait => _uiColumnModePortrait;
|
||||
set uiColumnModePortrait(DualpaneMode newval) {
|
||||
this._uiColumnModePortrait = newval;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
DualpaneMode get uiColumnModeLandscape => _uiColumnModeLandscape;
|
||||
set uiColumnModeLandscape(DualpaneMode newval) {
|
||||
this._uiColumnModeLandscape = newval;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
List<int> uiColumns(bool isLandscape) {
|
||||
var m = (!isLandscape || uiColumnModeLandscape == DualpaneMode.CopyPortrait) ? uiColumnModePortrait : uiColumnModeLandscape;
|
||||
switch(m) {
|
||||
case DualpaneMode.Single: return [1];
|
||||
case DualpaneMode.Dual1to2: return [1, 2];
|
||||
case DualpaneMode.Dual1to4: return [1, 4];
|
||||
}
|
||||
print("impossible column configuration: portrait/$uiColumnModePortrait landscape/$uiColumnModeLandscape");
|
||||
return [1];
|
||||
}
|
||||
|
||||
static List<DualpaneMode> uiColumnModeOptions(bool isLandscape) {
|
||||
if (isLandscape) return [DualpaneMode.CopyPortrait, DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4,];
|
||||
else return [DualpaneMode.Single, DualpaneMode.Dual1to2, DualpaneMode.Dual1to4];
|
||||
}
|
||||
|
||||
static DualpaneMode uiColumnModeFromString(String m) {
|
||||
switch(m) {
|
||||
case "DualpaneMode.Single": return DualpaneMode.Single;
|
||||
case "DualpaneMode.Dual1to2": return DualpaneMode.Dual1to2;
|
||||
case "DualpaneMode.Dual1to4": return DualpaneMode.Dual1to4;
|
||||
case "DualpaneMode.CopyPortrait": return DualpaneMode.CopyPortrait;
|
||||
}
|
||||
print("Error: ui requested translation of column mode [$m] which doesn't exist");
|
||||
return DualpaneMode.Single;
|
||||
}
|
||||
|
||||
static String uiColumnModeToString(DualpaneMode m) {
|
||||
// todo: translate
|
||||
switch(m) {
|
||||
case DualpaneMode.Single: return "Single";
|
||||
case DualpaneMode.Dual1to2: return "Double (1:2)";
|
||||
case DualpaneMode.Dual1to4: return "Double (1:4)";
|
||||
case DualpaneMode.CopyPortrait: return "Same as portrait mode setting";
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a default settings object.
|
||||
Settings(this.locale, this.theme);
|
||||
|
||||
/// Convert this Settings object to a JSON representation for serialization on the
|
||||
/// event bus.
|
||||
dynamic asJson() {
|
||||
var themeString = "light";
|
||||
if (theme == Opaque.dark) {
|
||||
themeString = "dark";
|
||||
}
|
||||
var themeString = theme.identifier();
|
||||
|
||||
return {
|
||||
"Locale": this.locale.languageCode,
|
||||
|
@ -153,7 +212,9 @@ class Settings extends ChangeNotifier {
|
|||
"ExperimentsEnabled": this.experimentsEnabled,
|
||||
"Experiments": experiments,
|
||||
"StateRootPane": 0,
|
||||
"FirstTime": false
|
||||
"FirstTime": false,
|
||||
"UIColumnModePortrait": uiColumnModePortrait.toString(),
|
||||
"UIColumnModeLandscape": uiColumnModeLandscape.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ class _AddContactViewState extends State<AddContactView> {
|
|||
Widget addGroupTab() {
|
||||
// TODO We should replace with with a "Paste in Server Key Bundle"
|
||||
if (Provider.of<ProfileInfoState>(context).serverList.servers.isEmpty) {
|
||||
return Text("You need to add a server before you can create a group.");
|
||||
return Text(AppLocalizations.of(context)!.addServerFirst);
|
||||
}
|
||||
|
||||
return Container(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -12,6 +13,7 @@ import 'package:provider/provider.dart';
|
|||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import '../cwtch_icons_icons.dart';
|
||||
import '../errorHandler.dart';
|
||||
import '../main.dart';
|
||||
import '../opaque.dart';
|
||||
import '../settings.dart';
|
||||
|
@ -100,7 +102,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
|||
),
|
||||
CwtchTextField(
|
||||
controller: ctrlrNick,
|
||||
autofocus: true,
|
||||
autofocus: false,
|
||||
labelText: AppLocalizations.of(context)!.yourDisplayName,
|
||||
validator: (value) {
|
||||
if (value.isEmpty) {
|
||||
|
@ -153,7 +155,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
|||
visible: usePassword,
|
||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
|
||||
Visibility(
|
||||
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty,
|
||||
visible: Provider.of<ProfileInfoState>(context, listen: false).onion.isNotEmpty && Provider.of<ProfileInfoState>(context).isEncrypted,
|
||||
child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
CwtchLabel(label: AppLocalizations.of(context)!.currentPasswordLabel),
|
||||
SizedBox(
|
||||
|
@ -163,9 +165,12 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
|||
controller: ctrlrOldPass,
|
||||
validator: (value) {
|
||||
// Password field can be empty when just updating the profile, not on creation
|
||||
if (Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
|
||||
if (Provider.of<ProfileInfoState>(context).isEncrypted && Provider.of<ProfileInfoState>(context, listen: false).onion.isEmpty && value.isEmpty && usePassword) {
|
||||
return AppLocalizations.of(context)!.passwordErrorEmpty;
|
||||
}
|
||||
if (Provider.of<ErrorHandler>(context).deleteProfileError == true) {
|
||||
return AppLocalizations.of(context)!.enterCurrentPasswordForDelete;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
|
@ -237,9 +242,7 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
|||
Tooltip(
|
||||
message: AppLocalizations.of(context)!.enterCurrentPasswordForDelete,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: checkCurrentPassword()
|
||||
? null
|
||||
: () {
|
||||
onPressed: () {
|
||||
showAlertDialog(context);
|
||||
},
|
||||
style: ElevatedButton.styleFrom(primary: theme.current().defaultButtonColor()),
|
||||
|
@ -307,37 +310,33 @@ class _AddEditProfileViewState extends State<AddEditProfileView> {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO Stub - wire this into a libCwtch call.
|
||||
bool checkCurrentPassword() {
|
||||
return ctrlrOldPass.value.text.isEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
showAlertDialog(BuildContext context) {
|
||||
showAlertDialog(BuildContext context) {
|
||||
// set up the buttons
|
||||
Widget cancelButton = TextButton(
|
||||
child: Text("Cancel"),
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
|
||||
foregroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonTextColor()),
|
||||
overlayColor: MaterialStateProperty.all(Opaque.current().defaultButtonActiveColor()),
|
||||
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // dismiss dialog
|
||||
},
|
||||
);
|
||||
Widget continueButton = TextButton(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonColor()),
|
||||
foregroundColor: MaterialStateProperty.all(Opaque.current().defaultButtonTextColor()),
|
||||
overlayColor: MaterialStateProperty.all(Opaque.current().defaultButtonActiveColor()),
|
||||
padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||
child: Text(AppLocalizations.of(context)!.deleteProfileConfirmBtn),
|
||||
onPressed: () {
|
||||
// TODO Actually Delete the Peer
|
||||
Navigator.of(context).pop(); // dismiss dialog
|
||||
var onion = Provider.of<ProfileInfoState>(context, listen: false).onion;
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.DeleteProfile(onion, ctrlrOldPass.value.text);
|
||||
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 500),
|
||||
() {
|
||||
if (globalErrorHandler.deleteProfileSuccess) {
|
||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.deleteProfileSuccess + ":" + onion));
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
Navigator.of(context).popUntil((route) => route.isFirst); // dismiss dialog
|
||||
} else {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// set up the AlertDialog
|
||||
AlertDialog alert = AlertDialog(
|
||||
|
@ -355,4 +354,5 @@ showAlertDialog(BuildContext context) {
|
|||
return alert;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import '../main.dart';
|
||||
import '../model.dart';
|
||||
import '../settings.dart';
|
||||
import 'contactsview.dart';
|
||||
import 'messageview.dart';
|
||||
|
||||
|
@ -14,24 +15,25 @@ class DoubleColumnView extends StatefulWidget {
|
|||
class _DoubleColumnViewState extends State<DoubleColumnView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var flwtch = Provider.of<FlwtchState>(context);
|
||||
var flwtch = Provider.of<AppState>(context);
|
||||
var cols = Provider.of<Settings>(context).uiColumns(true);
|
||||
return Flex(
|
||||
direction: Axis.horizontal,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
flex: flwtch.columns[0],
|
||||
flex: cols[0],
|
||||
child: ContactsView(
|
||||
key: widget.key,
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
flex: flwtch.columns[1],
|
||||
child: flwtch.selectedConversation == ""
|
||||
? Center(child: Text("pick a contact"))
|
||||
flex: cols[1],
|
||||
child: flwtch.selectedConversation == null
|
||||
? Card(child:Center(child: Text(AppLocalizations.of(context)!.addContactFirst)))
|
||||
: //dev
|
||||
MultiProvider(providers: [
|
||||
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context)),
|
||||
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation)!),
|
||||
ChangeNotifierProvider.value(value: Provider.of<ProfileInfoState>(context).contactList.getContact(flwtch.selectedConversation!)!),
|
||||
], child: Container(child: MessageView())),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -64,7 +64,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
}).toList())),
|
||||
SwitchListTile(
|
||||
title: Text(AppLocalizations.of(context)!.settingTheme, style: TextStyle(color: settings.current().mainTextColor())),
|
||||
value: settings.current() == Opaque.light,
|
||||
value: settings.current().identifier() == "light",
|
||||
onChanged: (bool value) {
|
||||
if (value) {
|
||||
settings.setLight();
|
||||
|
@ -80,24 +80,33 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
secondary: Icon(CwtchIcons.change_theme, color: settings.current().mainTextColor()),
|
||||
),
|
||||
ListTile(
|
||||
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns", style: TextStyle(color: settings.current().mainTextColor())),
|
||||
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns in Portrait Mode", style: TextStyle(color: settings.current().mainTextColor())),
|
||||
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()),
|
||||
trailing: DropdownButton(
|
||||
value: "Single",
|
||||
value: settings.uiColumnModePortrait.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
if (newValue == "Double (1:2)") {
|
||||
Provider.of<FlwtchState>(context).columns = [1, 2];
|
||||
} else if (newValue == "Double (1:4)") {
|
||||
Provider.of<FlwtchState>(context).columns = [1, 4];
|
||||
} else {
|
||||
Provider.of<FlwtchState>(context).columns = [1];
|
||||
}
|
||||
settings.uiColumnModePortrait = Settings.uiColumnModeFromString(newValue!);
|
||||
saveSettings(context);
|
||||
},
|
||||
// TODO: Only allow in landscape?
|
||||
items: (Platform.isAndroid ? ["Single"] : ["Single", "Double (1:2)", "Double (1:4)"]).map<DropdownMenuItem<String>>((String value) {
|
||||
items: Settings.uiColumnModeOptions(false).map<DropdownMenuItem<String>>((DualpaneMode value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
value: value.toString(),
|
||||
child: Text(Settings.uiColumnModeToString(value)),
|
||||
);
|
||||
}).toList())),
|
||||
ListTile(
|
||||
title: Text(/*AppLocalizations.of(context)!.settingLanguage*/ "UI Columns in Landscape Mode", style: TextStyle(color: settings.current().mainTextColor())),
|
||||
leading: Icon(Icons.table_chart, color: settings.current().mainTextColor()),
|
||||
trailing: DropdownButton(
|
||||
value: settings.uiColumnModeLandscape.toString(),
|
||||
onChanged: (String? newValue) {
|
||||
settings.uiColumnModeLandscape = Settings.uiColumnModeFromString(newValue!);
|
||||
saveSettings(context);
|
||||
},
|
||||
items: Settings.uiColumnModeOptions(true).map<DropdownMenuItem<String>>((DualpaneMode value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value.toString(),
|
||||
child: Text(Settings.uiColumnModeToString(value)),
|
||||
);
|
||||
}).toList())),
|
||||
SwitchListTile(
|
||||
|
@ -160,13 +169,7 @@ class _GlobalSettingsViewState extends State<GlobalSettingsView> {
|
|||
)),
|
||||
AboutListTile(
|
||||
icon: Icon(Icons.info, color: settings.current().mainTextColor()),
|
||||
applicationIcon: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Image(
|
||||
image: AssetImage("assets/knott.png"),
|
||||
width: 128,
|
||||
height: 128,
|
||||
)),
|
||||
applicationIcon: Padding(padding:EdgeInsets.all(5), child: Icon(CwtchIcons.cwtch_knott)),
|
||||
applicationName: "Cwtch (Flutter UI)",
|
||||
applicationVersion: AppLocalizations.of(context)!.versionBuilddate.replaceAll("%1", EnvironmentConfig.BUILD_VER).replaceAll("%2", EnvironmentConfig.BUILD_DATE),
|
||||
applicationLegalese: '\u{a9} 2021 Open Privacy Research Society',
|
||||
|
|
|
@ -159,7 +159,7 @@ class _GroupSettingsViewState extends State<GroupSettingsView> {
|
|||
showAlertDialog(BuildContext context) {
|
||||
// set up the buttons
|
||||
Widget cancelButton = TextButton(
|
||||
child: Text("Cancel"),
|
||||
child: Text(AppLocalizations.of(context)!.cancel),
|
||||
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // dismiss dialog
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:cwtch/cwtch_icons_icons.dart';
|
||||
import 'package:cwtch/widgets/profileimage.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cwtch/views/peersettingsview.dart';
|
||||
|
@ -41,11 +43,24 @@ class _MessageViewState extends State<MessageView> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var appState = Provider.of<AppState>(context);
|
||||
return WillPopScope(
|
||||
onWillPop: _onWillPop,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(Provider.of<ContactInfoState>(context).nickname),
|
||||
// setting leading to null makes it do the default behaviour; container() hides it
|
||||
leading: Provider.of<Settings>(context).uiColumns(appState.isLandscape(context)).length > 1 ? Container() : null,
|
||||
title: Row(children: [
|
||||
ProfileImage(
|
||||
imagePath: Provider.of<ContactInfoState>(context).imagePath,
|
||||
diameter: 42,
|
||||
border: Provider.of<Settings>(context).current().portraitOnlineBorderColor(),
|
||||
badgeTextColor: Colors.red,
|
||||
badgeColor: Colors.red,
|
||||
),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
),Text(Provider.of<ContactInfoState>(context).nickname)]),
|
||||
actions: [
|
||||
//IconButton(icon: Icon(Icons.chat), onPressed: _pushContactSettings),
|
||||
//IconButton(icon: Icon(Icons.list), onPressed: _pushContactSettings),
|
||||
|
@ -66,12 +81,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) {
|
||||
|
@ -129,7 +138,7 @@ class _MessageViewState extends State<MessageView> {
|
|||
child: TextFormField(
|
||||
key: Key('txtCompose'),
|
||||
controller: ctrlrCompose,
|
||||
autofocus: true,
|
||||
autofocus: !Platform.isAndroid,
|
||||
focusNode: focusNode,
|
||||
textInputAction: TextInputAction.send,
|
||||
onFieldSubmitted: _sendMessage,
|
||||
|
@ -139,11 +148,14 @@ class _MessageViewState extends State<MessageView> {
|
|||
enabled: true,
|
||||
prefixIcon: IconButton(
|
||||
icon: Icon(CwtchIcons.send_invite, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
|
||||
tooltip: "Send a contact or group invite",
|
||||
tooltip: AppLocalizations.of(context)!.sendInvite,
|
||||
enableFeedback: true,
|
||||
splashColor: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
hoverColor: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
onPressed: () => _modalSendInvitation(context)),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(CwtchIcons.send_24px, size: 24, color: Provider.of<Settings>(context).theme.mainTextColor()),
|
||||
tooltip: "Send Message",
|
||||
tooltip: AppLocalizations.of(context)!.sendMessage,
|
||||
onPressed: _sendMessage,
|
||||
),
|
||||
))),
|
||||
|
@ -190,7 +202,9 @@ class _MessageViewState extends State<MessageView> {
|
|||
ElevatedButton(
|
||||
child: Text(AppLocalizations.of(bcontext)!.inviteBtn, semanticsLabel: AppLocalizations.of(bcontext)!.inviteBtn),
|
||||
onPressed: () {
|
||||
if (this.selectedContact != "") {
|
||||
this._sendInvitation();
|
||||
}
|
||||
Navigator.pop(bcontext);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -77,8 +77,7 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
|
|||
};
|
||||
final setPeerAttributeJson = jsonEncode(setPeerAttribute);
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.SendProfileEvent(profileOnion, setPeerAttributeJson);
|
||||
// todo translations
|
||||
final snackBar = SnackBar(content: Text("Nickname changed successfully"));
|
||||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.nickChangeSuccess));
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
},
|
||||
icon: Icon(Icons.save),
|
||||
|
@ -187,6 +186,22 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
|
|||
);
|
||||
}).toList())),
|
||||
]),
|
||||
Column(mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [
|
||||
SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Row(crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.end, children: [
|
||||
Tooltip(
|
||||
message: AppLocalizations.of(context)!.leaveGroup,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
showAlertDialog(context);
|
||||
},
|
||||
icon: Icon(CwtchIcons.leave_chat),
|
||||
label: Text(AppLocalizations.of(context)!.leaveGroup),
|
||||
))
|
||||
])
|
||||
]),
|
||||
])))));
|
||||
});
|
||||
});
|
||||
|
@ -197,4 +212,44 @@ class _PeerSettingsViewState extends State<PeerSettingsView> {
|
|||
final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedClipboardNotification));
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
}
|
||||
|
||||
showAlertDialog(BuildContext context) {
|
||||
// set up the buttons
|
||||
Widget cancelButton = TextButton(
|
||||
child: Text("Cancel"),
|
||||
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(); // dismiss dialog
|
||||
},
|
||||
);
|
||||
Widget continueButton = TextButton(
|
||||
style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
|
||||
child: Text(AppLocalizations.of(context)!.yesLeave),
|
||||
onPressed: () {
|
||||
var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
|
||||
var handle = Provider.of<ContactInfoState>(context, listen: false).onion;
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.LeaveConversation(profileOnion, handle);
|
||||
Future.delayed(Duration(milliseconds: 500), () {
|
||||
Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// set up the AlertDialog
|
||||
AlertDialog alert = AlertDialog(
|
||||
title: Text(AppLocalizations.of(context)!.reallyLeaveThisGroupPrompt),
|
||||
actions: [
|
||||
cancelButton,
|
||||
continueButton,
|
||||
],
|
||||
);
|
||||
|
||||
// show the dialog
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return alert;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
@ -38,8 +42,12 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
|
|||
// Prevents Android back button from closing the app on the profile manager screen
|
||||
// (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,
|
||||
builder: (context, settings, child) =>
|
||||
WillPopScope(
|
||||
onWillPop: () async {
|
||||
_showShutdown();
|
||||
return closeApp;
|
||||
},
|
||||
child: Scaffold(
|
||||
backgroundColor: settings.theme.backgroundMainColor(),
|
||||
appBar: AppBar(
|
||||
|
@ -51,7 +59,10 @@ class _ProfileMgrViewState extends State<ProfileMgrView> {
|
|||
width: 32,
|
||||
height: 32,
|
||||
colorBlendMode: BlendMode.dstIn,
|
||||
color: Provider.of<Settings>(context).theme.backgroundHilightElementColor(),
|
||||
color: Provider
|
||||
.of<Settings>(context)
|
||||
.theme
|
||||
.backgroundHilightElementColor(),
|
||||
),
|
||||
SizedBox(
|
||||
width: 10,
|
||||
|
@ -86,9 +97,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 +108,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() {
|
||||
|
|
|
@ -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/core/knott-white.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")),
|
||||
])),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class _TorStatusView extends State<TorStatusView> {
|
|||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text("Tor Network Status"),
|
||||
title: Text(AppLocalizations.of(context)!.torNetworkStatus),
|
||||
),
|
||||
body: _buildSettingsList(),
|
||||
);
|
||||
|
@ -43,15 +43,19 @@ class _TorStatusView extends State<TorStatusView> {
|
|||
child: Column(children: [
|
||||
ListTile(
|
||||
leading: TorIcon(),
|
||||
title: Text("Tor Status"),
|
||||
title: Text(AppLocalizations.of(context)!.torStatus),
|
||||
subtitle: Text(torStatus.progress == 100 ? AppLocalizations.of(context)!.networkStatusOnline : torStatus.status),
|
||||
trailing: ElevatedButton(
|
||||
child: Text("Reset"),
|
||||
child: Text(AppLocalizations.of(context)!.resetTor),
|
||||
onPressed: () {
|
||||
Provider.of<FlwtchState>(context, listen: false).cwtch.ResetTor();
|
||||
},
|
||||
),
|
||||
)
|
||||
),
|
||||
ListTile(
|
||||
title: Text(AppLocalizations.of(context)!.torVersion),
|
||||
subtitle: Text(torStatus.version),
|
||||
),
|
||||
]))));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:cwtch/views/profilemgrview.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import '../main.dart';
|
||||
import '../model.dart';
|
||||
import '../settings.dart';
|
||||
import 'contactsview.dart';
|
||||
import 'messageview.dart';
|
||||
|
||||
// currently unused but maybe one day?
|
||||
class TripleColumnView extends StatefulWidget {
|
||||
@override
|
||||
_TripleColumnViewState createState() => _TripleColumnViewState();
|
||||
|
@ -14,20 +17,23 @@ class TripleColumnView extends StatefulWidget {
|
|||
class _TripleColumnViewState extends State<TripleColumnView> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var flwtch = Provider.of<FlwtchState>(context);
|
||||
var appState = Provider.of<AppState>(context);
|
||||
var settings = Provider.of<Settings>(context);
|
||||
var columns = settings.uiColumns(appState.isLandscape(context));
|
||||
|
||||
return Flex(direction: Axis.horizontal, children: <Widget>[
|
||||
Flexible(
|
||||
flex: flwtch.columns[0],
|
||||
flex: columns[0],
|
||||
child: ProfileMgrView(),
|
||||
),
|
||||
Flexible(
|
||||
flex: flwtch.columns[1],
|
||||
child: flwtch.selectedProfile == null ? Center(child: Text("pick a profile")) : ContactsView(), //dev
|
||||
flex: columns[1],
|
||||
child: appState.selectedProfile == null ? Center(child: Text(AppLocalizations.of(context)!.createProfileToBegin)) : ContactsView(), //dev
|
||||
),
|
||||
Flexible(
|
||||
flex: flwtch.columns[2],
|
||||
child: flwtch.selectedConversation == ""
|
||||
? Center(child: Text("pick a contact"))
|
||||
flex: columns[2],
|
||||
child: appState.selectedConversation == null
|
||||
? Center(child: Text(AppLocalizations.of(context)!.addContactFirst))
|
||||
: //dev
|
||||
Container(child: MessageView()),
|
||||
),
|
||||
|
|
|
@ -17,6 +17,18 @@ class CwtchButtonTextField extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
|
||||
late final FocusNode _focusNode;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_focusNode = FocusNode();
|
||||
_focusNode.addListener(() {
|
||||
// Select all...
|
||||
if (_focusNode.hasFocus) widget.controller.selection = TextSelection(baseOffset: 0, extentOffset: widget.controller.text.length);
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (context, theme, child) {
|
||||
|
@ -24,6 +36,7 @@ class _CwtchButtonTextFieldState extends State<CwtchButtonTextField> {
|
|||
controller: widget.controller,
|
||||
readOnly: widget.readonly,
|
||||
showCursor: !widget.readonly,
|
||||
focusNode: _focusNode,
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: IconButton(
|
||||
onPressed: widget.onPressed,
|
||||
|
|
|
@ -20,6 +20,8 @@ class _ContactRowState extends State<ContactRow> {
|
|||
var contact = Provider.of<ContactInfoState>(context);
|
||||
return Card(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
color: Provider.of<AppState>(context).selectedConversation == contact.onion ? Provider.of<Settings>(context).theme.backgroundHilightElementColor() : null,
|
||||
borderOnForeground: false,
|
||||
child: InkWell(
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
|
||||
Padding(
|
||||
|
@ -31,7 +33,7 @@ class _ContactRowState extends State<ContactRow> {
|
|||
diameter: 64.0,
|
||||
imagePath: contact.imagePath,
|
||||
maskOut: !contact.isOnline(),
|
||||
border: contact.isOnline() ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor()),
|
||||
border: contact.isOnline() ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor()),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
|
@ -41,11 +43,14 @@ class _ContactRowState extends State<ContactRow> {
|
|||
children: [
|
||||
Text(
|
||||
contact.nickname, //(contact.isInvitation ? "invite " : "non-invite ") + (contact.isBlocked ? "blokt" : "nonblokt"),//
|
||||
style: Provider.of<FlwtchState>(context).biggerFont,
|
||||
|
||||
style: TextStyle(fontSize: Provider.of<Settings>(context).theme.contactOnionTextSize(),
|
||||
color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor()), //Provider.of<FlwtchState>(context).biggerFont,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
Text(contact.onion),
|
||||
Text(contact.onion,
|
||||
style: TextStyle(color: contact.isBlocked ? Provider.of<Settings>(context).theme.portraitBlockedTextColor() : Provider.of<Settings>(context).theme.mainTextColor())),
|
||||
],
|
||||
))),
|
||||
Padding(
|
||||
|
@ -77,17 +82,19 @@ class _ContactRowState extends State<ContactRow> {
|
|||
]),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
var flwtch = Provider.of<FlwtchState>(context, listen: false);
|
||||
flwtch.setState(() => flwtch.selectedConversation = contact.onion);
|
||||
// case 2/3 handled by Double/TripleColumnView respectively
|
||||
if (flwtch.columns.length == 1) _pushMessageView(contact.onion);
|
||||
// requery instead of using contactinfostate directly because sometimes listview gets confused about data that resorts
|
||||
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(contact.onion)!.unreadMessages = 0;
|
||||
// triggers update in Double/TripleColumnView
|
||||
Provider.of<AppState>(context, listen: false).selectedConversation = contact.onion;
|
||||
// if in singlepane mode, push to the stack
|
||||
var isLandscape = Provider.of<AppState>(context, listen: false).isLandscape(context);
|
||||
if (Provider.of<Settings>(context, listen: false).uiColumns(isLandscape).length == 1) _pushMessageView(contact.onion);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
void _pushMessageView(String handle) {
|
||||
Provider.of<ProfileInfoState>(context, listen: false).contactList.getContact(handle)!.unreadMessages = 0;
|
||||
var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
|
|
|
@ -14,7 +14,6 @@ import 'messagebubbledecorations.dart';
|
|||
|
||||
// Like MessageBubble but for displaying chat overlay 100/101 invitations
|
||||
// Offers the user an accept/reject button if they don't have a matching contact already
|
||||
// todo: Reject buttons currently aren't tracked and will reset when the message is reloaded
|
||||
class InvitationBubble extends StatefulWidget {
|
||||
@override
|
||||
InvitationBubbleState createState() => InvitationBubbleState();
|
||||
|
@ -23,15 +22,19 @@ class InvitationBubble extends StatefulWidget {
|
|||
class InvitationBubbleState extends State<InvitationBubble> {
|
||||
bool rejected = false;
|
||||
bool isAccepted = false;
|
||||
FocusNode _focus = FocusNode();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (Provider.of<MessageState>(context).malformed) {
|
||||
return MalformedBubble();
|
||||
}
|
||||
|
||||
var fromMe = Provider.of<MessageState>(context).senderOnion == Provider.of<ProfileInfoState>(context).onion;
|
||||
var isGroup = Provider.of<MessageState>(context).overlay == 101;
|
||||
isAccepted = Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).inviteTarget) != null;
|
||||
var prettyDate = "";
|
||||
var borderRadiousEh = 15.0;
|
||||
var showGroupInvite = Provider.of<Settings>(context).isExperimentEnabled(TapirGroupsExperiment);
|
||||
rejected = Provider.of<MessageState>(context).flags & 0x01 == 0x01;
|
||||
var myKey = Provider.of<MessageState>(context).profileOnion + "::" + Provider.of<MessageState>(context).contactHandle + "::" + Provider.of<MessageState>(context).messageIndex.toString();
|
||||
|
||||
|
@ -63,14 +66,18 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
|||
return MalformedBubble();
|
||||
}
|
||||
|
||||
var wdgMessage = fromMe
|
||||
var wdgMessage = isGroup && !showGroupInvite ?
|
||||
Text(AppLocalizations.of(context)!.groupInviteSettingsWarning) :
|
||||
fromMe
|
||||
? senderInviteChrome(AppLocalizations.of(context)!.sendAnInvitation,
|
||||
isGroup ? Provider.of<ProfileInfoState>(context).contactList.getContact(Provider.of<MessageState>(context).inviteTarget)!.nickname : Provider.of<MessageState>(context).message, myKey)
|
||||
: (inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : AppLocalizations.of(context)!.contactSuggestion, Provider.of<MessageState>(context).inviteNick,
|
||||
Provider.of<MessageState>(context).inviteTarget, myKey));
|
||||
|
||||
Widget wdgDecorations;
|
||||
if (fromMe) {
|
||||
if (isGroup && !showGroupInvite) {
|
||||
wdgDecorations = Text('\u202F');
|
||||
} else if (fromMe) {
|
||||
wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageState>(context).ackd, errored: Provider.of<MessageState>(context).error, fromMe: fromMe, prettyDate: prettyDate);
|
||||
} else if (isAccepted) {
|
||||
wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F');
|
||||
|
@ -106,7 +113,7 @@ class InvitationBubbleState extends State<InvitationBubble> {
|
|||
child: Padding(
|
||||
padding: EdgeInsets.all(9.0),
|
||||
child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [
|
||||
Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(CwtchIcons.send_invite, size: 32))),
|
||||
Center(widthFactor: 1, child: Padding(padding: EdgeInsets.all(10.0), child: Icon(isGroup && !showGroupInvite ? CwtchIcons.enable_experiments : CwtchIcons.send_invite, size: 32))),
|
||||
Center(
|
||||
widthFactor: 1.0,
|
||||
child: Column(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:cwtch/cwtch_icons_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
final Color malformedColor = Color(0xFFE85DA1);
|
||||
|
||||
|
@ -45,7 +46,7 @@ class MalformedBubbleState extends State<MalformedBubble> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [Text("Malformed Message")],
|
||||
children: [Text(AppLocalizations.of(context)!.malformedMessage)],
|
||||
))
|
||||
])))));
|
||||
});
|
||||
|
|
|
@ -19,6 +19,8 @@ class _MessageListState extends State<MessageList> {
|
|||
bool showEphemeralWarning = (Provider.of<ContactInfoState>(context).isGroup == false && Provider.of<ContactInfoState>(context).savePeerHistory != "SaveHistory");
|
||||
bool showOfflineWarning = Provider.of<ContactInfoState>(context).isOnline() == false;
|
||||
bool showMessageWarning = showEphemeralWarning || showOfflineWarning;
|
||||
bool showSyncing = Provider.of<ContactInfoState>(context).isGroup == true && Provider.of<ContactInfoState>(context).status != "Synced";
|
||||
|
||||
return RepaintBoundary(
|
||||
child: Container(
|
||||
child: Column(children: [
|
||||
|
@ -27,7 +29,10 @@ class _MessageListState extends State<MessageList> {
|
|||
child: Container(
|
||||
padding: EdgeInsets.all(5.0),
|
||||
color: Provider.of<Settings>(context).theme.defaultButtonActiveColor(),
|
||||
child: showOfflineWarning
|
||||
child: showSyncing ?
|
||||
Text(AppLocalizations.of(context)!.serverNotSynced,
|
||||
textAlign: TextAlign.center)
|
||||
: showOfflineWarning
|
||||
? Text(Provider.of<ContactInfoState>(context).isGroup ? AppLocalizations.of(context)!.serverConnectivityDisconnected : AppLocalizations.of(context)!.peerOfflineMessage,
|
||||
textAlign: TextAlign.center)
|
||||
// Only show the ephemeral status for peer conversations, not for groups...
|
||||
|
@ -50,6 +55,7 @@ class _MessageListState extends State<MessageList> {
|
|||
alignment: Alignment.center,
|
||||
image: AssetImage("assets/core/negative_heart_512px.png"),
|
||||
colorFilter: ColorFilter.mode(Provider.of<Settings>(context).theme.hilightElementTextColor(), BlendMode.srcIn))),
|
||||
// Don't load messages for syncing server...
|
||||
child: ListView.builder(
|
||||
controller: ctrlr1,
|
||||
itemCount: Provider.of<ContactInfoState>(outerContext).totalMessages,
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:cwtch/cwtch_icons_icons.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../settings.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
// Provides a styled Password Input Field for use in Form Widgets.
|
||||
// Callers must provide a text controller, label helper text and a validator.
|
||||
|
@ -22,9 +23,9 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// todo: translations
|
||||
var label = "View Password";
|
||||
var label = AppLocalizations.of(context)!.tooltipShowPassword;
|
||||
if (!obscureText) {
|
||||
label = "Hide Password";
|
||||
label = AppLocalizations.of(context)!.tooltipHidePassword;
|
||||
}
|
||||
|
||||
return Consumer<Settings>(builder: (context, theme, child) {
|
||||
|
@ -33,6 +34,7 @@ class _CwtchTextFieldState extends State<CwtchPasswordField> {
|
|||
controller: widget.controller,
|
||||
validator: widget.validator,
|
||||
obscureText: obscureText,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
onFieldSubmitted: widget.action,
|
||||
textInputAction: TextInputAction.unspecified,
|
||||
enableSuggestions: false,
|
||||
|
|
|
@ -38,7 +38,7 @@ class _ProfileImageState extends State<ProfileImage> {
|
|||
filterQuality: FilterQuality.medium,
|
||||
// We need some theme specific blending here...we might want to consider making this a theme level attribute
|
||||
colorBlendMode: !widget.maskOut
|
||||
? Provider.of<Settings>(context).theme == Opaque.dark
|
||||
? Provider.of<Settings>(context).theme.identifier() == "dark"
|
||||
? BlendMode.softLight
|
||||
: BlendMode.darken
|
||||
: BlendMode.srcOut,
|
||||
|
|
|
@ -30,18 +30,32 @@ class _ProfileRowState extends State<ProfileRow> {
|
|||
padding: const EdgeInsets.all(2.0), //border size
|
||||
child: ProfileImage(
|
||||
badgeCount: 0,
|
||||
badgeColor: Provider.of<Settings>(context).theme.portraitProfileBadgeColor(),
|
||||
badgeTextColor: Provider.of<Settings>(context).theme.portraitProfileBadgeTextColor(),
|
||||
badgeColor: Provider
|
||||
.of<Settings>(context)
|
||||
.theme
|
||||
.portraitProfileBadgeColor(),
|
||||
badgeTextColor: Provider
|
||||
.of<Settings>(context)
|
||||
.theme
|
||||
.portraitProfileBadgeTextColor(),
|
||||
diameter: 64.0,
|
||||
imagePath: profile.imagePath,
|
||||
border: profile.isOnline ? Provider.of<Settings>(context).theme.portraitOnlineBorderColor() : Provider.of<Settings>(context).theme.portraitOfflineBorderColor())),
|
||||
border: profile.isOnline ? Provider
|
||||
.of<Settings>(context)
|
||||
.theme
|
||||
.portraitOnlineBorderColor() : Provider
|
||||
.of<Settings>(context)
|
||||
.theme
|
||||
.portraitOfflineBorderColor())),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
profile.nickname,
|
||||
semanticsLabel: profile.nickname,
|
||||
style: Provider.of<FlwtchState>(context).biggerFont,
|
||||
style: Provider
|
||||
.of<FlwtchState>(context)
|
||||
.biggerFont,
|
||||
softWrap: true,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
@ -58,57 +72,54 @@ class _ProfileRowState extends State<ProfileRow> {
|
|||
tooltip: AppLocalizations.of(context)!.editProfile + " " + profile.nickname,
|
||||
icon: Icon(Icons.create, color: Provider.of<Settings>(context).current().mainTextColor()),
|
||||
onPressed: () {
|
||||
_pushAddEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath);
|
||||
_pushAddEditProfile(onion: profile.onion, displayName: profile.nickname, profileImage: profile.imagePath, encrypted: profile.isEncrypted);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
var flwtch = Provider.of<FlwtchState>(context, listen: false);
|
||||
flwtch.cwtch.SelectProfile(profile.onion);
|
||||
flwtch.setState(() {
|
||||
flwtch.selectedProfile = profile;
|
||||
flwtch.selectedConversation = "";
|
||||
});
|
||||
var appState = Provider.of<AppState>(context, listen: false);
|
||||
appState.selectedProfile = profile.onion;
|
||||
appState.selectedConversation = null;
|
||||
|
||||
switch (flwtch.columns.length) {
|
||||
case 1:
|
||||
_pushContactList(profile, false);
|
||||
break;
|
||||
case 2:
|
||||
_pushContactList(profile, true);
|
||||
break;
|
||||
} // case 3: handled by TripleColumnView
|
||||
_pushContactList(profile, appState.isLandscape(context));//orientation == Orientation.landscape);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
void _pushContactList(ProfileInfoState profile, bool includeDoublePane) {
|
||||
void _pushContactList(ProfileInfoState profile, bool isLandscape) {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute<void>(
|
||||
settings: RouteSettings(name: "conversations"),
|
||||
builder: (BuildContext buildcontext) {
|
||||
return OrientationBuilder(
|
||||
builder: (orientationBuilderContext, orientation) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider<ProfileInfoState>.value(value: profile),
|
||||
ChangeNotifierProvider<ContactListState>.value(value: profile.contactList),
|
||||
],
|
||||
builder: (context, widget) => includeDoublePane ? DoubleColumnView() : ContactsView(),
|
||||
builder: (innercontext, widget) {
|
||||
var appState = Provider.of<AppState>(context);
|
||||
var settings = Provider.of<Settings>(context);
|
||||
return settings.uiColumns(appState.isLandscape(innercontext)).length > 1 ? DoubleColumnView() : ContactsView();
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _pushAddEditProfile({onion: "", displayName: "", profileImage: ""}) {
|
||||
void _pushAddEditProfile({onion: "", displayName: "", profileImage: "", encrypted: true}) {
|
||||
Navigator.of(context).push(MaterialPageRoute<void>(
|
||||
builder: (BuildContext context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider<ProfileInfoState>(
|
||||
create: (_) => ProfileInfoState(onion: onion, nickname: displayName, imagePath: profileImage),
|
||||
create: (_) => ProfileInfoState(onion: onion, nickname: displayName, imagePath: profileImage, encrypted: encrypted),
|
||||
),
|
||||
],
|
||||
builder: (context, widget) => AddEditProfileView(),
|
||||
|
|
|
@ -7,7 +7,7 @@ doNothing(String x) {}
|
|||
// Provides a styled Text Field for use in Form Widgets.
|
||||
// Callers must provide a text controller, label helper text and a validator.
|
||||
class CwtchTextField extends StatefulWidget {
|
||||
CwtchTextField({required this.controller, required this.labelText, this.validator = null, this.autofocus = false, this.onChanged = doNothing});
|
||||
CwtchTextField({required this.controller, required this.labelText, this.validator, this.autofocus = false, this.onChanged = doNothing});
|
||||
final TextEditingController controller;
|
||||
final String labelText;
|
||||
final FormFieldValidator? validator;
|
||||
|
@ -19,6 +19,18 @@ class CwtchTextField extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _CwtchTextFieldState extends State<CwtchTextField> {
|
||||
late final FocusNode _focusNode;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_focusNode = FocusNode();
|
||||
_focusNode.addListener(() {
|
||||
// Select all...
|
||||
if (_focusNode.hasFocus) widget.controller.selection = TextSelection(baseOffset: 0, extentOffset: widget.controller.text.length);
|
||||
});
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer<Settings>(builder: (context, theme, child) {
|
||||
|
@ -27,6 +39,7 @@ class _CwtchTextFieldState extends State<CwtchTextField> {
|
|||
validator: widget.validator,
|
||||
onChanged: widget.onChanged,
|
||||
autofocus: widget.autofocus,
|
||||
focusNode: _focusNode,
|
||||
decoration: InputDecoration(
|
||||
labelText: widget.labelText,
|
||||
labelStyle: TextStyle(color: theme.current().mainTextColor(), backgroundColor: theme.current().textfieldBackgroundColor()),
|
||||
|
|
|
@ -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;
|
|
@ -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
|
|
@ -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
|
|
@ -6,10 +6,6 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <window_size/window_size_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) window_size_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowSizePlugin");
|
||||
window_size_plugin_register_with_registrar(window_size_registrar);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
window_size
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
|
|
86
pubspec.lock
86
pubspec.lock
|
@ -1,13 +1,13 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
archive:
|
||||
ansicolor:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
name: ansicolor
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
version: "2.0.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -42,7 +42,7 @@ packages:
|
|||
name: charcode
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.3.1"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -57,13 +57,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -111,11 +104,6 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_driver:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_localizations:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -131,11 +119,6 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
fuchsia_remote_debug_protocol:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
glob:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -157,13 +140,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
integration_test:
|
||||
dependency: "direct main"
|
||||
injector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: integration_test
|
||||
name: injector
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.2+3"
|
||||
version: "2.0.0"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -192,6 +175,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
msix:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: msix
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -213,6 +203,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -372,13 +369,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
sync_http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sync_http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -392,7 +382,7 @@ packages:
|
|||
name: test_api
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
version: "0.4.1"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -407,20 +397,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.2.0"
|
||||
webdriver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webdriver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -428,15 +404,6 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
window_size:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "plugins/window_size"
|
||||
ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
|
||||
resolved-ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
|
||||
url: "git://github.com/google/flutter-desktop-embedding.git"
|
||||
source: git
|
||||
version: "0.1.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -451,6 +418,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.1.2"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
sdks:
|
||||
dart: ">=2.13.0 <3.0.0"
|
||||
flutter: ">=1.20.0"
|
||||
|
|
42
pubspec.yaml
42
pubspec.yaml
|
@ -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+15
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
@ -37,30 +37,18 @@ dependencies:
|
|||
desktop_notifications: 0.5.0
|
||||
|
||||
glob: any
|
||||
# todo: flutter_driver causes version conflict. eg https://github.com/flutter/flutter/issues/44829
|
||||
# testing-related deps
|
||||
integration_test: ^1.0.0
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_driver:
|
||||
sdk: flutter
|
||||
|
||||
window_size:
|
||||
git:
|
||||
url: git://github.com/google/flutter-desktop-embedding.git
|
||||
path: plugins/window_size
|
||||
ref: e48abe7c3e9ebfe0b81622167c5201d4e783bb81
|
||||
|
||||
dev_dependencies:
|
||||
msix: ^2.1.3
|
||||
# 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
|
||||
|
@ -119,3 +107,19 @@ flutter:
|
|||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/custom-fonts/#from-packages
|
||||
|
||||
|
||||
msix_config:
|
||||
display_name: Cwtch
|
||||
publisher_display_name: Open Privacy Research Society
|
||||
identity_name: im.cwtch.flwtch
|
||||
msix_version: 1.0.0.0
|
||||
certificate_path: codesign.pfx
|
||||
certificate_password: pfx_pass
|
||||
publisher: CN=Open Privacy Research Society, O=Open Privacy Research Society, L=Vancouver, S=British Columbia, C=CA
|
||||
logo_path: cwtch.png
|
||||
start_menu_icon_path: cwtch.png
|
||||
tile_icon_path: assets\cwtch_title.png
|
||||
icons_background_color: transparent
|
||||
architecture: x64
|
||||
capabilities: 'internetClient'
|
||||
|
|
|
@ -14,8 +14,8 @@ import 'package:provider/provider.dart';
|
|||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark);
|
||||
var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light);
|
||||
var settingsEnglishDark = Settings(Locale("en", ''), OpaqueDark());
|
||||
var settingsEnglishLight = Settings(Locale("en", ''), OpaqueLight());
|
||||
ChangeNotifierProvider<Settings> getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark);
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -14,8 +14,8 @@ import 'package:provider/provider.dart';
|
|||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark);
|
||||
var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light);
|
||||
var settingsEnglishDark = Settings(Locale("en", ''), OpaqueDark());
|
||||
var settingsEnglishLight = Settings(Locale("en", ''), OpaqueLight());
|
||||
ChangeNotifierProvider<Settings> getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark);
|
||||
|
||||
void main() {
|
||||
|
|
|
@ -14,8 +14,8 @@ import 'package:provider/provider.dart';
|
|||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark);
|
||||
var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light);
|
||||
var settingsEnglishDark = Settings(Locale("en", ''), OpaqueDark());
|
||||
var settingsEnglishLight = Settings(Locale("en", ''), OpaqueLight());
|
||||
ChangeNotifierProvider<Settings> getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark);
|
||||
|
||||
String file(String slug) {
|
||||
|
|
|
@ -14,8 +14,8 @@ import 'package:provider/provider.dart';
|
|||
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
var settingsEnglishDark = Settings(Locale("en", ''), Opaque.dark);
|
||||
var settingsEnglishLight = Settings(Locale("en", ''), Opaque.light);
|
||||
var settingsEnglishDark = Settings(Locale("en", ''), OpaqueDark());
|
||||
var settingsEnglishLight = Settings(Locale("en", ''), OpaqueLight());
|
||||
ChangeNotifierProvider<Settings> getSettingsEnglishDark() => ChangeNotifierProvider.value(value: settingsEnglishDark);
|
||||
|
||||
String file(String slug) {
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <window_size/window_size_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
WindowSizePluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("WindowSizePlugin"));
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
window_size
|
||||
)
|
||||
|
||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 51 KiB |
|
@ -0,0 +1,6 @@
|
|||
- cp nsis/cwtch-installer.nsi deploy/
|
||||
- cd deploy
|
||||
- makensis -V3 cwtch-installer.nsi
|
||||
- export BUILDDATE=`date +%G-%m-%d-%H-%M`
|
||||
- export FILENAME=cwtch-installer-$BUILDDATE.exe
|
||||
- mv cwtch-installer.exe $FILENAME
|
|
@ -0,0 +1,92 @@
|
|||
; USAGE: Run in ui/deploy, requires the output be in 'windows' directory
|
||||
|
||||
!include "MUI2.nsh"
|
||||
|
||||
; General settings ----------------------------
|
||||
Name "Cwtch"
|
||||
; !define MUI_BRANDINGTEXT "SIG Beta Ver. 1.0"
|
||||
|
||||
Unicode True
|
||||
|
||||
# define the name of the installer
|
||||
Outfile "cwtch-installer.exe"
|
||||
|
||||
# For removing Start Menu shortcut in Windows 7
|
||||
#RequestExecutionLevel user
|
||||
RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on)
|
||||
|
||||
# define the directory to install to, the desktop in this case as specified
|
||||
# by the predefined $DESKTOP variable
|
||||
InstallDir "$PROGRAMFILES\Cwtch"
|
||||
|
||||
;Get installation folder from registry if available
|
||||
InstallDirRegKey HKCU "Software\Cwtch" "installLocation"
|
||||
|
||||
; MUI Interface -----------------------------
|
||||
|
||||
!define MUI_INSTALLCOLORS "DFB9DE 281831"
|
||||
|
||||
; 128x128, 32bit
|
||||
!define MUI_ICON "../runner/resources/knot_128.ico"
|
||||
|
||||
!define MUI_HEADERIMAGE
|
||||
!define MUI_HEADERIMAGE_BITMAP "cwtch_title.bmp"
|
||||
|
||||
!define MUI_TEXTCOLOR "350052"
|
||||
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "brand_side.bmp"
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP_STRETCH NoStretchNoCrop
|
||||
|
||||
!define MUI_INSTFILESPAGE_COLORS "DFB9DE 281831"
|
||||
!define MUI_INSTFILESPAGE_PROGRESSBAR "colored"
|
||||
|
||||
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||
|
||||
|
||||
ShowInstDetails show
|
||||
|
||||
; Pages --------
|
||||
|
||||
|
||||
!define MUI_WELCOMEPAGE_TITLE "Welcome to the Cwtch installer"
|
||||
!define MUI_WELCOMEPAGE_TEXT "Cwtch (pronounced: kutch) is a Welsh word roughly meaning 'a hug that creates a safe space'$\n$\n\
|
||||
Cwtch is a platform for building consentful, decentralized, untrusted infrastructure using metadata resistant group communication applications. Currently there is a selfnamed instant messaging prototype app that is driving development and testing. Many Further apps are planned as the platform matures."
|
||||
|
||||
!define MUI_FINISHPAGE_TITLE "Enjoy Cwtch"
|
||||
!define MUI_FINISHPAGE_RUN $INSTDIR/ui.exe
|
||||
!define MUI_FINISHPAGE_TEXT "You can keep up-to-date on Cwtch and report any issues you have at https://cwtch.im"
|
||||
!define MUI_FINISHPAGE_LINK "https://cwtch.im"
|
||||
!define MUI_FINISHPAGE_LINK_LOCATION "https://cwtch.im"
|
||||
!define MUI_FINISHPAGE_LINK_COLOR "D01972"
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_LICENSE "../../LICENSE"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
; Languages --------------------------------
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
# default section
|
||||
Section
|
||||
|
||||
# define the output path for this file
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
# define what to install and place it in the output path
|
||||
# Filler for .sh to populate with contents of deploy/windows
|
||||
#FILESLISTSTART
|
||||
FILE /r "..\..\build\windows\runner\Release\"
|
||||
#FILESLISTEND
|
||||
|
||||
|
||||
# create a shortcut in the start menu programs directory
|
||||
CreateDirectory "$SMPROGRAMS\Cwtch"
|
||||
CreateShortcut "$SMPROGRAMS\Cwtch\Cwtch.lnk" "$INSTDIR\cwtch.exe" "" "$INSTDIR\cwtch.ico"
|
||||
|
||||
;Store installation folder
|
||||
WriteRegStr HKCU "Software\Cwtch" "installLocation" $INSTDIR
|
||||
|
||||
SectionEnd
|
Binary file not shown.
After Width: | Height: | Size: 9.5 KiB |
Loading…
Reference in New Issue