merge to master
the build was successful
Details
the build was successful
Details
This commit is contained in:
commit
78f4d96a9b
26
.drone.yml
26
.drone.yml
|
@ -14,15 +14,16 @@ pipeline:
|
|||
- QT_DIR=/opt/Qt
|
||||
- QT_DOCKER='true'
|
||||
- QT_API=5.13.0
|
||||
- GO111MODULE=on
|
||||
commands:
|
||||
- export GOPATH=$GOPATH:/media/sf_GOPATH1/
|
||||
- export PATH=$PATH:/home/user/work/bin:/media/sf_GOPATH1/bin
|
||||
- apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git openssh-client
|
||||
- go get -d
|
||||
- go mod download
|
||||
- $QT_DIR/$QT_API/gcc_64/bin/lrelease ui.pro
|
||||
- git fetch --tags
|
||||
- export VERSION=`git describe --tags`
|
||||
- export BUILDDATE=`date +%G-%m-%d-%H-%M`
|
||||
- go mod vendor
|
||||
- qtdeploy -ldflags "-X main.buildVer=$VERSION -X main.buildDate=$BUILDDATE" build linux
|
||||
- cp README.md deploy/linux
|
||||
- export FILENAME=cwtch-linux-$BUILDDATE.tar.gz
|
||||
|
@ -44,16 +45,18 @@ pipeline:
|
|||
- QT_API=5.13.0
|
||||
- ANDROID_NDK_DIR=/home/user/android-ndk-r18b
|
||||
- ANDROID_SDK_DIR=/home/user/android-sdk-linux
|
||||
- GO111MODULE=on
|
||||
commands:
|
||||
- export GOPATH=$GOPATH:/media/sf_GOPATH1/
|
||||
- export PATH=$PATH:/home/user/work/bin:/media/sf_GOPATH1/bin
|
||||
- apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git
|
||||
- find -iname 'moc*' | xargs rm
|
||||
- find -iname 'rcc*' | xargs rm
|
||||
- go get -d
|
||||
- rm -r vendor/
|
||||
- make clean
|
||||
- go mod download
|
||||
- export VERSION=`git describe --tags`
|
||||
- export BUILDDATE=`date +%G-%m-%d-%H-%M`
|
||||
- qtdeploy -ldflags "-X main.buildVer=$VERSION -X main.buildDate=$BUILDDATE" build android
|
||||
- go mod vendor
|
||||
- qtsetup generate android
|
||||
- qtdeploy -ldflags "-X main.buildVer=$VERSION -X main.buildDate=$BUILDDATE" build android
|
||||
- cd deploy
|
||||
- export FILENAME=cwtch-android-$BUILDDATE.apk
|
||||
- cp android/build-debug.apk $FILENAME
|
||||
|
@ -69,15 +72,16 @@ pipeline:
|
|||
- QT_DIR=/opt/Qt
|
||||
- QT_DOCKER='true'
|
||||
- QT_API=5.13.0
|
||||
- GO111MODULE=on
|
||||
commands:
|
||||
- export GOPATH=$GOPATH:/media/sf_GOPATH1/
|
||||
- export PATH=$PATH:/home/user/work/bin:/media/sf_GOPATH1/bin
|
||||
- apt-get -qq update && apt-get --no-install-recommends -qq -y install ca-certificates curl git zip
|
||||
- find -iname 'moc*' | xargs rm
|
||||
- find -iname 'rcc*' | xargs rm
|
||||
- go get -d
|
||||
- rm -r vendor
|
||||
- make clean
|
||||
- go mod download
|
||||
- export VERSION=`git describe --tags`
|
||||
- export BUILDDATE=`date +%G-%m-%d-%H-%M`
|
||||
- go mod vendor
|
||||
- qtdeploy -ldflags "-X main.buildVer=$VERSION -X main.buildDate=$BUILDDATE" build windows
|
||||
- cp README.md deploy/windows
|
||||
- cp -r windows/* deploy/windows
|
||||
|
|
6
Makefile
6
Makefile
|
@ -1,9 +1,9 @@
|
|||
.PHONY: all clean linux android
|
||||
all:
|
||||
|
||||
default: all
|
||||
all: clean linux android
|
||||
default: linux
|
||||
|
||||
clean:
|
||||
rm -r vendor || true
|
||||
find -type f -iname "moc*" | xargs rm
|
||||
find -iname "rcc*" | xargs rm
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@ The UI is built using QT so you will need the development libraries and tools fo
|
|||
|
||||
This code relies on [therecipe/qt](https://github.com/therecipe/qt) before getting started consult the [Installation](https://github.com/therecipe/qt/wiki/Installation) and [Getting Started](https://github.com/therecipe/qt/wiki/Getting-Started) documentation to get that up and running. It will make building this much easier.
|
||||
|
||||
We are aiming to use Go module support for versioning but it has some issues working with therecipe/qt so we aren't there yet. For now build with GO111MODULE=off using just the GOPATH for dependancies.
|
||||
Cwtch UI uses the Go module system for dependancies
|
||||
|
||||
## Linux
|
||||
|
||||
go get -d
|
||||
go mod vendor
|
||||
qtdeploy build linux
|
||||
./deploy/linux/ui -local -debug 2>&1 | grep -v 'Detected anchors on an item that is managed by a layout.'
|
||||
|
||||
|
@ -33,7 +33,7 @@ The grep statement filters out some QML noise.
|
|||
|
||||
We supply an arm-pie version of tor in `android/libs/armeabi-v7a` with the name `libtor.so`
|
||||
|
||||
go get -d
|
||||
go mod vendor
|
||||
qtdeploy -docker build android
|
||||
adb install deploy/android/build-debug.apk
|
||||
|
||||
|
@ -52,7 +52,7 @@ We supply an arm-pie version of tor in `android/libs/armeabi-v7a` with the name
|
|||
|
||||
If all that is done, then check out cwtch.im/ui
|
||||
|
||||
go get -d
|
||||
go mod vendor
|
||||
qtdeploy
|
||||
deploy/windows/ui
|
||||
|
||||
|
|
9
go.mod
9
go.mod
|
@ -3,14 +3,13 @@ module cwtch.im/ui
|
|||
go 1.12
|
||||
|
||||
require (
|
||||
cwtch.im/cwtch v0.3.0
|
||||
git.openprivacy.ca/openprivacy/libricochet-go v1.0.6
|
||||
cwtch.im/cwtch v0.3.8
|
||||
git.openprivacy.ca/openprivacy/libricochet-go v1.0.8
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/stretchr/testify v1.4.0 // indirect
|
||||
github.com/therecipe/qt v0.0.0-20191221221430-5e239f03fa53
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200103041036-2b818d970888 // indirect
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191221221430-5e239f03fa53 // indirect
|
||||
github.com/therecipe/qt v0.0.0-20191101232336-18864661ae4f
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 // indirect; Required - do not delete or `go tidy` away
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 // indirect
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
|
||||
|
|
36
go.sum
36
go.sum
|
@ -1,10 +1,10 @@
|
|||
cwtch.im/cwtch v0.3.0 h1:RFZyc2m9BowFNdngBs7GcQE41w75jMp3Ku5zEE92v9I=
|
||||
cwtch.im/cwtch v0.3.0/go.mod h1:8tmtp3c7fccWw9H7s9u6E8GD2PKI3ar21i0fjN8pzd0=
|
||||
cwtch.im/tapir v0.1.10 h1:V+TkmwXNd6gySZqlVw468wMYEkmDwMSyvhkkpOfUw7w=
|
||||
cwtch.im/tapir v0.1.10/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34=
|
||||
cwtch.im/cwtch v0.3.8 h1:QxuDu+sH5VIcLQZGGfah3zuseq02Iyqhm7O2+ATtA9M=
|
||||
cwtch.im/cwtch v0.3.8/go.mod h1:/CAGNdgidvJ0sOfsWeU2hxlYCXv8usf6kspsfhG8gtQ=
|
||||
cwtch.im/tapir v0.1.14 h1:lg+reZNT998l++4Q4RQBLXYv3ukqWffhI0Wed9RSjuA=
|
||||
cwtch.im/tapir v0.1.14/go.mod h1:QwERb982YIes9UOxDqIthm1HZ1xy0YQetD2+XxDbg9Y=
|
||||
git.openprivacy.ca/openprivacy/libricochet-go v1.0.4/go.mod h1:yMSG1gBaP4f1U+RMZXN85d29D39OK5s8aTpyVRoH5FY=
|
||||
git.openprivacy.ca/openprivacy/libricochet-go v1.0.6 h1:5o4K2qn3otEE1InC5v5CzU0yL7Wl7DhVp4s8H3K6mXY=
|
||||
git.openprivacy.ca/openprivacy/libricochet-go v1.0.6/go.mod h1:yMSG1gBaP4f1U+RMZXN85d29D39OK5s8aTpyVRoH5FY=
|
||||
git.openprivacy.ca/openprivacy/libricochet-go v1.0.8 h1:HVoyxfivFaEtkfO5K3piD6oq6MQB1qGF5IB2EYXeCW8=
|
||||
git.openprivacy.ca/openprivacy/libricochet-go v1.0.8/go.mod h1:6I+vO9Aagv3/yUWv+e7A57H8tgXgR67FCjfSj9Pp970=
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI=
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
|
||||
github.com/c-bata/go-prompt v0.2.3/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
|
||||
|
@ -19,6 +19,10 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f h1:KMlcu9X58lhTA/KrfX8Bi1LQSO4pzoVjTiL3h4Jk+Zk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
|
||||
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
|
||||
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
|
||||
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
|
@ -30,6 +34,8 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
|||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
|
||||
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
|
||||
github.com/pkg/term v0.0.0-20190109203006-aa71e9d9e942/go.mod h1:eCbImbZ95eXtAUIbLAuAVnBnwf83mjf6QIVH8SHYwqQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -43,17 +49,11 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/struCoder/pidusage v0.1.2/go.mod h1:pWBlW3YuSwRl6h7R5KbvA4N8oOqe9LjaKW5CwT1SPjI=
|
||||
github.com/therecipe/qt v0.0.0-20190824160953-615e084bab56 h1:CAFR/rHptsl8gEP6igtp6VbuQpPALEJ/B+gl9QkyFXU=
|
||||
github.com/therecipe/qt v0.0.0-20190824160953-615e084bab56/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
|
||||
github.com/therecipe/qt v0.0.0-20191221221430-5e239f03fa53 h1:vmHLq7TFJ0OQLZzJF0mnCQW6o7NKV319X4F9ImMcLv4=
|
||||
github.com/therecipe/qt v0.0.0-20191221221430-5e239f03fa53/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
|
||||
github.com/therecipe/qt v0.0.0-20200103041036-2b818d970888 h1:kwDtZGIbjPGYzvs4Dk/4O4E2nnJugQkccLyfFUHpHk0=
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200103041036-2b818d970888 h1:GnH3hKsPT8vfSw6LQ6+gHoYdJw7Zd5bifdXzP/Z1tus=
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200103041036-2b818d970888/go.mod h1:7m8PDYDEtEVqfjoUQc2UrFqhG0CDmoVJjRlQxexndFc=
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191221221430-5e239f03fa53 h1:nnH71oC0mqkEFEZT8hxykAV+7/yQXial8gbJuxNQNdY=
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191221221430-5e239f03fa53/go.mod h1:mH55Ek7AZcdns5KPp99O0bg+78el64YCYWHiQKrOdt4=
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200103041036-2b818d970888 h1:4tZ5WKqMm1U6iOM3kaRvD+mndR5flF61YMXr7gxQ9TU=
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200103041036-2b818d970888/go.mod h1:mH55Ek7AZcdns5KPp99O0bg+78el64YCYWHiQKrOdt4=
|
||||
github.com/therecipe/qt v0.0.0-20191101232336-18864661ae4f h1:06ICDSmDOBUC9jwgv44ngvyHzwudJNLa5H+rbCyDFRY=
|
||||
github.com/therecipe/qt v0.0.0-20191101232336-18864661ae4f/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
|
||||
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0/go.mod h1:mH55Ek7AZcdns5KPp99O0bg+78el64YCYWHiQKrOdt4=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
|
@ -69,6 +69,7 @@ golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -78,7 +79,6 @@ golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789 h1:FF0rjo15h51+N6642mf5S3QuplmKo2aCrJUYkHTx85s=
|
||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
package characters
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/app/plugins"
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/ui/go/constants"
|
||||
"cwtch.im/ui/go/cwutil"
|
||||
"cwtch.im/ui/go/gobjects"
|
||||
"cwtch.im/ui/go/gothings"
|
||||
"cwtch.im/ui/go/the"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool) {
|
||||
q := event.NewQueue()
|
||||
the.AppBus.Subscribe(event.NewPeer, q)
|
||||
the.AppBus.Subscribe(event.PeerError, q)
|
||||
the.AppBus.Subscribe(event.AppError, q)
|
||||
the.AppBus.Subscribe(event.ACNStatus, q)
|
||||
the.AppBus.Subscribe(event.ReloadDone, q)
|
||||
subscribed <- true
|
||||
|
||||
for {
|
||||
e := q.Next()
|
||||
|
||||
switch e.EventType {
|
||||
case event.ACNStatus:
|
||||
progStr := e.Data[event.Progreess]
|
||||
percent, _ := strconv.Atoi(progStr)
|
||||
message := e.Data[event.Status]
|
||||
var statuscode int
|
||||
if percent >= 0 && percent <= 25 {
|
||||
statuscode = 1
|
||||
message = "Connecting to network"
|
||||
} else if percent < 100 {
|
||||
statuscode = 2
|
||||
message = "Establishng Tor circut"
|
||||
} else if percent == 100 {
|
||||
statuscode = 3
|
||||
message = "tor appears to be running just fine!"
|
||||
} else {
|
||||
statuscode = 0
|
||||
message = "can't find tor. is it running? is the controlport configured?"
|
||||
}
|
||||
|
||||
gcd.TorStatus(statuscode, message)
|
||||
case event.PeerError:
|
||||
// current only case
|
||||
log.Errorf("couldn't load profiles: %v", e.Data[event.Error])
|
||||
os.Exit(1)
|
||||
|
||||
case event.AppError:
|
||||
|
||||
if e.Data[event.Error] == event.AppErrLoaded0 {
|
||||
// TODO: only an error if other profiles are not loaded
|
||||
log.Infoln("couldn't load your config file. attempting to create one now")
|
||||
|
||||
the.CwtchApp.CreatePeer("alice", the.AppPassword)
|
||||
}
|
||||
|
||||
case event.ReloadDone:
|
||||
if the.Peer == nil {
|
||||
the.CwtchApp.LoadProfiles(the.AppPassword)
|
||||
}
|
||||
case event.NewPeer:
|
||||
if the.Peer != nil {
|
||||
continue
|
||||
}
|
||||
onion := e.Data[event.Identity]
|
||||
|
||||
the.CwtchApp.AddPeerPlugin(onion, plugins.CONTACTRETRY)
|
||||
|
||||
the.Peer = the.CwtchApp.GetPeer(onion)
|
||||
the.EventBus = the.CwtchApp.GetEventBus(onion)
|
||||
|
||||
incSubscribed := make(chan bool)
|
||||
go IncomingListener(&gcd.UIState, incSubscribed)
|
||||
<-incSubscribed
|
||||
|
||||
gcd.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, cwutil.RandomProfileImage(the.Peer.GetProfile().Onion))
|
||||
|
||||
contacts := the.Peer.GetContacts()
|
||||
for i := range contacts {
|
||||
contact, _ := the.Peer.GetProfile().GetContact(contacts[i])
|
||||
displayName, _ := contact.GetAttribute("nick")
|
||||
|
||||
gcd.UIState.AddContact(&gobjects.Contact{
|
||||
Handle: contacts[i],
|
||||
DisplayName: displayName,
|
||||
Image: cwutil.RandomProfileImage(contacts[i]),
|
||||
Trusted: contact.Trusted,
|
||||
Blocked: contact.Blocked,
|
||||
Loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
groups := the.Peer.GetGroups()
|
||||
for i := range groups {
|
||||
group := the.Peer.GetGroup(groups[i])
|
||||
nick, exists := group.GetAttribute("nick")
|
||||
if !exists {
|
||||
nick = group.GroupID[:12]
|
||||
}
|
||||
// Only join servers for active and explicitly accepted groups.
|
||||
gcd.UIState.AddContact(&gobjects.Contact{
|
||||
Handle: group.GroupID,
|
||||
DisplayName: nick,
|
||||
Image: cwutil.RandomGroupImage(group.GroupID),
|
||||
Server: group.GroupServer,
|
||||
Trusted: group.Accepted,
|
||||
Loading: false,
|
||||
})
|
||||
}
|
||||
|
||||
if e.Data[event.Status] != "running" {
|
||||
the.CwtchApp.LaunchPeers()
|
||||
}
|
||||
|
||||
// load ui preferences
|
||||
gcd.RequestSettings()
|
||||
locale, exists := the.Peer.GetProfile().GetAttribute(constants.LocaleSetting)
|
||||
if exists {
|
||||
gcd.SetLocale_helper(locale)
|
||||
}
|
||||
|
||||
blockUnkownPeers, exists := the.Peer.GetProfile().GetAttribute(constants.BlockUnknownPeersSetting)
|
||||
if exists && blockUnkownPeers == "true" {
|
||||
the.EventBus.Publish(event.NewEvent(event.BlockUnknownPeers, map[event.Field]string{}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
package characters
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/protocol/connections"
|
||||
"cwtch.im/ui/go/cwutil"
|
||||
"cwtch.im/ui/go/gobjects"
|
||||
"cwtch.im/ui/go/gothings"
|
||||
"cwtch.im/ui/go/the"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) {
|
||||
q := event.NewQueue()
|
||||
the.EventBus.Subscribe(event.NewMessageFromPeer, q)
|
||||
the.EventBus.Subscribe(event.PeerAcknowledgement, q)
|
||||
the.EventBus.Subscribe(event.NewMessageFromGroup, q)
|
||||
the.EventBus.Subscribe(event.NewGroupInvite, q)
|
||||
the.EventBus.Subscribe(event.SendMessageToGroupError, q)
|
||||
the.EventBus.Subscribe(event.SendMessageToPeerError, q)
|
||||
the.EventBus.Subscribe(event.ServerStateChange, q)
|
||||
the.EventBus.Subscribe(event.PeerStateChange, q)
|
||||
subscribed <- true
|
||||
|
||||
for {
|
||||
e := q.Next()
|
||||
|
||||
switch e.EventType {
|
||||
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
||||
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampReceived])
|
||||
uiState.AddMessage(&gobjects.Message{
|
||||
Handle: e.Data[event.RemotePeer],
|
||||
From: e.Data[event.RemotePeer],
|
||||
Message: e.Data[event.Data],
|
||||
Image: cwutil.RandomProfileImage(e.Data[event.RemotePeer]),
|
||||
Timestamp: ts,
|
||||
})
|
||||
if the.Peer.GetContact(e.Data[event.RemotePeer]) == nil {
|
||||
the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
|
||||
}
|
||||
|
||||
case event.PeerAcknowledgement:
|
||||
ackI, ok := the.AcknowledgementIDs.Load(e.Data[event.EventID])
|
||||
if ok {
|
||||
ack := ackI.(*the.AckId)
|
||||
if ack.Peer == e.Data[event.RemotePeer] {
|
||||
ack.Ack = true
|
||||
uiState.Acknowledge(e.Data[event.EventID])
|
||||
continue
|
||||
}
|
||||
}
|
||||
log.Debugf("Received Ack ID for unknown message or peer: %v", e)
|
||||
case event.NewMessageFromGroup: //event.TimestampReceived, event.TimestampSent, event.Data, event.GroupID, event.RemotePeer
|
||||
var name string
|
||||
var exists bool
|
||||
ctc := the.Peer.GetContact(e.Data[event.RemotePeer])
|
||||
if ctc != nil {
|
||||
name, exists = ctc.GetAttribute("nick")
|
||||
if !exists || name == "" {
|
||||
name = e.Data[event.RemotePeer]
|
||||
}
|
||||
} else {
|
||||
name = e.Data[event.RemotePeer]
|
||||
}
|
||||
|
||||
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampSent])
|
||||
uiState.AddMessage(&gobjects.Message{
|
||||
MessageID: e.Data[event.Signature],
|
||||
Handle: e.Data[event.GroupID],
|
||||
From: e.Data[event.RemotePeer],
|
||||
Message: e.Data[event.Data],
|
||||
Image: cwutil.RandomProfileImage(e.Data[event.RemotePeer]),
|
||||
FromMe: e.Data[event.RemotePeer] == the.Peer.GetProfile().Onion,
|
||||
Timestamp: ts,
|
||||
Acknowledged: true,
|
||||
DisplayName: name,
|
||||
})
|
||||
case event.NewGroupInvite:
|
||||
gid, err := the.Peer.GetProfile().ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer])
|
||||
group := the.Peer.GetGroup(gid)
|
||||
if err == nil && group != nil {
|
||||
uiState.AddContact(&gobjects.Contact{
|
||||
Handle: gid,
|
||||
DisplayName: gid,
|
||||
Image: cwutil.RandomGroupImage(gid),
|
||||
Server: group.GroupServer,
|
||||
Trusted: false,
|
||||
Loading: false,
|
||||
})
|
||||
}
|
||||
case event.SendMessageToGroupError:
|
||||
uiState.AddSendMessageError(e.Data[event.GroupServer], e.Data[event.Signature], e.Data[event.Error])
|
||||
case event.SendMessageToPeerError:
|
||||
uiState.AddSendMessageError(e.Data[event.RemotePeer], e.Data[event.Signature], e.Data[event.Error])
|
||||
case event.PeerStateChange:
|
||||
cxnState := connections.ConnectionStateToType[e.Data[event.ConnectionState]]
|
||||
|
||||
// if it's not in the.Peer it's new. Only add once Authed
|
||||
_, exists := the.Peer.GetProfile().Contacts[e.Data[event.RemotePeer]]
|
||||
if !exists {
|
||||
// Contact does not exist, we will add them but we won't know who they are until they are authenticated
|
||||
// So if we get any other state from an unknown contact we do nothing
|
||||
// (the next exists check will fail)
|
||||
if cxnState == connections.AUTHENTICATED {
|
||||
the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
|
||||
}
|
||||
}
|
||||
|
||||
// if it's in the.Peer its either existing and needs an update or not in the UI and needs to be added
|
||||
if contact, exists := the.Peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; exists {
|
||||
contact.State = e.Data[event.ConnectionState]
|
||||
uiContact := uiState.GetContact(contact.Onion)
|
||||
if uiContact != nil {
|
||||
if uiContact.Status != int(cxnState) {
|
||||
uiContact.Status = int(cxnState)
|
||||
uiState.UpdateContact(contact.Onion)
|
||||
}
|
||||
} else {
|
||||
uiState.AddContact(&gobjects.Contact{
|
||||
e.Data[event.RemotePeer],
|
||||
e.Data[event.RemotePeer],
|
||||
cwutil.RandomProfileImage(e.Data[event.RemotePeer]),
|
||||
"",
|
||||
0,
|
||||
int(cxnState),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
})
|
||||
}
|
||||
}
|
||||
case event.ServerStateChange:
|
||||
serverOnion := e.Data[event.GroupServer]
|
||||
state := connections.ConnectionStateToType[e.Data[event.ConnectionState]]
|
||||
groups := the.Peer.GetGroups()
|
||||
for _, groupID := range groups {
|
||||
group := the.Peer.GetGroup(groupID)
|
||||
group.State = e.Data[event.ConnectionState]
|
||||
if group != nil && group.GroupServer == serverOnion {
|
||||
uiState.GetContact(group.GroupID).Status = int(state)
|
||||
if state == connections.AUTHENTICATED {
|
||||
uiState.GetContact(group.GroupID).Loading = true
|
||||
} else {
|
||||
uiState.GetContact(group.GroupID).Loading = false
|
||||
}
|
||||
uiState.UpdateContact(group.GroupID)
|
||||
} else {
|
||||
log.Errorf("found group that is nil :/")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package constants
|
||||
|
||||
const Nick = "nick"
|
||||
const LastRead = "last-read"
|
||||
const Picture = "picture"
|
||||
const DefaultPassword = "default-password"
|
||||
|
||||
const ProfileTypeV1DefaultPassword = "v1-defaultPassword"
|
||||
const ProfileTypeV1Password = "v1-userPassword"
|
|
@ -1,13 +0,0 @@
|
|||
package gobjects
|
||||
|
||||
type Contact struct {
|
||||
Handle string
|
||||
DisplayName string
|
||||
Image string
|
||||
Server string
|
||||
Badge int
|
||||
Status int
|
||||
Trusted bool
|
||||
Blocked bool
|
||||
Loading bool
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package gobjects
|
||||
|
||||
// a Letter is a very simple message object passed to us from the UI
|
||||
type Letter struct {
|
||||
To, Message string
|
||||
MID string
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package gobjects
|
||||
|
||||
import "time"
|
||||
|
||||
type Message struct {
|
||||
Handle string
|
||||
From string
|
||||
DisplayName string
|
||||
Message string
|
||||
Image string
|
||||
FromMe bool
|
||||
MessageID string
|
||||
Timestamp time.Time
|
||||
Acknowledged bool
|
||||
Error bool
|
||||
}
|
|
@ -1,195 +0,0 @@
|
|||
package gothings
|
||||
|
||||
import (
|
||||
"cwtch.im/ui/go/constants"
|
||||
"cwtch.im/ui/go/cwutil"
|
||||
"cwtch.im/ui/go/gobjects"
|
||||
"cwtch.im/ui/go/the"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type InterfaceState struct {
|
||||
parentGcd *GrandCentralDispatcher
|
||||
contacts sync.Map // string : *gobjects.Contact
|
||||
messages sync.Map
|
||||
}
|
||||
|
||||
func NewUIState(gcd *GrandCentralDispatcher) (uis InterfaceState) {
|
||||
uis = InterfaceState{gcd, sync.Map{}, sync.Map{}}
|
||||
return
|
||||
}
|
||||
|
||||
func (this *InterfaceState) Acknowledge(mID string) {
|
||||
this.parentGcd.Acknowledged(mID)
|
||||
}
|
||||
|
||||
func (this *InterfaceState) AddContact(c *gobjects.Contact) {
|
||||
if len(c.Handle) == 32 { // ADD GROUP
|
||||
if _, found := this.contacts.Load(c.Handle); !found {
|
||||
this.parentGcd.AddContact(c.Handle, c.DisplayName, c.Image, c.Server, c.Badge, c.Status, c.Trusted, c.Blocked, c.Loading)
|
||||
this.contacts.Store(c.Handle, c)
|
||||
}
|
||||
return
|
||||
} else if len(c.Handle) != 56 {
|
||||
log.Errorf("sorry, unable to handle AddContact(%v)", c.Handle)
|
||||
debug.PrintStack()
|
||||
return
|
||||
}
|
||||
|
||||
if _, found := this.contacts.Load(c.Handle); !found {
|
||||
this.contacts.Store(c.Handle, c)
|
||||
this.parentGcd.AddContact(c.Handle, c.DisplayName, c.Image, c.Server, c.Badge, c.Status, c.Trusted, c.Blocked, false)
|
||||
if the.Peer.GetContact(c.Handle) == nil {
|
||||
the.Peer.AddContact(c.DisplayName, c.Handle, c.Trusted)
|
||||
go the.Peer.PeerWithOnion(c.Handle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *InterfaceState) DeleteContact(id string) {
|
||||
this.contacts.Delete(id)
|
||||
}
|
||||
|
||||
func (this *InterfaceState) GetContact(handle string) *gobjects.Contact {
|
||||
if _, found := this.contacts.Load(handle); !found {
|
||||
if len(handle) == 32 {
|
||||
group := the.Peer.GetGroup(handle)
|
||||
if group != nil {
|
||||
nick, exists := group.GetAttribute("nick")
|
||||
if !exists {
|
||||
nick = group.GroupID[:12]
|
||||
}
|
||||
this.AddContact(&gobjects.Contact{
|
||||
handle,
|
||||
nick,
|
||||
cwutil.RandomGroupImage(handle),
|
||||
group.GroupServer,
|
||||
0,
|
||||
0,
|
||||
group.Accepted,
|
||||
false,
|
||||
false,
|
||||
})
|
||||
} else {
|
||||
log.Errorf("Attempting to add non existent group to ui %v", handle)
|
||||
}
|
||||
} else {
|
||||
contact := the.Peer.GetContact(handle)
|
||||
if contact != nil && handle != contact.Onion {
|
||||
nick, exists := contact.GetAttribute("name")
|
||||
if !exists {
|
||||
nick = contact.Onion
|
||||
}
|
||||
this.AddContact(&gobjects.Contact{
|
||||
handle,
|
||||
nick,
|
||||
cwutil.RandomProfileImage(handle),
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
contact.Blocked,
|
||||
false,
|
||||
})
|
||||
} else if contact == nil {
|
||||
//log.Errorf("Attempting to add non existent contact to ui %v", handle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contactIntf, _ := this.contacts.Load(handle)
|
||||
if contactIntf == nil {
|
||||
return nil
|
||||
}
|
||||
contact := contactIntf.(*gobjects.Contact)
|
||||
return contact
|
||||
}
|
||||
|
||||
func (this *InterfaceState) AddSendMessageError(peer string, signature string, err string) {
|
||||
ackI, ok := the.AcknowledgementIDs.Load(signature)
|
||||
if ok {
|
||||
ack := ackI.(*the.AckId)
|
||||
ack.Error = true
|
||||
}
|
||||
|
||||
messages, _ := this.messages.Load(peer)
|
||||
messageList, _ := messages.([]*gobjects.Message)
|
||||
|
||||
for _, message := range messageList {
|
||||
if message.MessageID == signature {
|
||||
message.Error = true
|
||||
}
|
||||
}
|
||||
log.Debugf("Received Error Sending Message: %v", err)
|
||||
// FIXME: Sometimes, for the first Peer message we send our error beats our message to the UI
|
||||
time.Sleep(time.Second * 1)
|
||||
this.parentGcd.GroupSendError(signature, err)
|
||||
}
|
||||
|
||||
func (this *InterfaceState) AddMessage(m *gobjects.Message) {
|
||||
this.GetContact(m.From)
|
||||
|
||||
_, found := this.messages.Load(m.Handle)
|
||||
if !found {
|
||||
this.messages.Store(m.Handle, make([]*gobjects.Message, 0))
|
||||
}
|
||||
|
||||
// Ack message sent to group
|
||||
this.parentGcd.Acknowledged(m.MessageID)
|
||||
|
||||
messages, _ := this.messages.Load(m.Handle)
|
||||
messageList, _ := messages.([]*gobjects.Message)
|
||||
this.messages.Store(m.Handle, append(messageList, m))
|
||||
|
||||
// If we have this group loaded already
|
||||
if this.parentGcd.CurrentOpenConversation() == m.Handle {
|
||||
// If the message is not from the user then add it, otherwise, just acknowledge.
|
||||
if !m.FromMe || !m.Acknowledged {
|
||||
this.parentGcd.AppendMessage(m.Handle, m.From, m.DisplayName, m.Message, m.Image, m.MessageID, m.FromMe, m.Timestamp.Format(constants.TIME_FORMAT), m.Acknowledged, m.Error)
|
||||
} else {
|
||||
this.parentGcd.Acknowledged(m.MessageID)
|
||||
}
|
||||
} else {
|
||||
c := this.GetContact(m.Handle)
|
||||
if c != nil {
|
||||
c.Badge++
|
||||
this.UpdateContact(c.Handle)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (this *InterfaceState) GetMessages(handle string) []*gobjects.Message {
|
||||
_, found := this.messages.Load(handle)
|
||||
if !found {
|
||||
this.messages.Store(handle, make([]*gobjects.Message, 0))
|
||||
}
|
||||
messages, found := this.messages.Load(handle)
|
||||
messageList, _ := messages.([]*gobjects.Message)
|
||||
return messageList
|
||||
}
|
||||
|
||||
func (this *InterfaceState) UpdateContact(handle string) {
|
||||
contact := the.Peer.GetContact(handle)
|
||||
if contact != nil {
|
||||
cif, found := this.contacts.Load(handle)
|
||||
if found {
|
||||
c := cif.(*gobjects.Contact)
|
||||
c.Blocked = contact.Blocked
|
||||
this.parentGcd.UpdateContact(c.Handle, c.DisplayName, c.Image, c.Server, c.Badge, c.Status, c.Trusted, c.Blocked, c.Loading)
|
||||
}
|
||||
} else {
|
||||
cif, found := this.contacts.Load(handle)
|
||||
if found {
|
||||
c := cif.(*gobjects.Contact)
|
||||
this.parentGcd.UpdateContact(c.Handle, c.DisplayName, c.Image, c.Server, c.Badge, c.Status, c.Trusted, c.Blocked, c.Loading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this *InterfaceState) UpdateContactAttribute(handle, key, value string) {
|
||||
this.parentGcd.UpdateContactAttribute(handle, key, value)
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/app/plugins"
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/ui/go/constants"
|
||||
"cwtch.im/ui/go/the"
|
||||
"cwtch.im/ui/go/ui"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingAccounts bool) {
|
||||
q := event.NewQueue()
|
||||
the.AppBus.Subscribe(event.NewPeer, q)
|
||||
the.AppBus.Subscribe(event.PeerError, q)
|
||||
the.AppBus.Subscribe(event.AppError, q)
|
||||
the.AppBus.Subscribe(event.ACNStatus, q)
|
||||
the.AppBus.Subscribe(event.NetworkStatus, q)
|
||||
the.AppBus.Subscribe(event.ReloadDone, q)
|
||||
subscribed <- true
|
||||
|
||||
networkOffline := false
|
||||
timeSinceLastSuccess := time.Unix(0, 0)
|
||||
|
||||
gcd.Loaded()
|
||||
|
||||
for {
|
||||
e := q.Next()
|
||||
|
||||
switch e.EventType {
|
||||
case event.NetworkStatus:
|
||||
status := e.Data[event.Status]
|
||||
if status == "Error" && !networkOffline {
|
||||
networkOffline = true
|
||||
// if it has been more that 5 minutes since we received any kind of success, then we should kill tor
|
||||
// anything less that this i.e. transient networking failures, should allow us to reconnect without issue
|
||||
if time.Now().Sub(timeSinceLastSuccess) > (time.Minute * 5) {
|
||||
the.ACN.Restart()
|
||||
}
|
||||
}
|
||||
|
||||
if status == "Success" && networkOffline {
|
||||
timeSinceLastSuccess = time.Now()
|
||||
networkOffline = false
|
||||
}
|
||||
|
||||
case event.ACNStatus:
|
||||
progStr := e.Data[event.Progreess]
|
||||
percent, _ := strconv.Atoi(progStr)
|
||||
message := e.Data[event.Status]
|
||||
var statuscode int
|
||||
if percent >= 0 && percent <= 25 {
|
||||
statuscode = 1
|
||||
message = "Connecting to network"
|
||||
} else if percent < 100 {
|
||||
statuscode = 2
|
||||
message = "Establishing Tor circuit"
|
||||
} else if percent == 100 {
|
||||
statuscode = 3
|
||||
message = "tor appears to be running just fine!"
|
||||
} else {
|
||||
statuscode = 0
|
||||
message = "can't find tor. is it running? is the controlport configured?"
|
||||
}
|
||||
|
||||
gcd.TorStatus(statuscode, message)
|
||||
|
||||
case event.PeerError:
|
||||
// current only case
|
||||
log.Errorf("couldn't load profiles: %v", e.Data[event.Error])
|
||||
os.Exit(1)
|
||||
|
||||
case event.AppError:
|
||||
|
||||
if e.Data[event.Error] == event.AppErrLoaded0 {
|
||||
if reloadingAccounts {
|
||||
reloadingAccounts = false
|
||||
} else {
|
||||
gcd.ErrorLoaded0()
|
||||
}
|
||||
}
|
||||
|
||||
case event.ReloadDone:
|
||||
reloadingAccounts = false
|
||||
if len(the.CwtchApp.ListPeers()) == 0 {
|
||||
the.CwtchApp.LoadProfiles(the.AppPassword)
|
||||
}
|
||||
case event.NewPeer:
|
||||
onion := e.Data[event.Identity]
|
||||
peer := the.CwtchApp.GetPeer(onion)
|
||||
|
||||
if tag, exists := peer.GetAttribute(app.AttributeTag); !exists || tag == "" {
|
||||
peer.SetAttribute(app.AttributeTag, constants.ProfileTypeV1DefaultPassword)
|
||||
}
|
||||
|
||||
log.Infof("NewPeer for %v\n", onion)
|
||||
ui.AddProfile(gcd, onion)
|
||||
|
||||
the.CwtchApp.AddPeerPlugin(onion, plugins.CONNECTIONRETRY)
|
||||
the.CwtchApp.AddPeerPlugin(onion, plugins.NETWORKCHECK)
|
||||
|
||||
incSubscribed := make(chan bool)
|
||||
go PeerHandler(onion, gcd.GetUiManager(peer.GetProfile().Onion), incSubscribed)
|
||||
<-incSubscribed
|
||||
|
||||
if e.Data[event.Status] != "running" {
|
||||
peer.Listen()
|
||||
peer.StartPeersConnections()
|
||||
peer.StartGroupConnections()
|
||||
}
|
||||
|
||||
blockUnkownPeers, exists := peer.GetProfile().GetAttribute(constants.BlockUnknownPeersSetting)
|
||||
if exists && blockUnkownPeers == "true" {
|
||||
the.EventBus.Publish(event.NewEvent(event.BlockUnknownPeers, map[event.Field]string{}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/protocol/connections"
|
||||
"cwtch.im/ui/go/constants"
|
||||
"cwtch.im/ui/go/the"
|
||||
"cwtch.im/ui/go/ui"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
|
||||
peer := the.CwtchApp.GetPeer(onion)
|
||||
eventBus := the.CwtchApp.GetEventBus(onion)
|
||||
q := event.NewQueue()
|
||||
eventBus.Subscribe(event.NewMessageFromPeer, q)
|
||||
eventBus.Subscribe(event.PeerAcknowledgement, q)
|
||||
eventBus.Subscribe(event.NewMessageFromGroup, q)
|
||||
eventBus.Subscribe(event.NewGroupInvite, q)
|
||||
eventBus.Subscribe(event.SendMessageToGroupError, q)
|
||||
eventBus.Subscribe(event.SendMessageToPeerError, q)
|
||||
eventBus.Subscribe(event.ServerStateChange, q)
|
||||
eventBus.Subscribe(event.PeerStateChange, q)
|
||||
eventBus.Subscribe(event.PeerCreated, q)
|
||||
eventBus.Subscribe(event.NetworkStatus, q)
|
||||
eventBus.Subscribe(event.ChangePasswordSuccess, q)
|
||||
eventBus.Subscribe(event.ChangePasswordError, q)
|
||||
|
||||
subscribed <- true
|
||||
|
||||
networkOffline := false
|
||||
|
||||
for {
|
||||
e := q.Next()
|
||||
|
||||
switch e.EventType {
|
||||
|
||||
case event.NetworkStatus:
|
||||
the.AppBus.Publish(*e)
|
||||
if e.Data["Status"] == "Success" && networkOffline {
|
||||
networkOffline = false
|
||||
// TODO we may have to reinitialize the peer
|
||||
} else {
|
||||
networkOffline = true
|
||||
}
|
||||
|
||||
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
|
||||
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampReceived])
|
||||
uiManager.AddMessage(e.Data[event.RemotePeer], e.Data[event.RemotePeer], e.Data[event.Data], false, e.EventID, ts, true)
|
||||
if peer.GetContact(e.Data[event.RemotePeer]) == nil {
|
||||
peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
|
||||
}
|
||||
|
||||
case event.PeerAcknowledgement:
|
||||
uiManager.Acknowledge(e.Data[event.EventID])
|
||||
|
||||
case event.NewMessageFromGroup: //event.TimestampReceived, event.TimestampSent, event.Data, event.GroupID, event.RemotePeer
|
||||
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampSent])
|
||||
uiManager.AddMessage(e.Data[event.GroupID], e.Data[event.RemotePeer], e.Data[event.Data], e.Data[event.RemotePeer] == peer.GetProfile().Onion, e.Data[event.Signature], ts, true)
|
||||
case event.NewGroupInvite:
|
||||
gid, err := peer.GetProfile().ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer])
|
||||
group := peer.GetGroup(gid)
|
||||
if err == nil && group != nil {
|
||||
uiManager.AddContact(gid)
|
||||
}
|
||||
case event.PeerCreated:
|
||||
onion := e.Data[event.RemotePeer]
|
||||
uiManager.AddContact(onion)
|
||||
case event.SendMessageToGroupError:
|
||||
uiManager.AddSendMessageError(e.Data[event.GroupServer], e.Data[event.Signature], e.Data[event.Error])
|
||||
case event.SendMessageToPeerError:
|
||||
uiManager.AddSendMessageError(e.Data[event.RemotePeer], e.Data[event.EventID], e.Data[event.Error])
|
||||
case event.PeerStateChange:
|
||||
cxnState := connections.ConnectionStateToType[e.Data[event.ConnectionState]]
|
||||
|
||||
// if it's not in the.PeerHandler it's new. Only add once Authed
|
||||
if _, exists := peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; !exists {
|
||||
// Contact does not exist, we will add them but we won't know who they are until they are authenticated
|
||||
// So if we get any other state from an unknown contact we do nothing
|
||||
// (the next exists check will fail)
|
||||
if cxnState == connections.AUTHENTICATED {
|
||||
peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
|
||||
uiManager.AddContact(e.Data[event.RemotePeer])
|
||||
}
|
||||
}
|
||||
|
||||
// if it's in the.PeerHandler its either existing and needs an update or not in the UI and needs to be added
|
||||
if contact, exists := peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; exists {
|
||||
contact.State = e.Data[event.ConnectionState]
|
||||
uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
|
||||
|
||||
}
|
||||
case event.ServerStateChange:
|
||||
serverOnion := e.Data[event.GroupServer]
|
||||
state := connections.ConnectionStateToType[e.Data[event.ConnectionState]]
|
||||
groups := peer.GetGroups()
|
||||
for _, groupID := range groups {
|
||||
group := peer.GetGroup(groupID)
|
||||
if group != nil && group.GroupServer == serverOnion {
|
||||
group.State = e.Data[event.ConnectionState]
|
||||
loading := false
|
||||
if state == connections.AUTHENTICATED {
|
||||
loading = true
|
||||
}
|
||||
uiManager.UpdateContactStatus(group.GroupID, int(state), loading)
|
||||
} else {
|
||||
log.Errorf("found group that is nil :/")
|
||||
}
|
||||
}
|
||||
case event.DeletePeer:
|
||||
log.Infof("PeerHandler got DeletePeer, SHUTTING down!\n")
|
||||
uiManager.ReloadProfiles()
|
||||
return
|
||||
case event.ChangePasswordSuccess:
|
||||
peer.SetAttribute(app.AttributeTag, constants.ProfileTypeV1Password)
|
||||
uiManager.ChangePasswordResponse(false)
|
||||
case event.ChangePasswordError:
|
||||
uiManager.ChangePasswordResponse(true)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ import (
|
|||
"cwtch.im/cwtch/event"
|
||||
libPeer "cwtch.im/cwtch/peer"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Terrible, to be replaced when proper profile/password management comes in ~ 0.2
|
||||
|
@ -19,12 +18,3 @@ var ACN connectivity.ACN
|
|||
var Peer libPeer.CwtchPeer
|
||||
var CwtchDir string
|
||||
var IPCBridge event.IPCBridge
|
||||
|
||||
type AckId struct {
|
||||
ID string
|
||||
Peer string
|
||||
Ack bool
|
||||
Error bool
|
||||
}
|
||||
|
||||
var AcknowledgementIDs sync.Map
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package gothings
|
||||
package ui
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/event"
|
||||
"cwtch.im/cwtch/protocol/connections"
|
||||
"cwtch.im/ui/go/constants"
|
||||
"cwtch.im/ui/go/cwutil"
|
||||
"github.com/therecipe/qt/qml"
|
||||
"sync"
|
||||
|
||||
"cwtch.im/ui/go/gobjects"
|
||||
"cwtch.im/ui/go/the"
|
||||
"encoding/base32"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
|
@ -18,23 +19,41 @@ import (
|
|||
type GrandCentralDispatcher struct {
|
||||
core.QObject
|
||||
|
||||
OutgoingMessages chan gobjects.Letter
|
||||
UIState InterfaceState
|
||||
QMLEngine *qml.QQmlApplicationEngine
|
||||
Translator *core.QTranslator
|
||||
QMLEngine *qml.QQmlApplicationEngine
|
||||
Translator *core.QTranslator
|
||||
|
||||
uIManagers map[string]Manager // profile-onion : Manager
|
||||
|
||||
profileLock sync.Mutex
|
||||
conversationLock sync.Mutex
|
||||
|
||||
m_selectedProfile string
|
||||
m_selectedConversation string
|
||||
|
||||
_ string `property:"os"`
|
||||
_ string `property:"currentOpenConversation"`
|
||||
_ float32 `property:"themeScale"`
|
||||
_ string `property:"version"`
|
||||
_ string `property:"buildDate"`
|
||||
_ string `property:"assetPath"`
|
||||
_ string `property:"selectedProfile,auto"`
|
||||
_ string `property:"selectedConversation,auto"`
|
||||
|
||||
// profile management stuff
|
||||
_ func() `signal:"Loaded"`
|
||||
_ func(handle, displayname, image, tag string) `signal:"AddProfile"`
|
||||
_ func() `signal:"ErrorLoaded0"`
|
||||
_ func() `signal:"ResetProfile"`
|
||||
_ func() `signal:"ResetProfileList"`
|
||||
_ func(failed bool) `signal:"ChangePasswordResponse"`
|
||||
|
||||
// contact list stuff
|
||||
_ func(handle, displayName, image, server string, badge, status int, trusted bool, blocked bool, loading bool) `signal:"AddContact"`
|
||||
_ func(handle, displayName, image, server string, badge, status int, trusted bool, blocked bool, loading bool) `signal:"UpdateContact"`
|
||||
_ func(handle string) `signal:"RemoveContact"`
|
||||
_ func(handle, key, value string) `signal:"UpdateContactAttribute"`
|
||||
_ func(handle, displayName, image, server string, badge, status int, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"`
|
||||
_ func(handle, displayName string) `signal:"UpdateContactDisplayName"`
|
||||
_ func(handle string, status int, loading bool) `signal:"UpdateContactStatus"`
|
||||
_ func(handle string, blocked bool) `signal:"UpdateContactBlocked"`
|
||||
_ func(handle string) `signal:"IncContactUnreadCount"`
|
||||
_ func(handle string) `signal:"RemoveContact"`
|
||||
_ func(handle, key, value string) `signal:"UpdateContactAttribute"`
|
||||
|
||||
// messages pane stuff
|
||||
_ func(handle, from, displayName, message, image string, mID string, fromMe bool, ts string, ackd bool, error bool) `signal:"AppendMessage"`
|
||||
|
@ -57,14 +76,26 @@ type GrandCentralDispatcher struct {
|
|||
_ func(onion, nick string, blocked bool) `signal:"SupplyPeerSettings"`
|
||||
|
||||
// signals emitted from the ui (written in go, below)
|
||||
// ui
|
||||
_ func() `signal:"onActivate,auto"`
|
||||
_ func(locale string) `signal:"setLocale,auto"`
|
||||
// profile managemenet
|
||||
_ func(onion, nick string) `signal:"updateNick,auto"`
|
||||
_ func(handle string) `signal:"loadProfile,auto"`
|
||||
_ func(nick string, defaultPass bool, password string) `signal:"createProfile,auto"`
|
||||
_ func(password string) `signal:"unlockProfiles,auto"`
|
||||
_ func() `signal:"reloadProfileList,auto"`
|
||||
_ func(onion string) `signal:"deleteProfile,auto"`
|
||||
_ func(onion, currentPassword, newPassword string, defaultPass bool) `signal:"changePassword,auto""`
|
||||
// operating a profile
|
||||
_ func(message string, mid string) `signal:"sendMessage,auto"`
|
||||
_ func(onion string) `signal:"blockPeer,auto"`
|
||||
_ func(onion string) `signal:"unblockPeer,auto"`
|
||||
_ func(onion string) `signal:"loadMessagesPane,auto"`
|
||||
_ func(signal string) `signal:"broadcast,auto"` // convenience relay signal
|
||||
_ func(str string) `signal:"importString,auto"`
|
||||
_ func(str string) `signal:"createContact,auto"`
|
||||
_ func(str string) `signal:"popup,auto"`
|
||||
_ func(nick string) `signal:"updateNick,auto"`
|
||||
_ func(server, groupName string) `signal:"createGroup,auto"`
|
||||
_ func(groupID string) `signal:"leaveGroup,auto"`
|
||||
_ func(groupID string) `signal:"acceptGroup,auto"`
|
||||
|
@ -77,9 +108,85 @@ type GrandCentralDispatcher struct {
|
|||
_ func(onion, groupID string) `signal:"inviteToGroup,auto"`
|
||||
_ func(onion, key, nick string) `signal:"setAttribute,auto"`
|
||||
_ func(onion string) `signal:"deleteContact,auto"`
|
||||
_ func(locale string) `signal:"setLocale,auto"`
|
||||
_ func() `signal:"allowUnknownPeers,auto"`
|
||||
_ func() `signal:"blockUnknownPeers,auto"`
|
||||
|
||||
_ func() `constructor:"init"`
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) init() {
|
||||
this.uIManagers = make(map[string]Manager)
|
||||
}
|
||||
|
||||
// GetUiManager gets (and creates if required) a ui Manager for the supplied profile id
|
||||
func (this *GrandCentralDispatcher) GetUiManager(profile string) Manager {
|
||||
this.profileLock.Lock()
|
||||
defer this.profileLock.Unlock()
|
||||
|
||||
if manager, exists := this.uIManagers[profile]; exists {
|
||||
return manager
|
||||
} else {
|
||||
this.uIManagers[profile] = NewManager(profile, this)
|
||||
return this.uIManagers[profile]
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) selectedProfile() string {
|
||||
this.profileLock.Lock()
|
||||
defer this.profileLock.Unlock()
|
||||
|
||||
return this.m_selectedProfile
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) setSelectedProfile(onion string) {
|
||||
this.profileLock.Lock()
|
||||
defer this.profileLock.Unlock()
|
||||
|
||||
this.m_selectedProfile = onion
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) selectedProfileChanged(onion string) {
|
||||
this.SelectedProfileChanged(onion)
|
||||
}
|
||||
|
||||
// DoIfProfile performs a gcd action for a profile IF it is the currently selected profile in the UI
|
||||
// otherwise it does nothing. it also locks profile switching for the duration of the action
|
||||
func (this *GrandCentralDispatcher) DoIfProfile(profile string, fn func()) {
|
||||
this.profileLock.Lock()
|
||||
defer this.profileLock.Unlock()
|
||||
|
||||
if this.m_selectedProfile == profile {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) selectedConversation() string {
|
||||
this.conversationLock.Lock()
|
||||
defer this.conversationLock.Unlock()
|
||||
|
||||
return this.m_selectedConversation
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) setSelectedConversation(handle string) {
|
||||
this.conversationLock.Lock()
|
||||
defer this.conversationLock.Unlock()
|
||||
|
||||
this.m_selectedConversation = handle
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) selectedConversationChanged(handle string) {
|
||||
this.SelectedConversationChanged(handle)
|
||||
}
|
||||
|
||||
// DoIfConversation performs a gcd action for a conversation IF it is the currently selected conversation in the UI
|
||||
// otherwise it does nothing. it also locks conversation switching for the duration of the action
|
||||
func (this *GrandCentralDispatcher) DoIfConversation(conversation string, fn func()) {
|
||||
this.conversationLock.Lock()
|
||||
defer this.conversationLock.Unlock()
|
||||
|
||||
if this.m_selectedConversation == conversation {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) sendMessage(message string, mID string) {
|
||||
|
@ -88,72 +195,34 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) {
|
|||
return
|
||||
}
|
||||
|
||||
if this.CurrentOpenConversation() == "" {
|
||||
if this.SelectedConversation() == "" {
|
||||
this.InvokePopup("ui error")
|
||||
return
|
||||
}
|
||||
|
||||
if len(this.CurrentOpenConversation()) == 32 { // SEND TO GROUP
|
||||
if !the.Peer.GetGroup(this.CurrentOpenConversation()).Accepted {
|
||||
err := the.Peer.AcceptInvite(this.CurrentOpenConversation())
|
||||
if isGroup(this.SelectedConversation()) {
|
||||
if !the.Peer.GetGroup(this.SelectedConversation()).Accepted {
|
||||
err := the.Peer.AcceptInvite(this.SelectedConversation())
|
||||
if err != nil {
|
||||
log.Errorf("tried to mark a nonexistent group as existed. bad!")
|
||||
return
|
||||
}
|
||||
c := this.UIState.GetContact(this.CurrentOpenConversation())
|
||||
c.Trusted = true
|
||||
this.UIState.UpdateContact(c.Handle)
|
||||
}
|
||||
|
||||
var err error
|
||||
mID, err = the.Peer.SendMessageToGroupTracked(this.CurrentOpenConversation(), message)
|
||||
mID, err = the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message)
|
||||
|
||||
this.UIState.AddMessage(&gobjects.Message{
|
||||
this.CurrentOpenConversation(),
|
||||
"me",
|
||||
"",
|
||||
message,
|
||||
"",
|
||||
true,
|
||||
mID,
|
||||
time.Now(),
|
||||
false,
|
||||
false,
|
||||
})
|
||||
this.GetUiManager(this.selectedProfile()).AddMessage(this.SelectedConversation(), "me", message, true, mID, time.Now(), false)
|
||||
|
||||
if err != nil {
|
||||
this.InvokePopup("failed to send message " + err.Error())
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
// TODO: require explicit invite accept/reject instead of implicitly trusting on send
|
||||
if !this.UIState.GetContact(this.CurrentOpenConversation()).Trusted {
|
||||
this.UIState.GetContact(this.CurrentOpenConversation()).Trusted = true
|
||||
this.UIState.UpdateContact(this.CurrentOpenConversation())
|
||||
}
|
||||
|
||||
to := this.CurrentOpenConversation()
|
||||
to := this.SelectedConversation()
|
||||
mID = the.Peer.SendMessageToPeer(to, message)
|
||||
|
||||
this.UIState.AddMessage(&gobjects.Message{
|
||||
to,
|
||||
"me",
|
||||
"",
|
||||
message,
|
||||
"",
|
||||
true,
|
||||
mID,
|
||||
time.Now(),
|
||||
false,
|
||||
false,
|
||||
})
|
||||
|
||||
ackID := new(the.AckId)
|
||||
ackID.ID = mID
|
||||
ackID.Ack = false
|
||||
ackID.Peer = to
|
||||
the.AcknowledgementIDs.Store(mID, ackID)
|
||||
this.GetUiManager(this.selectedProfile()).AddMessage(to, "me", message, true, mID, time.Now(), false)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -167,30 +236,21 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
|
|||
return
|
||||
}
|
||||
this.ClearMessages()
|
||||
this.SetCurrentOpenConversation(handle)
|
||||
c := this.UIState.GetContact(handle)
|
||||
this.SetSelectedConversation(handle)
|
||||
|
||||
if c == nil {
|
||||
this.UIState.AddContact(&gobjects.Contact{
|
||||
handle,
|
||||
handle,
|
||||
cwutil.RandomProfileImage(handle),
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
})
|
||||
} else {
|
||||
c.Badge = 0
|
||||
this.UIState.UpdateContact(handle)
|
||||
}
|
||||
|
||||
if len(handle) == 32 { // LOAD GROUP
|
||||
if isGroup(handle) { // LOAD GROUP
|
||||
group := the.Peer.GetGroup(handle)
|
||||
|
||||
loading := false
|
||||
state := connections.ConnectionStateToType[group.State]
|
||||
if state == connections.AUTHENTICATED {
|
||||
loading = true
|
||||
}
|
||||
this.UpdateContactStatus(group.GroupID, int(state), loading)
|
||||
|
||||
tl := group.GetTimeline()
|
||||
nick, _ := group.GetAttribute("nick")
|
||||
nick, _ := group.GetAttribute(constants.Nick)
|
||||
updateLastReadTime(group.GroupID)
|
||||
if nick == "" {
|
||||
this.SetToolbarTitle(handle)
|
||||
} else {
|
||||
|
@ -203,24 +263,16 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
|
|||
} else {
|
||||
handle = tl[i].PeerID
|
||||
}
|
||||
var name string
|
||||
var exists bool
|
||||
ctc := the.Peer.GetContact(tl[i].PeerID)
|
||||
if ctc != nil {
|
||||
name, exists = ctc.GetAttribute("nick")
|
||||
if !exists || name == "" {
|
||||
name = tl[i].PeerID
|
||||
}
|
||||
} else {
|
||||
name = tl[i].PeerID
|
||||
}
|
||||
|
||||
name := getOrDefault(tl[i].PeerID, constants.Nick, tl[i].PeerID)
|
||||
image := getProfilePic(tl[i].PeerID)
|
||||
|
||||
this.PrependMessage(
|
||||
handle,
|
||||
tl[i].PeerID,
|
||||
name,
|
||||
tl[i].Message,
|
||||
cwutil.RandomProfileImage(tl[i].PeerID),
|
||||
image,
|
||||
string(tl[i].Signature),
|
||||
tl[i].PeerID == the.Peer.GetProfile().Onion,
|
||||
tl[i].Timestamp.Format(constants.TIME_FORMAT),
|
||||
|
@ -232,42 +284,43 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
|
|||
} // ELSE LOAD CONTACT
|
||||
|
||||
contact, _ := the.Peer.GetProfile().GetContact(handle)
|
||||
|
||||
this.UpdateContactStatus(handle, int(connections.ConnectionStateToType[contact.State]), false)
|
||||
|
||||
var nick string
|
||||
if contact != nil {
|
||||
nick, _ := contact.GetAttribute("nick")
|
||||
nick, _ = contact.GetAttribute(constants.Nick)
|
||||
if nick == "" {
|
||||
this.SetToolbarTitle(handle)
|
||||
} else {
|
||||
this.SetToolbarTitle(nick)
|
||||
nick = handle
|
||||
}
|
||||
}
|
||||
updateLastReadTime(contact.Onion)
|
||||
this.SetToolbarTitle(nick)
|
||||
|
||||
messages := this.UIState.GetMessages(handle)
|
||||
peer := the.Peer.GetContact(handle)
|
||||
messages := peer.Timeline.GetMessages()
|
||||
for i := range messages {
|
||||
from := messages[i].From
|
||||
if messages[i].FromMe {
|
||||
from := messages[i].PeerID
|
||||
fromMe := messages[i].PeerID == the.Peer.GetProfile().Onion
|
||||
if fromMe {
|
||||
from = "me"
|
||||
}
|
||||
|
||||
ackI, ok := the.AcknowledgementIDs.Load(messages[i].MessageID)
|
||||
acked := false
|
||||
if ok {
|
||||
ack := ackI.(*the.AckId)
|
||||
acked = ack.Ack
|
||||
}
|
||||
displayname := getOrDefault(messages[i].PeerID, constants.Nick, messages[i].PeerID)
|
||||
image := getProfilePic(messages[i].PeerID)
|
||||
|
||||
this.AppendMessage(
|
||||
messages[i].Handle,
|
||||
from,
|
||||
messages[i].DisplayName,
|
||||
messages[i].PeerID,
|
||||
displayname,
|
||||
messages[i].Message,
|
||||
cwutil.RandomProfileImage(handle),
|
||||
messages[i].MessageID,
|
||||
messages[i].FromMe,
|
||||
image,
|
||||
string(messages[i].Signature),
|
||||
fromMe,
|
||||
messages[i].Timestamp.Format(constants.TIME_FORMAT),
|
||||
acked,
|
||||
messages[i].Error,
|
||||
messages[i].Acknowledged,
|
||||
messages[i].Error != "",
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,25 +338,22 @@ func (this *GrandCentralDispatcher) saveSettings(zoom, locale string) {
|
|||
return
|
||||
}
|
||||
|
||||
the.EventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{
|
||||
event.Key: constants.ZoomSetting,
|
||||
event.Data: zoom,
|
||||
}))
|
||||
the.Peer.GetProfile().SetAttribute(constants.ZoomSetting, zoom)
|
||||
the.Peer.SetAttribute(constants.ZoomSetting, zoom)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) requestPeerSettings() {
|
||||
contact := the.Peer.GetContact(this.CurrentOpenConversation())
|
||||
contact := the.Peer.GetContact(this.SelectedConversation())
|
||||
if contact == nil {
|
||||
log.Errorf("error: requested settings for unknown contact %v?", this.CurrentOpenConversation())
|
||||
this.SupplyPeerSettings(this.CurrentOpenConversation(), this.CurrentOpenConversation(), false)
|
||||
log.Errorf("error: requested settings for unknown contact %v?", this.SelectedConversation())
|
||||
this.SupplyPeerSettings(this.SelectedConversation(), this.SelectedConversation(), false)
|
||||
return
|
||||
}
|
||||
|
||||
name, exists := contact.GetAttribute("nick")
|
||||
name, exists := contact.GetAttribute(constants.Nick)
|
||||
if !exists {
|
||||
log.Errorf("error: couldn't find contact %v", this.CurrentOpenConversation())
|
||||
this.SupplyPeerSettings(this.CurrentOpenConversation(), this.CurrentOpenConversation(), contact.Blocked)
|
||||
log.Errorf("error: couldn't find contact %v", this.SelectedConversation())
|
||||
this.SupplyPeerSettings(this.SelectedConversation(), this.SelectedConversation(), contact.Blocked)
|
||||
this.SupplyPeerSettings(this.SelectedConversation(), this.SelectedConversation(), contact.Blocked)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -311,23 +361,8 @@ func (this *GrandCentralDispatcher) requestPeerSettings() {
|
|||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) savePeerSettings(onion, nick string) {
|
||||
contact := the.Peer.GetContact(onion)
|
||||
if contact == nil {
|
||||
log.Errorf("error: tried to save settings for unknown peer %v", onion)
|
||||
return
|
||||
}
|
||||
|
||||
contact.SetAttribute("nick", nick)
|
||||
the.EventBus.Publish(event.NewEvent(event.SetPeerAttribute, map[event.Field]string{
|
||||
event.RemotePeer: onion,
|
||||
event.Key: "nick",
|
||||
event.Data: nick,
|
||||
}))
|
||||
|
||||
cif, _ := this.UIState.contacts.Load(onion)
|
||||
c := cif.(*gobjects.Contact)
|
||||
c.DisplayName = nick
|
||||
this.UIState.UpdateContact(onion)
|
||||
the.Peer.SetContactAttribute(onion, constants.Nick, nick)
|
||||
this.UpdateContactDisplayName(onion, nick)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) requestGroupSettings(groupID string) {
|
||||
|
@ -338,13 +373,13 @@ func (this *GrandCentralDispatcher) requestGroupSettings(groupID string) {
|
|||
return
|
||||
}
|
||||
|
||||
nick, _ := group.GetAttribute("nick")
|
||||
nick, _ := group.GetAttribute(constants.Nick)
|
||||
invite, _ := the.Peer.ExportGroup(groupID)
|
||||
|
||||
contactaddrs := the.Peer.GetContacts()
|
||||
contactnames := make([]string, len(contactaddrs))
|
||||
for i, contact := range contactaddrs {
|
||||
name, hasname := the.Peer.GetContact(contact).GetAttribute("nick")
|
||||
name, hasname := the.Peer.GetContact(contact).GetAttribute(constants.Nick)
|
||||
if hasname {
|
||||
contactnames[i] = name
|
||||
} else {
|
||||
|
@ -356,24 +391,8 @@ func (this *GrandCentralDispatcher) requestGroupSettings(groupID string) {
|
|||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) saveGroupSettings(groupID, nick string) {
|
||||
group := the.Peer.GetGroup(groupID)
|
||||
|
||||
if group == nil {
|
||||
log.Errorf("couldn't find group %v", groupID)
|
||||
return
|
||||
}
|
||||
|
||||
group.SetAttribute("nick", nick)
|
||||
the.EventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{
|
||||
event.GroupID: groupID,
|
||||
event.Key: "nick",
|
||||
event.Data: nick,
|
||||
}))
|
||||
|
||||
cif, _ := this.UIState.contacts.Load(groupID)
|
||||
c := cif.(*gobjects.Contact)
|
||||
c.DisplayName = nick
|
||||
this.UIState.UpdateContact(groupID)
|
||||
the.Peer.SetGroupAttribute(groupID, constants.Nick, nick)
|
||||
this.UpdateContactDisplayName(groupID, nick)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) broadcast(signal string) {
|
||||
|
@ -382,9 +401,19 @@ func (this *GrandCentralDispatcher) broadcast(signal string) {
|
|||
log.Debugf("unhandled broadcast signal: %v", signal)
|
||||
case "ResetMessagePane":
|
||||
this.ResetMessagePane()
|
||||
case "ResetProfile":
|
||||
this.ResetProfile()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) createContact(onion string) {
|
||||
if contact := the.Peer.GetContact(onion); contact != nil {
|
||||
return
|
||||
}
|
||||
the.Peer.AddContact(onion, onion, false)
|
||||
the.Peer.PeerWithOnion(onion)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) importString(str string) {
|
||||
if len(str) < 5 {
|
||||
log.Debugf("ignoring short string")
|
||||
|
@ -435,7 +464,7 @@ func (this *GrandCentralDispatcher) importString(str string) {
|
|||
_, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion[:56]))
|
||||
if err != nil {
|
||||
log.Debugln(err)
|
||||
this.InvokePopup("bad format. missing characters?")
|
||||
this.InvokePopup("bad format. missing handlers?")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -443,25 +472,26 @@ func (this *GrandCentralDispatcher) importString(str string) {
|
|||
if checkc != nil {
|
||||
this.InvokePopup("already have this contact")
|
||||
return //TODO: bring them to the duplicate
|
||||
} else {
|
||||
the.Peer.AddContact(name, onion, false)
|
||||
the.Peer.PeerWithOnion(onion)
|
||||
}
|
||||
|
||||
this.UIState.AddContact(&gobjects.Contact{
|
||||
Handle: onion,
|
||||
DisplayName: name,
|
||||
Image: cwutil.RandomProfileImage(onion),
|
||||
Trusted: true,
|
||||
})
|
||||
this.GetUiManager(this.selectedProfile()).AddContact(onion)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) popup(str string) {
|
||||
this.InvokePopup(str)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) updateNick(nick string) {
|
||||
the.Peer.GetProfile().Name = nick
|
||||
the.EventBus.Publish(event.NewEvent(event.SetProfileName, map[event.Field]string{
|
||||
event.ProfileName: nick,
|
||||
}))
|
||||
func (this *GrandCentralDispatcher) updateNick(onion, nick string) {
|
||||
peer := the.CwtchApp.GetPeer(onion)
|
||||
if peer != nil {
|
||||
peer.GetProfile().Name = nick
|
||||
the.CwtchApp.GetEventBus(onion).Publish(event.NewEvent(event.SetProfileName, map[event.Field]string{
|
||||
event.ProfileName: nick,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) createGroup(server, groupName string) {
|
||||
|
@ -471,21 +501,9 @@ func (this *GrandCentralDispatcher) createGroup(server, groupName string) {
|
|||
return
|
||||
}
|
||||
|
||||
this.UIState.AddContact(&gobjects.Contact{
|
||||
Handle: groupID,
|
||||
DisplayName: groupName,
|
||||
Image: cwutil.RandomGroupImage(groupID),
|
||||
Server: server,
|
||||
Trusted: true,
|
||||
})
|
||||
this.GetUiManager(this.selectedProfile()).AddContact(groupID)
|
||||
|
||||
group := the.Peer.GetGroup(groupID)
|
||||
group.SetAttribute("nick", groupName)
|
||||
the.EventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{
|
||||
event.GroupID: groupID,
|
||||
event.Key: "nick",
|
||||
event.Data: groupName,
|
||||
}))
|
||||
the.Peer.SetGroupAttribute(groupID, constants.Nick, groupName)
|
||||
|
||||
the.Peer.JoinServer(server)
|
||||
}
|
||||
|
@ -495,7 +513,7 @@ func (this *GrandCentralDispatcher) blockPeer(onion string) {
|
|||
if err != nil {
|
||||
this.InvokePopup("Error Blocking Peer: " + err.Error())
|
||||
}
|
||||
this.UIState.UpdateContact(onion)
|
||||
this.UpdateContactBlocked(onion, true)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) unblockPeer(onion string) {
|
||||
|
@ -504,7 +522,7 @@ func (this *GrandCentralDispatcher) unblockPeer(onion string) {
|
|||
this.InvokePopup("Error Unblocking Peer: " + err.Error())
|
||||
}
|
||||
the.Peer.PeerWithOnion(onion)
|
||||
this.UIState.UpdateContact(onion)
|
||||
this.UpdateContactBlocked(onion, false)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) inviteToGroup(onion, groupID string) {
|
||||
|
@ -527,54 +545,28 @@ func (this *GrandCentralDispatcher) deleteContact(onion string) {
|
|||
func (this *GrandCentralDispatcher) acceptGroup(groupID string) {
|
||||
if the.Peer.GetGroup(groupID) != nil {
|
||||
the.Peer.AcceptInvite(groupID)
|
||||
this.UIState.UpdateContact(groupID)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) setAttribute(onion, key, value string) {
|
||||
pp, _ := the.Peer.GetProfile().GetContact(onion)
|
||||
if pp != nil {
|
||||
pp.SetAttribute(key, value)
|
||||
the.EventBus.Publish(event.NewEvent(event.SetPeerAttribute, map[event.Field]string{
|
||||
event.RemotePeer: onion,
|
||||
event.Key: key,
|
||||
event.Data: value,
|
||||
}))
|
||||
this.UIState.UpdateContactAttribute(onion, key, value)
|
||||
}
|
||||
the.Peer.SetContactAttribute(onion, key, value)
|
||||
this.GetUiManager(this.selectedProfile()).UpdateContactAttribute(onion, key, value)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) blockUnknownPeers() {
|
||||
|
||||
// Save this setting
|
||||
the.EventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{
|
||||
event.Key: constants.BlockUnknownPeersSetting,
|
||||
event.Data: "true",
|
||||
}))
|
||||
|
||||
the.Peer.GetProfile().SetAttribute(constants.BlockUnknownPeersSetting, "true")
|
||||
the.Peer.SetAttribute(constants.BlockUnknownPeersSetting, "true")
|
||||
the.EventBus.Publish(event.NewEvent(event.BlockUnknownPeers, map[event.Field]string{}))
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) allowUnknownPeers() {
|
||||
|
||||
// Save this setting
|
||||
the.EventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{
|
||||
event.Key: constants.BlockUnknownPeersSetting,
|
||||
event.Data: "false",
|
||||
}))
|
||||
|
||||
the.Peer.GetProfile().SetAttribute(constants.BlockUnknownPeersSetting, "false")
|
||||
the.Peer.SetAttribute(constants.BlockUnknownPeersSetting, "false")
|
||||
the.EventBus.Publish(event.NewEvent(event.AllowUnknownPeers, map[event.Field]string{}))
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) setLocale(locale string) {
|
||||
this.SetLocale_helper(locale)
|
||||
|
||||
the.EventBus.Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{
|
||||
event.Key: constants.LocaleSetting,
|
||||
event.Data: locale,
|
||||
}))
|
||||
the.Peer.SetAttribute(constants.LocaleSetting, locale)
|
||||
|
||||
zoom, _ := the.Peer.GetProfile().GetAttribute(constants.ZoomSetting)
|
||||
blockunkownpeers, _ := the.Peer.GetProfile().GetAttribute(constants.BlockUnknownPeersSetting)
|
||||
|
@ -582,6 +574,13 @@ func (this *GrandCentralDispatcher) setLocale(locale string) {
|
|||
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) onActivate() {
|
||||
log.Debugln("onActivate")
|
||||
if the.CwtchApp != nil {
|
||||
the.CwtchApp.QueryACNStatus()
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) SetLocale_helper(locale string) {
|
||||
core.QCoreApplication_RemoveTranslator(this.Translator)
|
||||
this.Translator = core.NewQTranslator(nil)
|
||||
|
@ -589,3 +588,72 @@ func (this *GrandCentralDispatcher) SetLocale_helper(locale string) {
|
|||
core.QCoreApplication_InstallTranslator(this.Translator)
|
||||
this.QMLEngine.Retranslate()
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) unlockProfiles(password string) {
|
||||
the.CwtchApp.LoadProfiles(password)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) loadProfile(onion string) {
|
||||
the.Peer = the.CwtchApp.GetPeer(onion)
|
||||
the.EventBus = the.CwtchApp.GetEventBus(onion)
|
||||
|
||||
pic, exists := the.Peer.GetAttribute(constants.Picture)
|
||||
if !exists {
|
||||
pic = RandomProfileImage(the.Peer.GetProfile().Onion)
|
||||
the.Peer.SetAttribute(constants.Picture, pic)
|
||||
}
|
||||
this.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, pic)
|
||||
|
||||
contacts := the.Peer.GetContacts()
|
||||
for i := range contacts {
|
||||
this.GetUiManager(this.selectedProfile()).AddContact(contacts[i])
|
||||
}
|
||||
|
||||
groups := the.Peer.GetGroups()
|
||||
for i := range groups {
|
||||
// Only join servers for active and explicitly accepted groups.
|
||||
this.GetUiManager(this.selectedProfile()).AddContact(groups[i])
|
||||
}
|
||||
|
||||
// load ui preferences
|
||||
this.RequestSettings()
|
||||
locale, exists := the.Peer.GetProfile().GetAttribute(constants.LocaleSetting)
|
||||
if exists {
|
||||
this.SetLocale_helper(locale)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) createProfile(nick string, defaultPass bool, password string) {
|
||||
if defaultPass {
|
||||
the.CwtchApp.CreateTaggedPeer(nick, the.AppPassword, constants.ProfileTypeV1DefaultPassword)
|
||||
} else {
|
||||
the.CwtchApp.CreateTaggedPeer(nick, password, constants.ProfileTypeV1Password)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) changePassword(onion, currentPassword, newPassword string, defaultPass bool) {
|
||||
tag, _ := the.CwtchApp.GetPeer(onion).GetAttribute(app.AttributeTag)
|
||||
|
||||
if tag == constants.ProfileTypeV1DefaultPassword {
|
||||
currentPassword = the.AppPassword
|
||||
}
|
||||
|
||||
if defaultPass {
|
||||
newPassword = the.AppPassword
|
||||
}
|
||||
|
||||
the.CwtchApp.ChangePeerPassword(onion, currentPassword, newPassword)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) reloadProfileList() {
|
||||
this.ResetProfileList()
|
||||
|
||||
for onion, _ := range the.CwtchApp.ListPeers() {
|
||||
AddProfile(this, onion)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) deleteProfile(onion string) {
|
||||
log.Infof("deleteProfile %v\n", onion)
|
||||
the.CwtchApp.DeletePeer(onion)
|
||||
}
|
|
@ -0,0 +1,281 @@
|
|||
package ui
|
||||
|
||||
import (
|
||||
"cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/model"
|
||||
"cwtch.im/cwtch/protocol/connections"
|
||||
"cwtch.im/ui/go/constants"
|
||||
"cwtch.im/ui/go/the"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"runtime/debug"
|
||||
"time"
|
||||
)
|
||||
|
||||
func isGroup(id string) bool {
|
||||
return len(id) == 32
|
||||
}
|
||||
|
||||
func isPeer(id string) bool {
|
||||
return len(id) == 56
|
||||
}
|
||||
|
||||
func getOrDefault(id, key, defaultVal string) string {
|
||||
var val string
|
||||
var ok bool
|
||||
if isGroup(id) {
|
||||
val, ok = the.Peer.GetGroupAttribute(id, key)
|
||||
} else {
|
||||
val, ok = the.Peer.GetContactAttribute(id, key)
|
||||
}
|
||||
if ok {
|
||||
return val
|
||||
} else {
|
||||
return defaultVal
|
||||
}
|
||||
}
|
||||
|
||||
func getWithSetDefault(id string, key, defaultVal string) string {
|
||||
var val string
|
||||
var ok bool
|
||||
if isGroup(id) {
|
||||
val, ok = the.Peer.GetGroupAttribute(id, key)
|
||||
} else {
|
||||
val, ok = the.Peer.GetContactAttribute(id, key)
|
||||
}
|
||||
if !ok {
|
||||
val = defaultVal
|
||||
if isGroup(id) {
|
||||
the.Peer.SetGroupAttribute(id, key, defaultVal)
|
||||
} else {
|
||||
the.Peer.SetContactAttribute(id, key, defaultVal)
|
||||
}
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// initLastReadTime checks and gets the Attributable's LastRead time or sets it to now
|
||||
func initLastReadTime(id string) time.Time {
|
||||
nowStr, _ := time.Now().MarshalText()
|
||||
lastReadStr := getWithSetDefault(id, constants.LastRead, string(nowStr))
|
||||
var lastRead time.Time
|
||||
lastRead.UnmarshalText([]byte(lastReadStr))
|
||||
return lastRead
|
||||
}
|
||||
|
||||
func initProfilePicture(id string) string {
|
||||
if isGroup(id) {
|
||||
return getWithSetDefault(id, constants.Picture, RandomGroupImage(id))
|
||||
} else {
|
||||
return getWithSetDefault(id, constants.Picture, RandomProfileImage(id))
|
||||
}
|
||||
}
|
||||
|
||||
// getProfilePic supplies a profile pic to use. In groups we may not have a contact so it will generate one
|
||||
func getProfilePic(id string) string {
|
||||
if isGroup(id) {
|
||||
if pic, exists := the.Peer.GetGroupAttribute(id, constants.Picture); !exists {
|
||||
return RandomGroupImage(id)
|
||||
} else {
|
||||
return pic
|
||||
}
|
||||
} else {
|
||||
if pic, exists := the.Peer.GetContactAttribute(id, constants.Picture); !exists {
|
||||
return RandomProfileImage(id)
|
||||
} else {
|
||||
return pic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateLastReadTime(id string) {
|
||||
lastRead, _ := time.Now().MarshalText()
|
||||
if isGroup(id) {
|
||||
the.Peer.SetGroupAttribute(id, constants.LastRead, string(lastRead))
|
||||
} else {
|
||||
the.Peer.SetContactAttribute(id, constants.LastRead, string(lastRead))
|
||||
}
|
||||
}
|
||||
|
||||
func countUnread(messages []model.Message, lastRead time.Time) int {
|
||||
count := 0
|
||||
for i := len(messages) - 1; i >= 0; i-- {
|
||||
if messages[i].Timestamp.After(lastRead) || messages[i].Timestamp.Equal(lastRead) {
|
||||
count++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// AddProfile adds a new profile to the UI
|
||||
func AddProfile(gcd *GrandCentralDispatcher, handle string) {
|
||||
peer := the.CwtchApp.GetPeer(handle)
|
||||
if peer != nil {
|
||||
nick := peer.GetProfile().Name
|
||||
if nick == "" {
|
||||
nick = handle
|
||||
peer.SetAttribute(constants.Nick, nick)
|
||||
}
|
||||
|
||||
pic, ok := peer.GetAttribute(constants.Picture)
|
||||
if !ok {
|
||||
pic = RandomProfileImage(handle)
|
||||
peer.SetAttribute(constants.Picture, pic)
|
||||
}
|
||||
|
||||
tag, _ := peer.GetAttribute(app.AttributeTag)
|
||||
log.Infof("AddProfile %v %v %v %v\n", handle, nick, pic, tag)
|
||||
gcd.AddProfile(handle, nick, pic, tag)
|
||||
}
|
||||
}
|
||||
|
||||
type manager struct {
|
||||
gcd *GrandCentralDispatcher
|
||||
profile string
|
||||
}
|
||||
|
||||
// Manager is a middleware helper for entities like peer event listeners wishing to trigger ui changes (via the gcd)
|
||||
// each manager is for one profile/peer
|
||||
// manager takes minimal arguments and builds the full struct of data (usually pulled from a cwtch peer) required to call the GCD to perform the ui action
|
||||
// manager also performs call filtering based on UI state: users of manager can safely always call it on events and not have to worry about weather the relevant ui is active
|
||||
// ie: you can always safely call AddMessage even if in the ui a different profile is selected. manager will check with gcd, and if the correct conditions are not met, it will not call on gcd to update the ui incorrectly
|
||||
type Manager interface {
|
||||
Acknowledge(mID string)
|
||||
AddContact(Handle string)
|
||||
AddSendMessageError(peer string, signature string, err string)
|
||||
AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool)
|
||||
|
||||
ReloadProfiles()
|
||||
|
||||
UpdateContactDisplayName(handle string, name string)
|
||||
UpdateContactStatus(handle string, status int, loading bool)
|
||||
UpdateContactAttribute(handle, key, value string)
|
||||
|
||||
ChangePasswordResponse(error bool)
|
||||
}
|
||||
|
||||
// NewManager returns a new Manager interface for a profile to the gcd
|
||||
func NewManager(profile string, gcd *GrandCentralDispatcher) Manager {
|
||||
return &manager{gcd: gcd, profile: profile}
|
||||
}
|
||||
|
||||
// Acknowledge acknowledges the given message id in the UI
|
||||
func (this *manager) Acknowledge(mID string) {
|
||||
this.gcd.DoIfProfile(this.profile, func() {
|
||||
this.gcd.Acknowledged(mID)
|
||||
})
|
||||
}
|
||||
|
||||
func getLastMessageTime(tl *model.Timeline) int {
|
||||
if len(tl.Messages) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return int(tl.Messages[len(tl.Messages)-1].Timestamp.Unix())
|
||||
}
|
||||
|
||||
// AddContact adds a new contact to the ui for this manager's profile
|
||||
func (this *manager) AddContact(Handle string) {
|
||||
this.gcd.DoIfProfile(this.profile, func() {
|
||||
|
||||
if isGroup(Handle) {
|
||||
group := the.Peer.GetGroup(Handle)
|
||||
if group != nil {
|
||||
lastRead := initLastReadTime(group.GroupID)
|
||||
unread := countUnread(group.Timeline.GetMessages(), lastRead)
|
||||
picture := initProfilePicture(Handle)
|
||||
nick, exists := group.GetAttribute(constants.Nick)
|
||||
if !exists {
|
||||
nick = Handle
|
||||
}
|
||||
|
||||
this.gcd.AddContact(Handle, nick, picture, group.GroupServer, unread, int(connections.ConnectionStateToType[group.State]), false, false, getLastMessageTime(&group.Timeline))
|
||||
}
|
||||
return
|
||||
} else if !isPeer(Handle) {
|
||||
log.Errorf("sorry, unable to handle AddContact(%v)", Handle)
|
||||
debug.PrintStack()
|
||||
return
|
||||
}
|
||||
|
||||
contact := the.Peer.GetContact(Handle)
|
||||
if contact != nil {
|
||||
lastRead := initLastReadTime(contact.Onion)
|
||||
unread := countUnread(contact.Timeline.GetMessages(), lastRead)
|
||||
picture := initProfilePicture(Handle)
|
||||
nick, exists := contact.GetAttribute(constants.Nick)
|
||||
if !exists {
|
||||
nick = Handle
|
||||
}
|
||||
|
||||
this.gcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false, getLastMessageTime(&contact.Timeline))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AddSendMessageError adds an error not and icon to a message in a conversation in the ui for the message identified by the peer/sig combo
|
||||
func (this *manager) AddSendMessageError(peer string, signature string, err string) {
|
||||
this.gcd.DoIfProfile(this.profile, func() {
|
||||
this.gcd.DoIfConversation(peer, func() {
|
||||
log.Debugf("Received Error Sending Message: %v", err)
|
||||
// FIXME: Sometimes, for the first Peer message we send our error beats our message to the UI
|
||||
time.Sleep(time.Second * 1)
|
||||
this.gcd.GroupSendError(signature, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// AddMessage adds a message to the message pane for the supplied conversation if it is active
|
||||
func (this *manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) {
|
||||
this.gcd.DoIfProfile(this.profile, func() {
|
||||
|
||||
nick := getOrDefault(handle, constants.Nick, handle)
|
||||
image := getProfilePic(handle)
|
||||
|
||||
// If we have this group loaded already
|
||||
this.gcd.DoIfConversation(handle, func() {
|
||||
updateLastReadTime(handle)
|
||||
// If the message is not from the user then add it, otherwise, just acknowledge.
|
||||
if !fromMe {
|
||||
this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Format(constants.TIME_FORMAT), false, false)
|
||||
} else {
|
||||
if !Acknowledged {
|
||||
this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Format(constants.TIME_FORMAT), false, false)
|
||||
} else {
|
||||
this.gcd.Acknowledged(messageID)
|
||||
}
|
||||
}
|
||||
})
|
||||
this.gcd.IncContactUnreadCount(handle)
|
||||
})
|
||||
}
|
||||
|
||||
func (this *manager) ReloadProfiles() {
|
||||
this.gcd.reloadProfileList()
|
||||
}
|
||||
|
||||
// UpdateContactDisplayName updates a contact's display name in the contact list and conversations
|
||||
func (this *manager) UpdateContactDisplayName(handle string, name string) {
|
||||
this.gcd.DoIfProfile(this.profile, func() {
|
||||
this.gcd.UpdateContactDisplayName(handle, name)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateContactStatus updates a contact's status in the ui
|
||||
func (this *manager) UpdateContactStatus(handle string, status int, loading bool) {
|
||||
this.gcd.DoIfProfile(this.profile, func() {
|
||||
this.gcd.UpdateContactStatus(handle, status, loading)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateContactAttribute update's a contacts attribute in the ui
|
||||
func (this *manager) UpdateContactAttribute(handle, key, value string) {
|
||||
this.gcd.DoIfProfile(this.profile, func() {
|
||||
this.gcd.UpdateContactAttribute(handle, key, value)
|
||||
})
|
||||
}
|
||||
|
||||
func (this *manager) ChangePasswordResponse(error bool) {
|
||||
this.gcd.ChangePasswordResponse(error)
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
package cwutil
|
||||
package ui
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
"strings"
|
||||
)
|
||||
|
@ -23,7 +22,7 @@ func RandomGroupImage(handle string) string {
|
|||
choices := []string{"001-borobudur", "002-opera-house", "003-burj-al-arab", "004-chrysler", "005-acropolis", "006-empire-state-building", "007-temple", "008-indonesia-1", "009-new-zealand", "010-notre-dame", "011-space-needle", "012-seoul", "013-mosque", "014-milan", "015-statue", "016-pyramid", "017-cologne", "018-brandenburg-gate", "019-berlin-cathedral", "020-hungarian-parliament", "021-buckingham", "022-thailand", "023-independence", "024-angkor-wat", "025-vaticano", "026-christ-the-redeemer", "027-colosseum", "028-golden-gate-bridge", "029-sphinx", "030-statue-of-liberty", "031-cradle-of-humankind", "032-istanbul", "033-london-eye", "034-sagrada-familia", "035-tower-bridge", "036-burj-khalifa", "037-washington", "038-big-ben", "039-stonehenge", "040-white-house", "041-ahu-tongariki", "042-capitol", "043-eiffel-tower", "044-church-of-the-savior-on-spilled-blood", "045-arc-de-triomphe", "046-windmill", "047-louvre", "048-torii-gate", "049-petronas", "050-matsumoto-castle", "051-fuji", "052-temple-of-heaven", "053-pagoda", "054-chichen-itza", "055-forbidden-city", "056-merlion", "057-great-wall-of-china", "058-taj-mahal", "059-pisa", "060-indonesia"}
|
||||
barr, err := hex.DecodeString(handle)
|
||||
if err != nil || len(barr) == 0 {
|
||||
fmt.Printf("error: %v %v %v\n", handle, err, barr)
|
||||
log.Errorf("error: %v %v %v\n", handle, err, barr)
|
||||
return "qrc:/qml/images/extra/openprivacy.png"
|
||||
}
|
||||
return "servers/" + choices[int(barr[0])%len(choices)] + ".png"
|
|
@ -4,239 +4,469 @@
|
|||
<context>
|
||||
<name>AddGroupPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="19"/>
|
||||
<source>create-group-title</source>
|
||||
<translation type="vanished">Gruppe Anlegen</translation>
|
||||
<translation>Gruppe Anlegen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="42"/>
|
||||
<source>server-label</source>
|
||||
<extracomment>Server label</extracomment>
|
||||
<translation type="vanished">Server</translation>
|
||||
<translation>Server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="53"/>
|
||||
<source>group-name-label</source>
|
||||
<extracomment>Group name label</extracomment>
|
||||
<translation type="vanished">Gruppenname</translation>
|
||||
<translation>Gruppenname</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="60"/>
|
||||
<source>default-group-name</source>
|
||||
<extracomment>default suggested group name</extracomment>
|
||||
<translation type="vanished">Tolle Gruppe</translation>
|
||||
<translation>Tolle Gruppe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="65"/>
|
||||
<source>create-group-btn</source>
|
||||
<extracomment>create group button</extracomment>
|
||||
<translation type="vanished">Anlegen</translation>
|
||||
<translation>Anlegen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BulletinOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="203"/>
|
||||
<source>new-bulletin-label</source>
|
||||
<translation type="vanished">Neue Meldung</translation>
|
||||
<translation>Neue Meldung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="215"/>
|
||||
<source>post-new-bulletin-label</source>
|
||||
<extracomment>Post a new Bulletin Post</extracomment>
|
||||
<translation type="vanished">Neue Meldung veröffentlichen</translation>
|
||||
<translation>Neue Meldung veröffentlichen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="221"/>
|
||||
<source>title-placeholder</source>
|
||||
<extracomment>title place holder text</extracomment>
|
||||
<translation type="vanished">Titel...</translation>
|
||||
<translation>Titel...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GroupSettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="42"/>
|
||||
<source>server-label</source>
|
||||
<translation type="vanished">Server</translation>
|
||||
<translation>Server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="53"/>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="74"/>
|
||||
<source>copy-btn</source>
|
||||
<translation type="vanished">Kopieren</translation>
|
||||
<translation>Kopieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="63"/>
|
||||
<source>invitation-label</source>
|
||||
<translation type="vanished">Einladung</translation>
|
||||
<translation>Einladung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="84"/>
|
||||
<source>group-name-label</source>
|
||||
<translation type="vanished">Gruppenname</translation>
|
||||
<translation>Gruppenname</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="93"/>
|
||||
<source>save-btn</source>
|
||||
<translation type="vanished">Speichern</translation>
|
||||
<translation>Speichern</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="103"/>
|
||||
<source>invite-to-group-label</source>
|
||||
<extracomment>Invite someone to the group</extracomment>
|
||||
<translation type="vanished">In die Gruppe einladen</translation>
|
||||
<translation>In die Gruppe einladen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="114"/>
|
||||
<source>invite-btn</source>
|
||||
<translation type="vanished">Einladen</translation>
|
||||
<translation>Einladen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="123"/>
|
||||
<source>delete-btn</source>
|
||||
<translation type="vanished">Löschen</translation>
|
||||
<translation>Löschen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>InplaceEditText</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/InplaceEditText.qml" line="85"/>
|
||||
<source>Update</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ListOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="184"/>
|
||||
<source>add-list-item</source>
|
||||
<extracomment>Add a New List Item</extracomment>
|
||||
<translation type="vanished">Liste hinzufügen</translation>
|
||||
<translation>Liste hinzufügen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="196"/>
|
||||
<source>add-new-item</source>
|
||||
<extracomment>Add a new item to the list</extracomment>
|
||||
<translation type="vanished">Neues Listenelement hinzüfgen</translation>
|
||||
<translation>Neues Listenelement hinzüfgen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="202"/>
|
||||
<source>todo-placeholder</source>
|
||||
<extracomment>Todo... placeholder text</extracomment>
|
||||
<translation type="vanished">noch zu erledigen</translation>
|
||||
<translation>noch zu erledigen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MembershipOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/MembershipOverlay.qml" line="21"/>
|
||||
<source>membership-description</source>
|
||||
<extracomment>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.</extracomment>
|
||||
<translation type="vanished">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.</translation>
|
||||
<translation>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.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Message</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="56"/>
|
||||
<source>dm-tooltip</source>
|
||||
<extracomment>Click to DM</extracomment>
|
||||
<translation type="vanished">Klicken, um DM zu senden</translation>
|
||||
<translation>Klicken, um DM zu senden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>could-not-send-msg-error</source>
|
||||
<extracomment>Could not send this message</extracomment>
|
||||
<translation type="vanished">Nachricht konnte nicht gesendet werden</translation>
|
||||
<translation>Nachricht konnte nicht gesendet werden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>acknowledged-label</source>
|
||||
<translation type="vanished">bestätigt</translation>
|
||||
<translation>bestätigt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>pending-label</source>
|
||||
<translation type="vanished">Bestätigung ausstehend</translation>
|
||||
<translation>Bestätigung ausstehend</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MyProfile</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="185"/>
|
||||
<source>copy-btn</source>
|
||||
<extracomment>Button for copying profile onion address to clipboard</extracomment>
|
||||
<translation type="vanished">Kopieren</translation>
|
||||
<translation>Kopieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="189"/>
|
||||
<source>copied-clipboard-notification</source>
|
||||
<extracomment>Copied to clipboard</extracomment>
|
||||
<translation type="vanished">in die Zwischenablage kopiert</translation>
|
||||
<translation>in die Zwischenablage kopiert</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="219"/>
|
||||
<source>new-group-btn</source>
|
||||
<extracomment>create new group button</extracomment>
|
||||
<translation type="vanished">Neue Gruppe anlegen</translation>
|
||||
<translation>Neue Gruppe anlegen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="229"/>
|
||||
<source>paste-address-to-add-contact</source>
|
||||
<extracomment>ex: "... paste an address here to add a contact ..."</extracomment>
|
||||
<translation type="vanished">Adresse hier hinzufügen, um einen Kontakt aufzunehmen</translation>
|
||||
<translation>Adresse hier hinzufügen, um einen Kontakt aufzunehmen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OverlayPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="44"/>
|
||||
<source>accept-group-invite-label</source>
|
||||
<extracomment>Do you want to accept the invitation to $GROUP</extracomment>
|
||||
<translation type="vanished">Möchtest Du die Einladung annehmen</translation>
|
||||
<translation>Möchtest Du die Einladung annehmen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="49"/>
|
||||
<source>accept-group-btn</source>
|
||||
<extracomment>Accept group invite button</extracomment>
|
||||
<translation type="vanished">Annehmen</translation>
|
||||
<translation>Annehmen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="59"/>
|
||||
<source>reject-group-btn</source>
|
||||
<extracomment>Reject Group invite button</extracomment>
|
||||
<translation type="vanished">Ablehnen</translation>
|
||||
<translation>Ablehnen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="73"/>
|
||||
<source>chat-btn</source>
|
||||
<translation type="vanished">Chat</translation>
|
||||
<translation>Chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="80"/>
|
||||
<source>lists-btn</source>
|
||||
<translation type="vanished">Listen</translation>
|
||||
<translation>Listen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="87"/>
|
||||
<source>bulletins-btn</source>
|
||||
<translation type="vanished">Meldungen</translation>
|
||||
<translation>Meldungen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="94"/>
|
||||
<source>puzzle-game-btn</source>
|
||||
<translation type="vanished">Puzzlespiel</translation>
|
||||
<translation>Puzzlespiel</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PeerSettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="42"/>
|
||||
<source>address-label</source>
|
||||
<translation type="vanished">Adresse</translation>
|
||||
<translation>Adresse</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="53"/>
|
||||
<source>copy-btn</source>
|
||||
<translation type="vanished">Kopieren</translation>
|
||||
<translation>Kopieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="57"/>
|
||||
<source>copied-to-clipboard-notification</source>
|
||||
<extracomment>notification: copied to clipboard</extracomment>
|
||||
<translation type="vanished">in die Zwischenablage kopiert</translation>
|
||||
<translation>in die Zwischenablage kopiert</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="64"/>
|
||||
<source>display-name-label</source>
|
||||
<translation type="vanished">Angezeigter Name</translation>
|
||||
<translation>Angezeigter Name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="73"/>
|
||||
<source>save-btn</source>
|
||||
<translation type="vanished">speichern</translation>
|
||||
<translation>speichern</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="85"/>
|
||||
<source>block-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="85"/>
|
||||
<source>unblock-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="99"/>
|
||||
<source>delete-btn</source>
|
||||
<translation type="vanished">löschen</translation>
|
||||
<translation>löschen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileAddEditPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="24"/>
|
||||
<source>add-profile-title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="24"/>
|
||||
<source>edit-profile-title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="92"/>
|
||||
<source>profile-name</source>
|
||||
<extracomment>Display name</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="32"/>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="100"/>
|
||||
<source>default-profile-name</source>
|
||||
<extracomment>default suggested profile name</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="81"/>
|
||||
<source>profile-onion-label</source>
|
||||
<extracomment>Onion</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="112"/>
|
||||
<source>radio-use-password</source>
|
||||
<extracomment>Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="122"/>
|
||||
<source>radio-no-password</source>
|
||||
<extracomment>Unencrypted (No password)</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="133"/>
|
||||
<source>no-password-warning</source>
|
||||
<extracomment>Not using a password on this account means that all data stored locally will not be encrypted</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="140"/>
|
||||
<source>current-password-label</source>
|
||||
<extracomment>Current Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="154"/>
|
||||
<source>password1-label</source>
|
||||
<extracomment>Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="174"/>
|
||||
<source>password2-label</source>
|
||||
<extracomment>Reenter password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="188"/>
|
||||
<source>create-profile-btn</source>
|
||||
<extracomment>Create Profile || Save Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="188"/>
|
||||
<source>save-profile-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="217"/>
|
||||
<source>password-error-match</source>
|
||||
<extracomment>Passwords do not match</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="225"/>
|
||||
<source>password-change-error</source>
|
||||
<extracomment>Error changing password: Supplied password rejected</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="234"/>
|
||||
<source>delete-profile-btn</source>
|
||||
<extracomment>Delete Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="247"/>
|
||||
<source>delete-confirm-label</source>
|
||||
<extracomment>Type DELETE to confirm</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="263"/>
|
||||
<source>delete-profile-confirm-btn</source>
|
||||
<extracomment>Really Delete Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="269"/>
|
||||
<source>delete-confirm-text</source>
|
||||
<extracomment>DELETE</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileList</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/ProfileList.qml" line="88"/>
|
||||
<location filename="../qml/widgets/ProfileList.qml" line="101"/>
|
||||
<source>add-new-profile-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileManagerPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="26"/>
|
||||
<source>enter-profile-password</source>
|
||||
<extracomment>Please enter password:</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="42"/>
|
||||
<source>error-0-profiles-loaded-for-password</source>
|
||||
<extracomment>0 profiles loaded with that password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="52"/>
|
||||
<source>unlock</source>
|
||||
<extracomment>Unlock</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="21"/>
|
||||
<source>cwtch-settings-title</source>
|
||||
<extracomment>Cwtch Settings title</extracomment>
|
||||
<translation type="vanished">Cwtch Einstellungen</translation>
|
||||
<translation>Cwtch Einstellungen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="47"/>
|
||||
<source>version %1 builddate %2</source>
|
||||
<extracomment>Version: %1 Built on: %2</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="54"/>
|
||||
<source>zoom-label</source>
|
||||
<extracomment>Interface zoom (mostly affects text and button sizes)</extracomment>
|
||||
<translation type="vanished">Benutzeroberflächen-Zoom (betriftt hauptsächlich Text- und Knopgrößen)</translation>
|
||||
<translation>Benutzeroberflächen-Zoom (betriftt hauptsächlich Text- und Knopgrößen)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="83"/>
|
||||
<source>block-unknown-label</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="90"/>
|
||||
<source>large-text-label</source>
|
||||
<translation type="vanished">Groß</translation>
|
||||
<translation>Groß</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="98"/>
|
||||
<source>default-scaling-text</source>
|
||||
<extracomment>"Default size text (scale factor: "</extracomment>
|
||||
<translation type="vanished">defaultmäßige Textgröße (Skalierungsfaktor:</translation>
|
||||
<translation>defaultmäßige Textgröße (Skalierungsfaktor:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="102"/>
|
||||
<source>small-text-label</source>
|
||||
<translation type="vanished">Klein</translation>
|
||||
<translation>Klein</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>StackToolbar</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/StackToolbar.qml" line="58"/>
|
||||
<source>view-group-membership-tooltip</source>
|
||||
<extracomment>View Group Membership</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
Binary file not shown.
|
@ -4,264 +4,469 @@
|
|||
<context>
|
||||
<name>AddGroupPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="19"/>
|
||||
<source>create-group-title</source>
|
||||
<translation type="vanished">Create Group</translation>
|
||||
<translation>Create Group</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="42"/>
|
||||
<source>server-label</source>
|
||||
<extracomment>Server label</extracomment>
|
||||
<translation type="vanished">Server</translation>
|
||||
<translation>Server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="53"/>
|
||||
<source>group-name-label</source>
|
||||
<extracomment>Group name label</extracomment>
|
||||
<translation type="vanished">Group name</translation>
|
||||
<translation>Group name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="60"/>
|
||||
<source>default-group-name</source>
|
||||
<extracomment>default suggested group name</extracomment>
|
||||
<translation type="vanished">Awesome Group</translation>
|
||||
<translation>Awesome Group</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="65"/>
|
||||
<source>create-group-btn</source>
|
||||
<extracomment>create group button</extracomment>
|
||||
<translation type="vanished">Create</translation>
|
||||
<translation>Create</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BulletinOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="203"/>
|
||||
<source>new-bulletin-label</source>
|
||||
<translation type="vanished">New Bulletin</translation>
|
||||
<translation>New Bulletin</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="215"/>
|
||||
<source>post-new-bulletin-label</source>
|
||||
<extracomment>Post a new Bulletin Post</extracomment>
|
||||
<translation type="vanished">Post new bulletin</translation>
|
||||
<translation>Post new bulletin</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="221"/>
|
||||
<source>title-placeholder</source>
|
||||
<extracomment>title place holder text</extracomment>
|
||||
<translation type="vanished">title...</translation>
|
||||
<translation>title...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GroupSettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="42"/>
|
||||
<source>server-label</source>
|
||||
<translation type="vanished">Server</translation>
|
||||
<translation>Server</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="53"/>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="74"/>
|
||||
<source>copy-btn</source>
|
||||
<translation type="vanished">Copy</translation>
|
||||
<translation>Copy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="63"/>
|
||||
<source>invitation-label</source>
|
||||
<translation type="vanished">Invitation</translation>
|
||||
<translation>Invitation</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="84"/>
|
||||
<source>group-name-label</source>
|
||||
<translation type="vanished">Group Name</translation>
|
||||
<translation>Group Name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="93"/>
|
||||
<source>save-btn</source>
|
||||
<translation type="vanished">Save</translation>
|
||||
<translation>Save</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="103"/>
|
||||
<source>invite-to-group-label</source>
|
||||
<extracomment>Invite someone to the group</extracomment>
|
||||
<translation type="vanished">Invite to group</translation>
|
||||
<translation>Invite to group</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="114"/>
|
||||
<source>invite-btn</source>
|
||||
<translation type="vanished">Invite</translation>
|
||||
<translation>Invite</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="123"/>
|
||||
<source>delete-btn</source>
|
||||
<translation type="vanished">Delete</translation>
|
||||
<translation>Delete</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>InplaceEditText</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/InplaceEditText.qml" line="85"/>
|
||||
<source>Update</source>
|
||||
<translation>Update</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ListOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="184"/>
|
||||
<source>add-list-item</source>
|
||||
<extracomment>Add a New List Item</extracomment>
|
||||
<translation type="vanished">Add a New List Item</translation>
|
||||
<translation>Add a New List Item</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="196"/>
|
||||
<source>add-new-item</source>
|
||||
<extracomment>Add a new item to the list</extracomment>
|
||||
<translation type="vanished">Add a new item to the list</translation>
|
||||
<translation>Add a new item to the list</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="202"/>
|
||||
<source>todo-placeholder</source>
|
||||
<extracomment>Todo... placeholder text</extracomment>
|
||||
<translation type="vanished">Todo...</translation>
|
||||
<translation>Todo...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MembershipOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/MembershipOverlay.qml" line="21"/>
|
||||
<source>membership-description</source>
|
||||
<extracomment>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.</extracomment>
|
||||
<translation type="vanished">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.</translation>
|
||||
<translation>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.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Message</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="56"/>
|
||||
<source>dm-tooltip</source>
|
||||
<extracomment>Click to DM</extracomment>
|
||||
<translation type="vanished">Click to DM</translation>
|
||||
<translation>Click to DM</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>could-not-send-msg-error</source>
|
||||
<extracomment>Could not send this message</extracomment>
|
||||
<translation type="vanished">Could not send this message</translation>
|
||||
<translation>Could not send this message</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>acknowledged-label</source>
|
||||
<translation type="vanished">Acknowledged</translation>
|
||||
<translation>Acknowledged</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>pending-label</source>
|
||||
<translation type="vanished">Pending</translation>
|
||||
<translation>Pending</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MyProfile</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="185"/>
|
||||
<source>copy-btn</source>
|
||||
<extracomment>Button for copying profile onion address to clipboard</extracomment>
|
||||
<translation type="vanished">Copy</translation>
|
||||
<translation>Copy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="189"/>
|
||||
<source>copied-clipboard-notification</source>
|
||||
<extracomment>Copied to clipboard</extracomment>
|
||||
<translation type="vanished">Copied to clipboard</translation>
|
||||
<translation>Copied to clipboard</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="219"/>
|
||||
<source>new-group-btn</source>
|
||||
<extracomment>create new group button</extracomment>
|
||||
<translation type="vanished">Create new group</translation>
|
||||
<translation>Create new group</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="229"/>
|
||||
<source>paste-address-to-add-contact</source>
|
||||
<extracomment>ex: "... paste an address here to add a contact ..."</extracomment>
|
||||
<translation type="vanished">... paste an address here to add a contact...</translation>
|
||||
<translation>... paste an address here to add a contact...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OverlayPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="44"/>
|
||||
<source>accept-group-invite-label</source>
|
||||
<extracomment>Do you want to accept the invitation to $GROUP</extracomment>
|
||||
<translation type="vanished">Do you want to accept the invitation to</translation>
|
||||
<translation>Do you want to accept the invitation to</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="49"/>
|
||||
<source>accept-group-btn</source>
|
||||
<extracomment>Accept group invite button</extracomment>
|
||||
<translation type="vanished">Accept</translation>
|
||||
<translation>Accept</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="59"/>
|
||||
<source>reject-group-btn</source>
|
||||
<extracomment>Reject Group invite button</extracomment>
|
||||
<translation type="vanished">Reject</translation>
|
||||
<translation>Reject</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="73"/>
|
||||
<source>chat-btn</source>
|
||||
<translation type="vanished">Chat</translation>
|
||||
<translation>Chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="80"/>
|
||||
<source>lists-btn</source>
|
||||
<translation type="vanished">Lists</translation>
|
||||
<translation>Lists</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="87"/>
|
||||
<source>bulletins-btn</source>
|
||||
<translation type="vanished">Bulletins</translation>
|
||||
<translation>Bulletins</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="94"/>
|
||||
<source>puzzle-game-btn</source>
|
||||
<translation type="vanished">Puzzle Game</translation>
|
||||
<translation>Puzzle Game</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PeerSettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="42"/>
|
||||
<source>address-label</source>
|
||||
<translation type="vanished">Address</translation>
|
||||
<translation>Address</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="53"/>
|
||||
<source>copy-btn</source>
|
||||
<translation type="vanished">Copy</translation>
|
||||
<translation>Copy</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="57"/>
|
||||
<source>copied-to-clipboard-notification</source>
|
||||
<extracomment>notification: copied to clipboard</extracomment>
|
||||
<translation type="vanished">Copied to Clipboard</translation>
|
||||
<translation>Copied to Clipboard</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="64"/>
|
||||
<source>display-name-label</source>
|
||||
<translation type="vanished">Display Name</translation>
|
||||
<translation>Display Name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="73"/>
|
||||
<source>save-btn</source>
|
||||
<translation type="vanished">Save</translation>
|
||||
<translation>Save</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="85"/>
|
||||
<source>block-btn</source>
|
||||
<translation type="vanished">Block Peer</translation>
|
||||
<translation>Block Peer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="85"/>
|
||||
<source>unblock-btn</source>
|
||||
<translation type="vanished">Unblock Peer</translation>
|
||||
<translation>Unblock Peer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="99"/>
|
||||
<source>delete-btn</source>
|
||||
<translation type="vanished">Delete</translation>
|
||||
<translation>Delete</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileAddEditPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="24"/>
|
||||
<source>add-profile-title</source>
|
||||
<translation>Add new profile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="24"/>
|
||||
<source>edit-profile-title</source>
|
||||
<translation>Edit Profile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="92"/>
|
||||
<source>profile-name</source>
|
||||
<extracomment>Display name</extracomment>
|
||||
<translation>Display name</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="32"/>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="100"/>
|
||||
<source>default-profile-name</source>
|
||||
<extracomment>default suggested profile name</extracomment>
|
||||
<translation>Alice</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="81"/>
|
||||
<source>profile-onion-label</source>
|
||||
<extracomment>Onion</extracomment>
|
||||
<translation>Onion</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="112"/>
|
||||
<source>radio-use-password</source>
|
||||
<extracomment>Password</extracomment>
|
||||
<translation>Password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="122"/>
|
||||
<source>radio-no-password</source>
|
||||
<extracomment>Unencrypted (No password)</extracomment>
|
||||
<translation>Unencrypted (No password)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="133"/>
|
||||
<source>no-password-warning</source>
|
||||
<extracomment>Not using a password on this account means that all data stored locally will not be encrypted</extracomment>
|
||||
<translation>Not using a password on this account means that all data stored locally will not be encrypted</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="140"/>
|
||||
<source>current-password-label</source>
|
||||
<extracomment>Current Password</extracomment>
|
||||
<translation>Current Password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="154"/>
|
||||
<source>password1-label</source>
|
||||
<extracomment>Password</extracomment>
|
||||
<translation>Password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="174"/>
|
||||
<source>password2-label</source>
|
||||
<extracomment>Reenter password</extracomment>
|
||||
<translation>Reenter password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="188"/>
|
||||
<source>create-profile-btn</source>
|
||||
<extracomment>Create Profile || Save Profile</extracomment>
|
||||
<translation>Create Profile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="188"/>
|
||||
<source>save-profile-btn</source>
|
||||
<translation>Save Profile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="217"/>
|
||||
<source>password-error-match</source>
|
||||
<extracomment>Passwords do not match</extracomment>
|
||||
<translation>Passwords do not match</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="225"/>
|
||||
<source>password-change-error</source>
|
||||
<extracomment>Error changing password: Supplied password rejected</extracomment>
|
||||
<translation>Error changing password: Supplied password rejected</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="234"/>
|
||||
<source>delete-profile-btn</source>
|
||||
<extracomment>Delete Profile</extracomment>
|
||||
<translation>Delete Profile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="247"/>
|
||||
<source>delete-confirm-label</source>
|
||||
<extracomment>Type DELETE to confirm</extracomment>
|
||||
<translation>Type DELETE to confirm</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="263"/>
|
||||
<source>delete-profile-confirm-btn</source>
|
||||
<extracomment>Really Delete Profile</extracomment>
|
||||
<translation>Really Delete Profile</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="269"/>
|
||||
<source>delete-confirm-text</source>
|
||||
<extracomment>DELETE</extracomment>
|
||||
<translation>DELETE</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileList</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/ProfileList.qml" line="88"/>
|
||||
<location filename="../qml/widgets/ProfileList.qml" line="101"/>
|
||||
<source>add-new-profile-btn</source>
|
||||
<translation>Add new profile</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileManagerPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="26"/>
|
||||
<source>enter-profile-password</source>
|
||||
<extracomment>Please enter password:</extracomment>
|
||||
<translation>Please enter password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="42"/>
|
||||
<source>error-0-profiles-loaded-for-password</source>
|
||||
<extracomment>0 profiles loaded with that password</extracomment>
|
||||
<translation>0 profiles loaded with that password</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="52"/>
|
||||
<source>unlock</source>
|
||||
<extracomment>Unlock</extracomment>
|
||||
<translation>Unlock</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="21"/>
|
||||
<source>cwtch-settings-title</source>
|
||||
<extracomment>Cwtch Settings title</extracomment>
|
||||
<translation type="vanished">Cwtch Settings</translation>
|
||||
<translation>Cwtch Settings</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="47"/>
|
||||
<source>version %1 builddate %2</source>
|
||||
<extracomment>Version: %1 Built on: %2</extracomment>
|
||||
<translation type="vanished">Version: %1 Built on: %2</translation>
|
||||
<translation>Version: %1 Built on: %2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="54"/>
|
||||
<source>zoom-label</source>
|
||||
<extracomment>Interface zoom (mostly affects text and button sizes)</extracomment>
|
||||
<translation type="vanished">Interface zoom (mostly affects text and button sizes)</translation>
|
||||
<translation>Interface zoom (mostly affects text and button sizes)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="83"/>
|
||||
<source>block-unknown-label</source>
|
||||
<translation type="vanished">Block Unknown Peers</translation>
|
||||
<translation>Block Unknown Peers</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="90"/>
|
||||
<source>large-text-label</source>
|
||||
<translation type="vanished">Large</translation>
|
||||
<translation>Large</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="98"/>
|
||||
<source>default-scaling-text</source>
|
||||
<extracomment>"Default size text (scale factor: "</extracomment>
|
||||
<translation type="vanished">Default size text (scale factor:</translation>
|
||||
<translation>Default size text (scale factor:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="102"/>
|
||||
<source>small-text-label</source>
|
||||
<translation type="vanished">Small</translation>
|
||||
<translation>Small</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>StackToolbar</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/StackToolbar.qml" line="58"/>
|
||||
<source>view-group-membership-tooltip</source>
|
||||
<extracomment>View Group Membership</extracomment>
|
||||
<translation type="vanished">View Group Membership</translation>
|
||||
<translation>View Group Membership</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -4,239 +4,469 @@
|
|||
<context>
|
||||
<name>AddGroupPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="19"/>
|
||||
<source>create-group-title</source>
|
||||
<translation type="vanished">Créer un groupe</translation>
|
||||
<translation>Créer un groupe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="42"/>
|
||||
<source>server-label</source>
|
||||
<extracomment>Server label</extracomment>
|
||||
<translation type="vanished">Serveur</translation>
|
||||
<translation>Serveur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="53"/>
|
||||
<source>group-name-label</source>
|
||||
<extracomment>Group name label</extracomment>
|
||||
<translation type="vanished">Groupe</translation>
|
||||
<translation>Groupe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="60"/>
|
||||
<source>default-group-name</source>
|
||||
<extracomment>default suggested group name</extracomment>
|
||||
<translation type="vanished">Un super groupe</translation>
|
||||
<translation>Un super groupe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="65"/>
|
||||
<source>create-group-btn</source>
|
||||
<extracomment>create group button</extracomment>
|
||||
<translation type="vanished">Créer</translation>
|
||||
<translation>Créer</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BulletinOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="203"/>
|
||||
<source>new-bulletin-label</source>
|
||||
<translation type="vanished">Nouveau bulletin</translation>
|
||||
<translation>Nouveau bulletin</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="215"/>
|
||||
<source>post-new-bulletin-label</source>
|
||||
<extracomment>Post a new Bulletin Post</extracomment>
|
||||
<translation type="vanished">Envoyer un nouveau bulletin</translation>
|
||||
<translation>Envoyer un nouveau bulletin</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="221"/>
|
||||
<source>title-placeholder</source>
|
||||
<extracomment>title place holder text</extracomment>
|
||||
<translation type="vanished">titre...</translation>
|
||||
<translation>titre...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GroupSettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="42"/>
|
||||
<source>server-label</source>
|
||||
<translation type="vanished">Serveur</translation>
|
||||
<translation>Serveur</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="53"/>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="74"/>
|
||||
<source>copy-btn</source>
|
||||
<translation type="vanished">Copier</translation>
|
||||
<translation>Copier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="63"/>
|
||||
<source>invitation-label</source>
|
||||
<translation type="vanished">Invitation</translation>
|
||||
<translation>Invitation</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="84"/>
|
||||
<source>group-name-label</source>
|
||||
<translation type="vanished">Nom du groupe</translation>
|
||||
<translation>Nom du groupe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="93"/>
|
||||
<source>save-btn</source>
|
||||
<translation type="vanished">Sauvegarder</translation>
|
||||
<translation>Sauvegarder</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="103"/>
|
||||
<source>invite-to-group-label</source>
|
||||
<extracomment>Invite someone to the group</extracomment>
|
||||
<translation type="vanished">Inviter quelqu'un</translation>
|
||||
<translation>Inviter quelqu'un</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="114"/>
|
||||
<source>invite-btn</source>
|
||||
<translation type="vanished">Invitation</translation>
|
||||
<translation>Invitation</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="123"/>
|
||||
<source>delete-btn</source>
|
||||
<translation type="vanished">Effacer</translation>
|
||||
<translation>Effacer</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>InplaceEditText</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/InplaceEditText.qml" line="85"/>
|
||||
<source>Update</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ListOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="184"/>
|
||||
<source>add-list-item</source>
|
||||
<extracomment>Add a New List Item</extracomment>
|
||||
<translation type="vanished">Ajouter un nouvel élément</translation>
|
||||
<translation>Ajouter un nouvel élément</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="196"/>
|
||||
<source>add-new-item</source>
|
||||
<extracomment>Add a new item to the list</extracomment>
|
||||
<translation type="vanished">Ajouter un nouvel élément à la liste</translation>
|
||||
<translation>Ajouter un nouvel élément à la liste</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="202"/>
|
||||
<source>todo-placeholder</source>
|
||||
<extracomment>Todo... placeholder text</extracomment>
|
||||
<translation type="vanished">A faire...</translation>
|
||||
<translation>A faire...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MembershipOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/MembershipOverlay.qml" line="21"/>
|
||||
<source>membership-description</source>
|
||||
<extracomment>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.</extracomment>
|
||||
<translation type="vanished">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.</translation>
|
||||
<translation>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.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Message</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="56"/>
|
||||
<source>dm-tooltip</source>
|
||||
<extracomment>Click to DM</extracomment>
|
||||
<translation type="vanished">Envoyer un message privé</translation>
|
||||
<translation>Envoyer un message privé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>could-not-send-msg-error</source>
|
||||
<extracomment>Could not send this message</extracomment>
|
||||
<translation type="vanished">Impossible d'envoyer ce message</translation>
|
||||
<translation>Impossible d'envoyer ce message</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>acknowledged-label</source>
|
||||
<translation type="vanished">Confirmé</translation>
|
||||
<translation>Confirmé</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>pending-label</source>
|
||||
<translation type="vanished">En attente</translation>
|
||||
<translation>En attente</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MyProfile</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="185"/>
|
||||
<source>copy-btn</source>
|
||||
<extracomment>Button for copying profile onion address to clipboard</extracomment>
|
||||
<translation type="vanished">Copier</translation>
|
||||
<translation>Copier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="189"/>
|
||||
<source>copied-clipboard-notification</source>
|
||||
<extracomment>Copied to clipboard</extracomment>
|
||||
<translation type="vanished">Copié dans le presse-papier</translation>
|
||||
<translation>Copié dans le presse-papier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="219"/>
|
||||
<source>new-group-btn</source>
|
||||
<extracomment>create new group button</extracomment>
|
||||
<translation type="vanished">Créer un nouveau groupe</translation>
|
||||
<translation>Créer un nouveau groupe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="229"/>
|
||||
<source>paste-address-to-add-contact</source>
|
||||
<extracomment>ex: "... paste an address here to add a contact ..."</extracomment>
|
||||
<translation type="vanished">... coller une adresse ici pour ajouter un contact...</translation>
|
||||
<translation>... coller une adresse ici pour ajouter un contact...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OverlayPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="44"/>
|
||||
<source>accept-group-invite-label</source>
|
||||
<extracomment>Do you want to accept the invitation to $GROUP</extracomment>
|
||||
<translation type="vanished">Voulez-vous accepter l'invitation au groupe</translation>
|
||||
<translation>Voulez-vous accepter l'invitation au groupe</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="49"/>
|
||||
<source>accept-group-btn</source>
|
||||
<extracomment>Accept group invite button</extracomment>
|
||||
<translation type="vanished">Accepter</translation>
|
||||
<translation>Accepter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="59"/>
|
||||
<source>reject-group-btn</source>
|
||||
<extracomment>Reject Group invite button</extracomment>
|
||||
<translation type="vanished">Refuser</translation>
|
||||
<translation>Refuser</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="73"/>
|
||||
<source>chat-btn</source>
|
||||
<translation type="vanished">Discuter</translation>
|
||||
<translation>Discuter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="80"/>
|
||||
<source>lists-btn</source>
|
||||
<translation type="vanished">Listes</translation>
|
||||
<translation>Listes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="87"/>
|
||||
<source>bulletins-btn</source>
|
||||
<translation type="vanished">Bulletins</translation>
|
||||
<translation>Bulletins</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="94"/>
|
||||
<source>puzzle-game-btn</source>
|
||||
<translation type="vanished">Puzzle</translation>
|
||||
<translation>Puzzle</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PeerSettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="42"/>
|
||||
<source>address-label</source>
|
||||
<translation type="vanished">Adresse</translation>
|
||||
<translation>Adresse</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="53"/>
|
||||
<source>copy-btn</source>
|
||||
<translation type="vanished">Copier</translation>
|
||||
<translation>Copier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="57"/>
|
||||
<source>copied-to-clipboard-notification</source>
|
||||
<extracomment>notification: copied to clipboard</extracomment>
|
||||
<translation type="vanished">Copié dans le presse-papier</translation>
|
||||
<translation>Copié dans le presse-papier</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="64"/>
|
||||
<source>display-name-label</source>
|
||||
<translation type="vanished">Pseudo</translation>
|
||||
<translation>Pseudo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="73"/>
|
||||
<source>save-btn</source>
|
||||
<translation type="vanished">Sauvegarder</translation>
|
||||
<translation>Sauvegarder</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="85"/>
|
||||
<source>block-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="85"/>
|
||||
<source>unblock-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="99"/>
|
||||
<source>delete-btn</source>
|
||||
<translation type="vanished">Effacer</translation>
|
||||
<translation>Effacer</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileAddEditPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="24"/>
|
||||
<source>add-profile-title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="24"/>
|
||||
<source>edit-profile-title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="92"/>
|
||||
<source>profile-name</source>
|
||||
<extracomment>Display name</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="32"/>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="100"/>
|
||||
<source>default-profile-name</source>
|
||||
<extracomment>default suggested profile name</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="81"/>
|
||||
<source>profile-onion-label</source>
|
||||
<extracomment>Onion</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="112"/>
|
||||
<source>radio-use-password</source>
|
||||
<extracomment>Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="122"/>
|
||||
<source>radio-no-password</source>
|
||||
<extracomment>Unencrypted (No password)</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="133"/>
|
||||
<source>no-password-warning</source>
|
||||
<extracomment>Not using a password on this account means that all data stored locally will not be encrypted</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="140"/>
|
||||
<source>current-password-label</source>
|
||||
<extracomment>Current Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="154"/>
|
||||
<source>password1-label</source>
|
||||
<extracomment>Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="174"/>
|
||||
<source>password2-label</source>
|
||||
<extracomment>Reenter password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="188"/>
|
||||
<source>create-profile-btn</source>
|
||||
<extracomment>Create Profile || Save Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="188"/>
|
||||
<source>save-profile-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="217"/>
|
||||
<source>password-error-match</source>
|
||||
<extracomment>Passwords do not match</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="225"/>
|
||||
<source>password-change-error</source>
|
||||
<extracomment>Error changing password: Supplied password rejected</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="234"/>
|
||||
<source>delete-profile-btn</source>
|
||||
<extracomment>Delete Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="247"/>
|
||||
<source>delete-confirm-label</source>
|
||||
<extracomment>Type DELETE to confirm</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="263"/>
|
||||
<source>delete-profile-confirm-btn</source>
|
||||
<extracomment>Really Delete Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="269"/>
|
||||
<source>delete-confirm-text</source>
|
||||
<extracomment>DELETE</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileList</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/ProfileList.qml" line="88"/>
|
||||
<location filename="../qml/widgets/ProfileList.qml" line="101"/>
|
||||
<source>add-new-profile-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileManagerPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="26"/>
|
||||
<source>enter-profile-password</source>
|
||||
<extracomment>Please enter password:</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="42"/>
|
||||
<source>error-0-profiles-loaded-for-password</source>
|
||||
<extracomment>0 profiles loaded with that password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="52"/>
|
||||
<source>unlock</source>
|
||||
<extracomment>Unlock</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="21"/>
|
||||
<source>cwtch-settings-title</source>
|
||||
<extracomment>Cwtch Settings title</extracomment>
|
||||
<translation type="vanished">Préférences Cwtch</translation>
|
||||
<translation>Préférences Cwtch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="47"/>
|
||||
<source>version %1 builddate %2</source>
|
||||
<extracomment>Version: %1 Built on: %2</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="54"/>
|
||||
<source>zoom-label</source>
|
||||
<extracomment>Interface zoom (mostly affects text and button sizes)</extracomment>
|
||||
<translation type="vanished">Interface zoom (essentiellement la taille du texte et des composants de l'interface)</translation>
|
||||
<translation>Interface zoom (essentiellement la taille du texte et des composants de l'interface)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="83"/>
|
||||
<source>block-unknown-label</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="90"/>
|
||||
<source>large-text-label</source>
|
||||
<translation type="obsolete">Large</translation>
|
||||
<translation type="unfinished">Large</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="98"/>
|
||||
<source>default-scaling-text</source>
|
||||
<extracomment>"Default size text (scale factor: "</extracomment>
|
||||
<translation type="vanished">Taille par défaut du texte (échelle:</translation>
|
||||
<translation>Taille par défaut du texte (échelle:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="102"/>
|
||||
<source>small-text-label</source>
|
||||
<translation type="vanished">Petit</translation>
|
||||
<translation>Petit</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>StackToolbar</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/StackToolbar.qml" line="58"/>
|
||||
<source>view-group-membership-tooltip</source>
|
||||
<extracomment>View Group Membership</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
|
@ -4,239 +4,469 @@
|
|||
<context>
|
||||
<name>AddGroupPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="19"/>
|
||||
<source>create-group-title</source>
|
||||
<translation type="vanished">Criar Grupo</translation>
|
||||
<translation>Criar Grupo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="42"/>
|
||||
<source>server-label</source>
|
||||
<extracomment>Server label</extracomment>
|
||||
<translation type="vanished">Servidor</translation>
|
||||
<translation>Servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="53"/>
|
||||
<source>group-name-label</source>
|
||||
<extracomment>Group name label</extracomment>
|
||||
<translation type="vanished">Nome do grupo</translation>
|
||||
<translation>Nome do grupo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="60"/>
|
||||
<source>default-group-name</source>
|
||||
<extracomment>default suggested group name</extracomment>
|
||||
<translation type="vanished">Grupo incrível</translation>
|
||||
<translation>Grupo incrível</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/AddGroupPane.qml" line="65"/>
|
||||
<source>create-group-btn</source>
|
||||
<extracomment>create group button</extracomment>
|
||||
<translation type="vanished">Criar</translation>
|
||||
<translation>Criar</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BulletinOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="203"/>
|
||||
<source>new-bulletin-label</source>
|
||||
<translation type="vanished">Novo Boletim</translation>
|
||||
<translation>Novo Boletim</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="215"/>
|
||||
<source>post-new-bulletin-label</source>
|
||||
<extracomment>Post a new Bulletin Post</extracomment>
|
||||
<translation type="vanished">Postar novo boletim</translation>
|
||||
<translation>Postar novo boletim</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/BulletinOverlay.qml" line="221"/>
|
||||
<source>title-placeholder</source>
|
||||
<extracomment>title place holder text</extracomment>
|
||||
<translation type="vanished">título…</translation>
|
||||
<translation>título…</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>GroupSettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="42"/>
|
||||
<source>server-label</source>
|
||||
<translation type="vanished">Servidor</translation>
|
||||
<translation>Servidor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="53"/>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="74"/>
|
||||
<source>copy-btn</source>
|
||||
<translation type="vanished">Copiar</translation>
|
||||
<translation>Copiar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="63"/>
|
||||
<source>invitation-label</source>
|
||||
<translation type="vanished">Convite</translation>
|
||||
<translation>Convite</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="84"/>
|
||||
<source>group-name-label</source>
|
||||
<translation type="vanished">Nome do Grupo</translation>
|
||||
<translation>Nome do Grupo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="93"/>
|
||||
<source>save-btn</source>
|
||||
<translation type="vanished">Salvar</translation>
|
||||
<translation>Salvar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="103"/>
|
||||
<source>invite-to-group-label</source>
|
||||
<extracomment>Invite someone to the group</extracomment>
|
||||
<translation type="vanished">Convidar ao grupo</translation>
|
||||
<translation>Convidar ao grupo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="114"/>
|
||||
<source>invite-btn</source>
|
||||
<translation type="vanished">Convidar</translation>
|
||||
<translation>Convidar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/GroupSettingsPane.qml" line="123"/>
|
||||
<source>delete-btn</source>
|
||||
<translation type="vanished">Deletar</translation>
|
||||
<translation>Deletar</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>InplaceEditText</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/InplaceEditText.qml" line="85"/>
|
||||
<source>Update</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ListOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="184"/>
|
||||
<source>add-list-item</source>
|
||||
<extracomment>Add a New List Item</extracomment>
|
||||
<translation type="vanished">Adicionar Item à Lista</translation>
|
||||
<translation>Adicionar Item à Lista</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="196"/>
|
||||
<source>add-new-item</source>
|
||||
<extracomment>Add a new item to the list</extracomment>
|
||||
<translation type="vanished">Adicionar novo item à lista</translation>
|
||||
<translation>Adicionar novo item à lista</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/overlays/ListOverlay.qml" line="202"/>
|
||||
<source>todo-placeholder</source>
|
||||
<extracomment>Todo... placeholder text</extracomment>
|
||||
<translation type="vanished">Afazer…</translation>
|
||||
<translation>Afazer…</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MembershipOverlay</name>
|
||||
<message>
|
||||
<location filename="../qml/overlays/MembershipOverlay.qml" line="21"/>
|
||||
<source>membership-description</source>
|
||||
<extracomment>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.</extracomment>
|
||||
<translation type="vanished">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.</translation>
|
||||
<translation>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.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>Message</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="56"/>
|
||||
<source>dm-tooltip</source>
|
||||
<extracomment>Click to DM</extracomment>
|
||||
<translation type="vanished">Clique para DM</translation>
|
||||
<translation>Clique para DM</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>could-not-send-msg-error</source>
|
||||
<extracomment>Could not send this message</extracomment>
|
||||
<translation type="vanished">Não deu para enviar esta mensagem</translation>
|
||||
<translation>Não deu para enviar esta mensagem</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>acknowledged-label</source>
|
||||
<translation type="vanished">Confirmada</translation>
|
||||
<translation>Confirmada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/Message.qml" line="162"/>
|
||||
<source>pending-label</source>
|
||||
<translation type="vanished">Pendente</translation>
|
||||
<translation>Pendente</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MyProfile</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="185"/>
|
||||
<source>copy-btn</source>
|
||||
<extracomment>Button for copying profile onion address to clipboard</extracomment>
|
||||
<translation type="vanished">Copiar</translation>
|
||||
<translation>Copiar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="189"/>
|
||||
<source>copied-clipboard-notification</source>
|
||||
<extracomment>Copied to clipboard</extracomment>
|
||||
<translation type="vanished">Copiado</translation>
|
||||
<translation>Copiado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="219"/>
|
||||
<source>new-group-btn</source>
|
||||
<extracomment>create new group button</extracomment>
|
||||
<translation type="vanished">Criar novo grupo</translation>
|
||||
<translation>Criar novo grupo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/widgets/MyProfile.qml" line="229"/>
|
||||
<source>paste-address-to-add-contact</source>
|
||||
<extracomment>ex: "... paste an address here to add a contact ..."</extracomment>
|
||||
<translation type="vanished">… cole um endereço aqui para adicionar um contato…</translation>
|
||||
<translation>… cole um endereço aqui para adicionar um contato…</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>OverlayPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="44"/>
|
||||
<source>accept-group-invite-label</source>
|
||||
<extracomment>Do you want to accept the invitation to $GROUP</extracomment>
|
||||
<translation type="vanished">Você quer aceitar o convite para</translation>
|
||||
<translation>Você quer aceitar o convite para</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="49"/>
|
||||
<source>accept-group-btn</source>
|
||||
<extracomment>Accept group invite button</extracomment>
|
||||
<translation type="vanished">Aceitar</translation>
|
||||
<translation>Aceitar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="59"/>
|
||||
<source>reject-group-btn</source>
|
||||
<extracomment>Reject Group invite button</extracomment>
|
||||
<translation type="vanished">Recusar</translation>
|
||||
<translation>Recusar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="73"/>
|
||||
<source>chat-btn</source>
|
||||
<translation type="vanished">Chat</translation>
|
||||
<translation>Chat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="80"/>
|
||||
<source>lists-btn</source>
|
||||
<translation type="vanished">Listas</translation>
|
||||
<translation>Listas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="87"/>
|
||||
<source>bulletins-btn</source>
|
||||
<translation type="vanished">Boletins</translation>
|
||||
<translation>Boletins</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/OverlayPane.qml" line="94"/>
|
||||
<source>puzzle-game-btn</source>
|
||||
<translation type="vanished">Jogo de Adivinhação</translation>
|
||||
<translation>Jogo de Adivinhação</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PeerSettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="42"/>
|
||||
<source>address-label</source>
|
||||
<translation type="vanished">Endereço</translation>
|
||||
<translation>Endereço</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="53"/>
|
||||
<source>copy-btn</source>
|
||||
<translation type="vanished">Copiar</translation>
|
||||
<translation>Copiar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="57"/>
|
||||
<source>copied-to-clipboard-notification</source>
|
||||
<extracomment>notification: copied to clipboard</extracomment>
|
||||
<translation type="vanished">Copiado</translation>
|
||||
<translation>Copiado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="64"/>
|
||||
<source>display-name-label</source>
|
||||
<translation type="vanished">Nome de Exibição</translation>
|
||||
<translation>Nome de Exibição</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="73"/>
|
||||
<source>save-btn</source>
|
||||
<translation type="vanished">Salvar</translation>
|
||||
<translation>Salvar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="85"/>
|
||||
<source>block-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="85"/>
|
||||
<source>unblock-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/PeerSettingsPane.qml" line="99"/>
|
||||
<source>delete-btn</source>
|
||||
<translation type="vanished">Deletar</translation>
|
||||
<translation>Deletar</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileAddEditPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="24"/>
|
||||
<source>add-profile-title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="24"/>
|
||||
<source>edit-profile-title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="92"/>
|
||||
<source>profile-name</source>
|
||||
<extracomment>Display name</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="32"/>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="100"/>
|
||||
<source>default-profile-name</source>
|
||||
<extracomment>default suggested profile name</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="81"/>
|
||||
<source>profile-onion-label</source>
|
||||
<extracomment>Onion</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="112"/>
|
||||
<source>radio-use-password</source>
|
||||
<extracomment>Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="122"/>
|
||||
<source>radio-no-password</source>
|
||||
<extracomment>Unencrypted (No password)</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="133"/>
|
||||
<source>no-password-warning</source>
|
||||
<extracomment>Not using a password on this account means that all data stored locally will not be encrypted</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="140"/>
|
||||
<source>current-password-label</source>
|
||||
<extracomment>Current Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="154"/>
|
||||
<source>password1-label</source>
|
||||
<extracomment>Password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="174"/>
|
||||
<source>password2-label</source>
|
||||
<extracomment>Reenter password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="188"/>
|
||||
<source>create-profile-btn</source>
|
||||
<extracomment>Create Profile || Save Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="188"/>
|
||||
<source>save-profile-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="217"/>
|
||||
<source>password-error-match</source>
|
||||
<extracomment>Passwords do not match</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="225"/>
|
||||
<source>password-change-error</source>
|
||||
<extracomment>Error changing password: Supplied password rejected</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="234"/>
|
||||
<source>delete-profile-btn</source>
|
||||
<extracomment>Delete Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="247"/>
|
||||
<source>delete-confirm-label</source>
|
||||
<extracomment>Type DELETE to confirm</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="263"/>
|
||||
<source>delete-profile-confirm-btn</source>
|
||||
<extracomment>Really Delete Profile</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileAddEditPane.qml" line="269"/>
|
||||
<source>delete-confirm-text</source>
|
||||
<extracomment>DELETE</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileList</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/ProfileList.qml" line="88"/>
|
||||
<location filename="../qml/widgets/ProfileList.qml" line="101"/>
|
||||
<source>add-new-profile-btn</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProfileManagerPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="26"/>
|
||||
<source>enter-profile-password</source>
|
||||
<extracomment>Please enter password:</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="42"/>
|
||||
<source>error-0-profiles-loaded-for-password</source>
|
||||
<extracomment>0 profiles loaded with that password</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/ProfileManagerPane.qml" line="52"/>
|
||||
<source>unlock</source>
|
||||
<extracomment>Unlock</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsPane</name>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="21"/>
|
||||
<source>cwtch-settings-title</source>
|
||||
<extracomment>Cwtch Settings title</extracomment>
|
||||
<translation type="vanished">Configurações do Cwtch</translation>
|
||||
<translation>Configurações do Cwtch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="47"/>
|
||||
<source>version %1 builddate %2</source>
|
||||
<extracomment>Version: %1 Built on: %2</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="54"/>
|
||||
<source>zoom-label</source>
|
||||
<extracomment>Interface zoom (mostly affects text and button sizes)</extracomment>
|
||||
<translation type="vanished">Zoom da interface (afeta principalmente tamanho de texto e botões)</translation>
|
||||
<translation>Zoom da interface (afeta principalmente tamanho de texto e botões)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="83"/>
|
||||
<source>block-unknown-label</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="90"/>
|
||||
<source>large-text-label</source>
|
||||
<translation type="vanished">Grande</translation>
|
||||
<translation>Grande</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="98"/>
|
||||
<source>default-scaling-text</source>
|
||||
<extracomment>"Default size text (scale factor: "</extracomment>
|
||||
<translation type="vanished">Texto tamanho padrão (fator de escala: </translation>
|
||||
<translation>Texto tamanho padrão (fator de escala: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../qml/panes/SettingsPane.qml" line="102"/>
|
||||
<source>small-text-label</source>
|
||||
<translation type="vanished">Pequeno</translation>
|
||||
<translation>Pequeno</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>StackToolbar</name>
|
||||
<message>
|
||||
<location filename="../qml/widgets/StackToolbar.qml" line="58"/>
|
||||
<source>view-group-membership-tooltip</source>
|
||||
<extracomment>View Group Membership</extracomment>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
|
28
main.go
28
main.go
|
@ -3,11 +3,10 @@ package main
|
|||
import (
|
||||
libapp "cwtch.im/cwtch/app"
|
||||
"cwtch.im/cwtch/event/bridge"
|
||||
"cwtch.im/ui/go/characters"
|
||||
"cwtch.im/ui/go/gobjects"
|
||||
"cwtch.im/ui/go/gothings"
|
||||
"cwtch.im/ui/go/gothings/android"
|
||||
"cwtch.im/ui/go/handlers"
|
||||
"cwtch.im/ui/go/the"
|
||||
"cwtch.im/ui/go/ui"
|
||||
"cwtch.im/ui/go/ui/android"
|
||||
"flag"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/connectivity"
|
||||
"git.openprivacy.ca/openprivacy/libricochet-go/log"
|
||||
|
@ -34,7 +33,7 @@ var (
|
|||
|
||||
func init() {
|
||||
// make go-defined types available in qml
|
||||
gothings.GrandCentralDispatcher_QmlRegisterType2("CustomQmlTypes", 1, 0, "GrandCentralDispatcher")
|
||||
ui.GrandCentralDispatcher_QmlRegisterType2("CustomQmlTypes", 1, 0, "GrandCentralDispatcher")
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -58,7 +57,9 @@ func main() {
|
|||
}
|
||||
|
||||
// TESTING
|
||||
//log.SetLevel(log.LevelDebug)
|
||||
if buildVer == "" {
|
||||
log.SetLevel(log.LevelDebug)
|
||||
}
|
||||
//log.ExcludeFromPattern("connection/connection")
|
||||
//log.ExcludeFromPattern("outbound/3dhauthchannel")
|
||||
//log.AddNothingExceptFilter("event/eventmanager")
|
||||
|
@ -75,6 +76,12 @@ func main() {
|
|||
}
|
||||
the.CwtchDir = path.Join(usr.HomeDir, ".cwtch")
|
||||
}
|
||||
|
||||
if buildVer == "" && os.Getenv("CWTCH_FOLDER") == "" {
|
||||
log.Infoln("Development build: using dev directory for dev profiles")
|
||||
the.CwtchDir = path.Join(the.CwtchDir, "dev")
|
||||
}
|
||||
|
||||
the.ACN = nil
|
||||
the.Peer = nil
|
||||
the.IPCBridge = nil
|
||||
|
@ -111,6 +118,7 @@ func mainService() {
|
|||
log.Infoln("Making QGuiApplication...")
|
||||
app = gui.NewQGuiApplication(len(os.Args), os.Args)
|
||||
}
|
||||
|
||||
log.Infoln("Cwtch Service starting app.Exec")
|
||||
app.Exec()
|
||||
}
|
||||
|
@ -121,7 +129,7 @@ func mainUi(flagLocal bool, flagClientUI bool) {
|
|||
//app := gui.NewQGuiApplication(len(os.Args), os.Args)
|
||||
app := widgets.NewQApplication(len(os.Args), os.Args)
|
||||
// our globals
|
||||
gcd := gothings.NewGrandCentralDispatcher(nil)
|
||||
gcd := ui.NewGrandCentralDispatcher(nil)
|
||||
gcd.SetOs(runtime.GOOS)
|
||||
ex, err := os.Executable()
|
||||
if err != nil { log.Infof("error getting path: %v", err) }
|
||||
|
@ -139,8 +147,6 @@ func mainUi(flagLocal bool, flagClientUI bool) {
|
|||
gcd.SetVersion("development")
|
||||
gcd.SetBuildDate("now")
|
||||
}
|
||||
gcd.UIState = gothings.NewUIState(gcd)
|
||||
gcd.OutgoingMessages = make(chan gobjects.Letter, 1000)
|
||||
|
||||
//TODO: put theme stuff somewhere better
|
||||
gcd.SetThemeScale(1.0)
|
||||
|
@ -232,7 +238,7 @@ func loadACN() {
|
|||
}
|
||||
}
|
||||
|
||||
func loadNetworkingAndFiles(gcd *gothings.GrandCentralDispatcher, service bool, clientUI bool) {
|
||||
func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, clientUI bool) {
|
||||
if service || clientUI || runtime.GOOS == "android" {
|
||||
clientIn := path.Join(the.CwtchDir, "clientIn")
|
||||
serviceIn := path.Join(the.CwtchDir, "serviceIn")
|
||||
|
@ -255,7 +261,7 @@ func loadNetworkingAndFiles(gcd *gothings.GrandCentralDispatcher, service bool,
|
|||
if !service {
|
||||
the.AppBus = the.CwtchApp.GetPrimaryBus()
|
||||
subscribed := make(chan bool)
|
||||
go characters.AppEventListener(gcd, subscribed)
|
||||
go handlers.App(gcd, subscribed, clientUI)
|
||||
<-subscribed
|
||||
}
|
||||
|
||||
|
|
45
qml/main.qml
45
qml/main.qml
|
@ -127,6 +127,13 @@ ApplicationWindow {
|
|||
currentIndex: 1
|
||||
anchors.fill: parent
|
||||
|
||||
currentIndex: 0
|
||||
readonly property int splashPane: 0
|
||||
readonly property int managementPane: 1
|
||||
readonly property int addEditProfilePane: 2
|
||||
readonly property int profilePane: 3
|
||||
property alias pane: parentStack.currentIndex
|
||||
|
||||
Rectangle { // Splash pane
|
||||
color: "#FFFFFF"
|
||||
//Layout.fillHeight: true
|
||||
|
@ -143,6 +150,28 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
Rectangle { // Profile login/management pane
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
color: "#D2C0DD"
|
||||
|
||||
ProfileManagerPane {
|
||||
id: profilesPane
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Profile login/management pane
|
||||
anchors.fill: parent
|
||||
color: "#EEEEFF"
|
||||
|
||||
|
||||
ProfileAddEditPane{
|
||||
id: profileAddEditPane
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR
|
||||
/* anchors.left: ratio >= 0.92 ? parent.left : toolbar.right
|
||||
|
@ -260,7 +289,23 @@ ApplicationWindow {
|
|||
onSetToolbarTitle: function(str) {
|
||||
theStack.title = str
|
||||
}
|
||||
|
||||
onLoaded: function() {
|
||||
parentStack.pane = parentStack.managementPane
|
||||
splashPane.running = false
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: Mutant.standard.imagePath = gcd.assetPath;
|
||||
|
||||
Connections {
|
||||
target: Qt.application
|
||||
onStateChanged: function() {
|
||||
// https://doc.qt.io/qt-5/qt.html#ApplicationState-enum
|
||||
if (Qt.application.state == 4) {
|
||||
// Active
|
||||
gcd.onActivate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,7 @@ import QtQuick.Controls 1.4
|
|||
import QtQuick.Layouts 1.3
|
||||
|
||||
|
||||
import "../widgets"
|
||||
import "../widgets/controls" as Awesome
|
||||
import "../widgets" as Widgets
|
||||
import "../fonts/Twemoji.js" as T
|
||||
import "../utils.js" as Utils
|
||||
import "../styles"
|
||||
|
@ -87,13 +86,13 @@ ColumnLayout {
|
|||
})
|
||||
}
|
||||
|
||||
if (sv.contentY + sv.height >= sv.contentHeight - colMessages.height && sv.contentHeight > sv.height) {
|
||||
/*if (sv.contentY + sv.height >= sv.contentHeight - colMessages.height && sv.contentHeight > sv.height) {
|
||||
sv.contentY = sv.contentHeight - sv.height
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) {
|
||||
if (gcd.currentOpenConversation == _handle) {
|
||||
onUpdateContactStatus: function(_handle, _status, _loading) {
|
||||
if (gcd.selectedConversation == _handle) {
|
||||
if (_loading == true) {
|
||||
newposttitle.enabled = false
|
||||
newpostbody.enabled = false
|
||||
|
@ -176,7 +175,7 @@ ColumnLayout {
|
|||
width: parent.width - 50
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
id: replybtn
|
||||
visible: selected
|
||||
text: "reply"
|
||||
|
@ -231,7 +230,7 @@ ColumnLayout {
|
|||
}
|
||||
|
||||
|
||||
SimpleButton { // SEND MESSAGE BUTTON
|
||||
Widgets.SimpleButton { // SEND MESSAGE BUTTON
|
||||
id: btnSend
|
||||
icon: "regular/paper-plane"
|
||||
text: "post"
|
||||
|
|
|
@ -112,10 +112,10 @@ Item {
|
|||
messagesListView.positionViewAtEnd()
|
||||
}
|
||||
|
||||
onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) {
|
||||
if (gcd.currentOpenConversation == _handle) {
|
||||
onUpdateContactStatus: function(_handle, _status, _loading) {
|
||||
if (gcd.selectedConversation == _handle) {
|
||||
// Group is Synced OR p2p is Authenticated
|
||||
if ( (_handle.length == 32 && _status == 4) || _status == 3) {
|
||||
if ( (_handle.length == 32 && _status == 4) || (_handle.length == 56 && _status == 3) ) {
|
||||
txtMessage.enabled = true
|
||||
btnSend.enabled = true
|
||||
} else {
|
||||
|
|
|
@ -5,7 +5,7 @@ import QtQuick.Controls.Material 2.0
|
|||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import "../widgets"
|
||||
import "../widgets" as Widgets
|
||||
import "../widgets/controls" as Awesome
|
||||
import "../fonts/Twemoji.js" as T
|
||||
import "../utils.js" as Utils
|
||||
|
@ -87,17 +87,17 @@ ColumnLayout {
|
|||
})
|
||||
}
|
||||
|
||||
if(msg.c != undefined) {
|
||||
/*if(msg.c != undefined) {
|
||||
jsonModel4.get(msg.c).complete = true
|
||||
}
|
||||
|
||||
if (sv.contentY + sv.height >= sv.contentHeight - colMessages.height && sv.contentHeight > sv.height) {
|
||||
sv.contentY = sv.contentHeight - sv.height
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) {
|
||||
if (gcd.currentOpenConversation == _handle) {
|
||||
onUpdateContactStatus: function(_handle, _status, _loading) {
|
||||
if (gcd.selectedConversation == _handle) {
|
||||
if (_loading == true) {
|
||||
newposttitle.enabled = false
|
||||
btnSend.enabled = false
|
||||
|
@ -204,7 +204,7 @@ ColumnLayout {
|
|||
style: CwtchTextFieldStyle{}
|
||||
}
|
||||
|
||||
SimpleButton { // SEND MESSAGE BUTTON
|
||||
Widgets.SimpleButton { // SEND MESSAGE BUTTON
|
||||
id: btnSend
|
||||
icon: "regular/paper-plane"
|
||||
text: "add"
|
||||
|
|
|
@ -105,7 +105,6 @@ ColumnLayout {
|
|||
handle: _handle
|
||||
displayName: _displayName
|
||||
image: _image
|
||||
trusted: true
|
||||
blocked: false
|
||||
background: false
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import QtQuick.Layouts 1.3
|
|||
import QtQuick.Window 2.11
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "../widgets"
|
||||
import "../widgets" as Widgets
|
||||
import "../styles"
|
||||
|
||||
ColumnLayout { // settingsPane
|
||||
|
@ -14,7 +14,7 @@ ColumnLayout { // settingsPane
|
|||
anchors.fill: parent
|
||||
|
||||
|
||||
StackToolbar {
|
||||
Widgets.StackToolbar {
|
||||
id: stb
|
||||
text: qsTr("create-group-title")
|
||||
aux.visible: false
|
||||
|
@ -37,7 +37,7 @@ ColumnLayout { // settingsPane
|
|||
spacing: 5
|
||||
width: root.width
|
||||
|
||||
ScalingLabel {
|
||||
Widgets.ScalingLabel {
|
||||
//: Server label
|
||||
text: qsTr("server-label") + ":"
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ ColumnLayout { // settingsPane
|
|||
text: "2c3kmoobnyghj2zw6pwv7d57yzld753auo3ugauezzpvfak3ahc4bdyd"
|
||||
}
|
||||
|
||||
ScalingLabel{
|
||||
Widgets.ScalingLabel{
|
||||
//: Group name label
|
||||
text: qsTr("group-name-label") + ":"
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ ColumnLayout { // settingsPane
|
|||
text: qsTr("default-group-name")
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
//: create group button
|
||||
text: qsTr("create-group-btn")
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import QtQuick.Layouts 1.3
|
|||
import QtQuick.Window 2.11
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "../widgets"
|
||||
import "../widgets" as Widgets
|
||||
import "../styles"
|
||||
import "../utils.js" as Utils
|
||||
|
||||
|
@ -16,7 +16,7 @@ ColumnLayout { // groupSettingsPane
|
|||
property string groupID
|
||||
property variant addrbook
|
||||
|
||||
StackToolbar {
|
||||
Widgets.StackToolbar {
|
||||
id: toolbar
|
||||
aux.visible: false
|
||||
back.onClicked: theStack.pane = theStack.messagePane
|
||||
|
@ -38,7 +38,7 @@ ColumnLayout { // groupSettingsPane
|
|||
leftPadding: 10
|
||||
spacing: 5
|
||||
|
||||
ScalingLabel {
|
||||
Widgets.ScalingLabel {
|
||||
text: qsTr("server-label") + ":"
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ ColumnLayout { // groupSettingsPane
|
|||
readOnly: true
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
icon: "regular/clipboard"
|
||||
text: qsTr("copy-btn")
|
||||
|
||||
|
@ -59,7 +59,7 @@ ColumnLayout { // groupSettingsPane
|
|||
}
|
||||
}
|
||||
|
||||
ScalingLabel {
|
||||
Widgets.ScalingLabel {
|
||||
text: qsTr("invitation-label") + ":"
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ ColumnLayout { // groupSettingsPane
|
|||
readOnly: true
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
icon: "regular/clipboard"
|
||||
text: qsTr("copy-btn")
|
||||
|
||||
|
@ -80,7 +80,7 @@ ColumnLayout { // groupSettingsPane
|
|||
}
|
||||
}
|
||||
|
||||
ScalingLabel{
|
||||
Widgets.ScalingLabel{
|
||||
text: qsTr("group-name-label") + ":"
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ ColumnLayout { // groupSettingsPane
|
|||
style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 }
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
text: qsTr("save-btn")
|
||||
|
||||
onClicked: {
|
||||
|
@ -100,7 +100,7 @@ ColumnLayout { // groupSettingsPane
|
|||
}
|
||||
|
||||
//: Invite someone to the group
|
||||
ScalingLabel { text: qsTr("invite-to-group-label") }
|
||||
Widgets.ScalingLabel { text: qsTr("invite-to-group-label") }
|
||||
|
||||
ComboBox {
|
||||
id: cbInvite
|
||||
|
@ -110,7 +110,7 @@ ColumnLayout { // groupSettingsPane
|
|||
style: CwtchComboBoxStyle{}
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
text: qsTr("invite-btn")
|
||||
|
||||
onClicked: {
|
||||
|
@ -118,7 +118,7 @@ ColumnLayout { // groupSettingsPane
|
|||
}
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
icon: "regular/trash-alt"
|
||||
text: qsTr("delete-btn")
|
||||
|
||||
|
|
|
@ -19,14 +19,14 @@ ColumnLayout {
|
|||
StackToolbar {
|
||||
id: toolbar
|
||||
|
||||
membership.visible: gcd.currentOpenConversation.length == 32
|
||||
membership.visible: gcd.selectedConversation.length == 32
|
||||
|
||||
membership.onClicked: overlayStack.overlay = overlayStack.membershipOverlay
|
||||
|
||||
aux.onClicked: {
|
||||
if (gcd.currentOpenConversation.length == 32) {
|
||||
if (gcd.selectedConversation.length == 32) {
|
||||
theStack.pane = theStack.groupProfilePane
|
||||
gcd.requestGroupSettings(gcd.currentOpenConversation)
|
||||
gcd.requestGroupSettings(gcd.selectedConversation)
|
||||
} else {
|
||||
theStack.pane = theStack.userProfilePane
|
||||
gcd.requestPeerSettings()
|
||||
|
@ -36,7 +36,7 @@ ColumnLayout {
|
|||
}
|
||||
|
||||
RowLayout {
|
||||
visible:!overlay.accepted && (gcd.currentOpenConversation.length == 32)
|
||||
visible:!overlay.accepted && (gcd.selectedConversation.length == 32)
|
||||
|
||||
|
||||
Text {
|
||||
|
@ -49,8 +49,8 @@ ColumnLayout {
|
|||
text: qsTr("accept-group-btn")
|
||||
icon: "regular/heart"
|
||||
onClicked: {
|
||||
gcd.acceptGroup(gcd.currentOpenConversation)
|
||||
gcd.requestGroupSettings(gcd.currentOpenConversation)
|
||||
gcd.acceptGroup(gcd.selectedConversation)
|
||||
gcd.requestGroupSettings(gcd.selectedConversation)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ ColumnLayout {
|
|||
text: qsTr("reject-group-btn")
|
||||
icon: "regular/trash-alt"
|
||||
onClicked: {
|
||||
gcd.leaveGroup(gcd.currentOpenConversation)
|
||||
gcd.leaveGroup(gcd.selectedConversation)
|
||||
theStack.pane = theStack.emptyPane
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import QtQuick.Layouts 1.3
|
|||
import QtQuick.Window 2.11
|
||||
import QtQuick.Controls 1.4
|
||||
|
||||
import "../widgets"
|
||||
import "../widgets" as Widgets
|
||||
import "../styles"
|
||||
|
||||
ColumnLayout { // peerSettingsPane
|
||||
|
@ -14,7 +14,7 @@ ColumnLayout { // peerSettingsPane
|
|||
anchors.fill: parent
|
||||
property bool blocked
|
||||
|
||||
StackToolbar {
|
||||
Widgets.StackToolbar {
|
||||
id: toolbar
|
||||
aux.visible: false
|
||||
|
||||
|
@ -38,7 +38,7 @@ ColumnLayout { // peerSettingsPane
|
|||
leftPadding: 10
|
||||
spacing: 5
|
||||
|
||||
ScalingLabel {
|
||||
Widgets.ScalingLabel {
|
||||
text: qsTr("address-label")
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ ColumnLayout { // peerSettingsPane
|
|||
readOnly: true
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
icon: "regular/clipboard"
|
||||
text: qsTr("copy-btn")
|
||||
|
||||
|
@ -60,7 +60,7 @@ ColumnLayout { // peerSettingsPane
|
|||
}
|
||||
}
|
||||
|
||||
ScalingLabel{
|
||||
Widgets.ScalingLabel{
|
||||
text: qsTr("display-name-label")
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ ColumnLayout { // peerSettingsPane
|
|||
style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 }
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
text: qsTr("save-btn")
|
||||
|
||||
onClicked: {
|
||||
|
@ -80,7 +80,7 @@ ColumnLayout { // peerSettingsPane
|
|||
}
|
||||
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
icon: "solid/hand-paper"
|
||||
text: root.blocked ? qsTr("unblock-btn") : qsTr("block-btn")
|
||||
|
||||
|
@ -94,7 +94,7 @@ ColumnLayout { // peerSettingsPane
|
|||
}
|
||||
}
|
||||
|
||||
SimpleButton {
|
||||
Widgets.SimpleButton {
|
||||
icon: "regular/trash-alt"
|
||||
text: qsTr("delete-btn")
|
||||
|
||||
|
|
|
@ -0,0 +1,303 @@
|
|||
import QtGraphicalEffects 1.0
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Controls.Material 2.0
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Window 2.11
|
||||
|
||||
|
||||
import "../widgets" as Widgets
|
||||
// import "../styles"
|
||||
|
||||
ColumnLayout { // Add Profile Pane
|
||||
id: profileAddEditPane
|
||||
anchors.fill: parent
|
||||
|
||||
property string mode // edit or add
|
||||
property string onion
|
||||
property string tag
|
||||
property bool deleting
|
||||
property bool changingPassword
|
||||
|
||||
Widgets.StackToolbar {
|
||||
id: stb
|
||||
text: mode == "add" ? qsTr("add-profile-title") : qsTr("edit-profile-title")
|
||||
aux.visible: false
|
||||
membership.visible: false
|
||||
stack: "management"
|
||||
}
|
||||
|
||||
function reset() {
|
||||
mode = "add"
|
||||
txtProfileName.text = qsTr("default-profile-name")
|
||||
changingPassword = false
|
||||
txtPassword1.text = ""
|
||||
txtPassword2.text = ""
|
||||
deleting = false
|
||||
deleteConfirmLabel.color = "black"
|
||||
passwordErrorLabel.visible = false
|
||||
txtCurrentPassword.text = ""
|
||||
|
||||
tag = ""
|
||||
confirmDeleteTxt.text = ""
|
||||
radioUsePassword.checked = true
|
||||
passwordChangeErrorLabel.visible = false
|
||||
}
|
||||
|
||||
function load(userOnion, name, userTag) {
|
||||
reset()
|
||||
|
||||
mode = "edit"
|
||||
tag = userTag
|
||||
onion = userOnion
|
||||
txtPassword1.text = ""
|
||||
txtPassword2.text = ""
|
||||
onionLabel.text = onion
|
||||
txtProfileName.text = name
|
||||
|
||||
if (tag == "v1-defaultPassword" || tag == "v1-default-password") {
|
||||
radioNoPassword.checked = true
|
||||
} else {
|
||||
radioUsePassword.checked = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Flickable {
|
||||
anchors.top: stb.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip:true
|
||||
contentWidth: tehcol.width
|
||||
contentHeight: tehcol.height
|
||||
|
||||
Column {
|
||||
id: tehcol
|
||||
leftPadding: 10
|
||||
spacing: 5
|
||||
width: profileAddEditPane.width
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
//: Onion
|
||||
text: qsTr("profile-onion-label") + ":"
|
||||
visible: mode == "edit"
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: onionLabel
|
||||
visible: mode == "edit"
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
//: Display name
|
||||
text: qsTr("profile-name") + ":"
|
||||
}
|
||||
|
||||
Widgets.TextField {
|
||||
id: txtProfileName
|
||||
Layout.fillWidth: true
|
||||
//style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 }
|
||||
//: default suggested profile name
|
||||
text: qsTr("default-profile-name")
|
||||
|
||||
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
//id: radioButtons
|
||||
|
||||
Widgets.RadioButton {
|
||||
id: radioUsePassword
|
||||
checked: true
|
||||
//: Password
|
||||
text: qsTr("radio-use-password")
|
||||
visible: mode == "add" || tag == "v1-defaultPassword"
|
||||
onClicked: {
|
||||
changingPassword = true
|
||||
}
|
||||
}
|
||||
|
||||
Widgets.RadioButton {
|
||||
id: radioNoPassword
|
||||
//: Unencrypted (No password)
|
||||
text: qsTr("radio-no-password")
|
||||
visible: mode == "add" || tag == "v1-defaultPassword"
|
||||
onClicked: {
|
||||
changingPassword = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: noPasswordLabel
|
||||
//: Not using a password on this account means that all data stored locally will not be encrypted
|
||||
text: qsTr("no-password-warning")
|
||||
visible: radioNoPassword.checked
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: currentPasswordLabel
|
||||
//: Current Password
|
||||
text: qsTr("current-password-label") + ":"
|
||||
visible: radioUsePassword.checked && mode == "edit" && tag != "v1-defaultPassword"
|
||||
}
|
||||
|
||||
Widgets.TextField {
|
||||
id: txtCurrentPassword
|
||||
Layout.fillWidth: true
|
||||
echoMode: TextInput.Password
|
||||
visible: radioUsePassword.checked && mode == "edit" && tag != "v1-defaultPassword"
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: passwordLabel
|
||||
//: Password
|
||||
text: qsTr("password1-label") + ":"
|
||||
visible: radioUsePassword.checked
|
||||
}
|
||||
|
||||
Widgets.TextField {
|
||||
id: txtPassword1
|
||||
Layout.fillWidth: true
|
||||
//style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 }
|
||||
echoMode: TextInput.Password
|
||||
visible: radioUsePassword.checked
|
||||
|
||||
onTextEdited: {
|
||||
changingPassword = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: passwordReLabel
|
||||
//: Reenter password
|
||||
text: qsTr("password2-label") + ":"
|
||||
visible: radioUsePassword.checked
|
||||
}
|
||||
|
||||
Widgets.TextField {
|
||||
id: txtPassword2
|
||||
Layout.fillWidth: true
|
||||
//style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 }
|
||||
echoMode: TextInput.Password
|
||||
visible: radioUsePassword.checked
|
||||
}
|
||||
|
||||
Widgets.SimpleButton { // ADD or SAVE button
|
||||
//: Create Profile || Save Profile
|
||||
text: mode == "add" ? qsTr("create-profile-btn") : qsTr("save-profile-btn")
|
||||
|
||||
onClicked: {
|
||||
if (mode == "add") {
|
||||
if (txtPassword1.text != txtPassword2.text) {
|
||||
passwordErrorLabel.visible = true
|
||||
} else {
|
||||
gcd.createProfile(txtProfileName.text, radioNoPassword.checked, txtPassword1.text)
|
||||
gcd.reloadProfileList()
|
||||
parentStack.pane = parentStack.managementPane
|
||||
}
|
||||
} else {
|
||||
gcd.updateNick(onion, txtProfileName.text)
|
||||
|
||||
if (changingPassword) {
|
||||
if (txtPassword1.text != txtPassword2.text) {
|
||||
passwordErrorLabel.visible = true
|
||||
} else {
|
||||
gcd.changePassword(onion, txtCurrentPassword.text, txtPassword1.text, radioNoPassword.checked)
|
||||
}
|
||||
} else {
|
||||
gcd.reloadProfileList()
|
||||
parentStack.pane = parentStack.managementPane
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: passwordErrorLabel
|
||||
//: Passwords do not match
|
||||
text: qsTr("password-error-match")
|
||||
visible: false
|
||||
color: "red"
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: passwordChangeErrorLabel
|
||||
//: Error changing password: Supplied password rejected
|
||||
text: qsTr("password-change-error")
|
||||
visible: false
|
||||
color: "red"
|
||||
}
|
||||
|
||||
// ***** Delete button and confirm flow *****
|
||||
|
||||
Widgets.SimpleButton {
|
||||
//: Delete Profile
|
||||
text: qsTr("delete-profile-btn")
|
||||
icon: "regular/trash-alt"
|
||||
visible: mode == "edit"
|
||||
|
||||
|
||||
onClicked: {
|
||||
deleting = true
|
||||
}
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: deleteConfirmLabel
|
||||
//: Type DELETE to confirm
|
||||
text: qsTr("delete-confirm-label")+ ":"
|
||||
visible: deleting
|
||||
}
|
||||
|
||||
Widgets.TextField {
|
||||
id: confirmDeleteTxt
|
||||
Layout.fillWidth: true
|
||||
//style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 }
|
||||
visible: deleting
|
||||
}
|
||||
|
||||
Widgets.SimpleButton {
|
||||
id: confirmDeleteBtn
|
||||
icon: "regular/trash-alt"
|
||||
|
||||
//: Really Delete Profile
|
||||
text: qsTr("delete-profile-confirm-btn")
|
||||
color: "red"
|
||||
visible: deleting
|
||||
|
||||
onClicked: {
|
||||
//: DELETE
|
||||
if (confirmDeleteTxt.text == qsTr("delete-confirm-text")) {
|
||||
deleteConfirmLabel.color = "black"
|
||||
gcd.deleteProfile(onion)
|
||||
gcd.reloadProfileList()
|
||||
parentStack.pane = parentStack.managementPane
|
||||
} else {
|
||||
deleteConfirmLabel.color = "red"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}//end of column with padding
|
||||
}//end of flickable
|
||||
|
||||
Connections { // UPDATE UNREAD MESSAGES COUNTER
|
||||
target: gcd
|
||||
|
||||
onChangePasswordResponse: function(error) {
|
||||
if (!error) {
|
||||
gcd.reloadProfileList()
|
||||
parentStack.pane = parentStack.managementPane
|
||||
} else {
|
||||
passwordChangeErrorLabel.visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import QtQuick 2.0
|
||||
import QtGraphicalEffects 1.0
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Controls.Material 2.0
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Window 2.11
|
||||
import QtQuick.Controls 1.4
|
||||
import QtQuick.Controls.Styles 1.4
|
||||
|
||||
import "../widgets" as Widgets
|
||||
import "../widgets/controls"
|
||||
import "../styles"
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: thecol
|
||||
anchors.fill: parent
|
||||
//leftPadding: 10
|
||||
//spacing: 5
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
wrapMode: TextEdit.Wrap
|
||||
//: Please enter password:
|
||||
text: qsTr("enter-profile-password")+":"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: txtPassword
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
style: CwtchTextFieldStyle{ width: thecol.width * 0.8 }
|
||||
echoMode: TextInput.Password
|
||||
onAccepted: button.clicked()
|
||||
}
|
||||
|
||||
Widgets.ScalingLabel {
|
||||
id: error
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: "red"
|
||||
//: 0 profiles loaded with that password
|
||||
text: qsTr("error-0-profiles-loaded-for-password")
|
||||
visible: false
|
||||
}
|
||||
|
||||
Widgets.SimpleButton {
|
||||
id: "button"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
icon: "solid/unlock-alt"
|
||||
//: Unlock
|
||||
text: qsTr("unlock")
|
||||
|
||||
onClicked: {
|
||||
gcd.unlockProfiles(txtPassword.text)
|
||||
txtPassword.text = ""
|
||||
error.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
Connections { // ADD/REMOVE CONTACT ENTRIES
|
||||
target: gcd
|
||||
|
||||
onErrorLoaded0: function() {
|
||||
error.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
|
||||
color: "#D2C0DD"
|
||||
width: thecol.width
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumWidth: Layout.maximumWidth
|
||||
//Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : 450
|
||||
|
||||
Widgets.ProfileList {
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -47,19 +47,36 @@ ColumnLayout {
|
|||
Connections { // ADD/REMOVE CONTACT ENTRIES
|
||||
target: gcd
|
||||
|
||||
onAddContact: function(handle, displayName, image, server, badge, status, trusted, blocked, loading) {
|
||||
contactsModel.append({
|
||||
"_handle": handle,
|
||||
"_displayName": displayName + (blocked ? " (blocked)" : "" ),
|
||||
"_image": image,
|
||||
"_server": server,
|
||||
"_badge": badge,
|
||||
"_status": status,
|
||||
"_trusted": trusted,
|
||||
"_blocked": blocked,
|
||||
"_loading": loading,
|
||||
"_loading": loading
|
||||
})
|
||||
onAddContact: function(handle, displayName, image, server, badge, status, blocked, loading, lastMsgTs) {
|
||||
|
||||
for (var i = 0; i < contactsModel.count; i++) {
|
||||
if (contactsModel.get(i)["_handle"] == handle) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var index = contactsModel.count
|
||||
for (var i = 0; i < contactsModel.count; i++) {
|
||||
if (contactsModel.get(i)["_lastMsgTs"] < lastMsgTs) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var newContact = {
|
||||
"_handle": handle,
|
||||
"_displayName": displayName + (blocked ? " (blocked)" : "" ),
|
||||
"_image": image,
|
||||
"_server": server,
|
||||
"_badge": badge,
|
||||
"_status": status,
|
||||
"_blocked": blocked,
|
||||
"_loading": loading,
|
||||
"_loading": loading,
|
||||
"_lastMsgTs": lastMsgTs
|
||||
}
|
||||
|
||||
contactsModel.insert(index, newContact)
|
||||
}
|
||||
|
||||
onRemoveContact: function(handle) {
|
||||
|
@ -71,6 +88,22 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
onIncContactUnreadCount: function(handle) {
|
||||
var ts = Math.round((new Date()).getTime() / 1000);
|
||||
for(var i = 0; i < contactsModel.count; i++){
|
||||
if(contactsModel.get(i)["_handle"] == handle) {
|
||||
var contact = contactsModel.get(i)
|
||||
contact["_lastMsgTs"] = ts
|
||||
console.log("Found at " + i + " contact: " + contact)
|
||||
contactsModel.move(i, 0, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onResetProfile: function() {
|
||||
contactsModel.clear()
|
||||
}
|
||||
}
|
||||
|
||||
ListModel { // CONTACT OBJECTS ARE STORED HERE ...
|
||||
|
@ -86,11 +119,18 @@ ColumnLayout {
|
|||
server: _server
|
||||
badge: _badge
|
||||
status: _status
|
||||
trusted: _trusted
|
||||
blocked: _blocked
|
||||
loading: _loading
|
||||
type: "contact"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ Item {
|
|||
property bool isGroup
|
||||
property bool showStatus
|
||||
property bool highlight
|
||||
property bool button
|
||||
property real logscale: 4 * Math.log10(gcd.themeScale + 1)
|
||||
property int baseWidth: 48 * logscale
|
||||
|
||||
|
@ -30,7 +31,7 @@ Item {
|
|||
Rectangle {
|
||||
width: highlight ? baseWidth - 4 : baseWidth
|
||||
height: highlight ? baseWidth - 4 : baseWidth
|
||||
color: "#FFFFFF"
|
||||
color: button ? windowItem.cwtch_dark_color: "#FFFFFF"
|
||||
radius: width / 2
|
||||
anchors.centerIn:parent
|
||||
|
||||
|
@ -69,7 +70,7 @@ Item {
|
|||
anchors.margins: 4 * logscale
|
||||
|
||||
|
||||
Rectangle { //-2:WtfCodeError,-1:Untrusted,0:Disconnected,1:Connecting,2:Connected,3:Authenticated,4:Synced,5:Failed,6:Killed
|
||||
Rectangle { //-2:WtfCodeError,-1:Error,0:Disconnected,1:Connecting,2:Connected,3:Authenticated,4:Synced,5:Failed,6:Killed
|
||||
color: status == 4 ? "green" : status == 3 ? "green" : status == -1 ? "blue" : status == 1 ? "orange" : status == 2 ? "orange" : "red"
|
||||
width: 5 * logscale
|
||||
height: 5 * logscale
|
||||
|
|
|
@ -10,6 +10,7 @@ import QtQuick.Controls.Styles 1.4
|
|||
|
||||
|
||||
Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
||||
id: crItem
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: 48 * logscale + 3
|
||||
|
@ -22,12 +23,18 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
|||
property int badge
|
||||
property bool isActive
|
||||
property bool isHover
|
||||
property bool trusted
|
||||
property bool background: true
|
||||
property string type // profile or contact or button
|
||||
property string tag // profile version/type
|
||||
|
||||
// Profile
|
||||
property bool defaultPassword
|
||||
|
||||
// Contact
|
||||
property bool blocked
|
||||
property bool loading
|
||||
property alias status: imgProfile.status
|
||||
property string server
|
||||
property bool background: true
|
||||
property alias status: imgProfile.status
|
||||
property string server
|
||||
|
||||
|
||||
Rectangle { // CONTACT ENTRY BACKGROUND COLOR
|
||||
|
@ -40,24 +47,43 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
|||
|
||||
ContactPicture {
|
||||
id: imgProfile
|
||||
showStatus: true
|
||||
showStatus: type == "contact"
|
||||
button: type == "button"
|
||||
}
|
||||
|
||||
Label { // CONTACT NAME
|
||||
id: cn
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
//wrapMode: Text.WordWrap
|
||||
ColumnLayout {
|
||||
|
||||
anchors.left: imgProfile.right
|
||||
anchors.right: loading ? loadingProgress.left : rectUnread.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.pixelSize: 16 * gcd.themeScale
|
||||
font.italic: !trusted
|
||||
font.strikeout: blocked
|
||||
textFormat: Text.PlainText
|
||||
//fontSizeMode: Text.HorizontalFit
|
||||
elide: Text.ElideRight
|
||||
color: "#000000"
|
||||
|
||||
|
||||
Label { // CONTACT NAME
|
||||
id: cn
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
//wrapMode: Text.WordWrap
|
||||
font.pixelSize: 16 * gcd.themeScale
|
||||
font.strikeout: blocked
|
||||
textFormat: Text.PlainText
|
||||
//fontSizeMode: Text.HorizontalFit
|
||||
elide: Text.ElideRight
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
Label { // Onion
|
||||
id: onion
|
||||
text: handle
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
font.pixelSize: 10 * gcd.themeScale
|
||||
font.strikeout: blocked
|
||||
textFormat: Text.PlainText
|
||||
//fontSizeMode: Text.HorizontalFit
|
||||
elide: Text.ElideRight
|
||||
color: "#000000"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle { // UNREAD MESSAGES?
|
||||
|
@ -87,6 +113,27 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
|||
}
|
||||
}
|
||||
|
||||
// Profile
|
||||
|
||||
Image {// Profle Type
|
||||
id: profiletype
|
||||
|
||||
source: tag == "v1-userPassword" ? "qrc:/qml/images/fontawesome/solid/lock.svg" : "qrc:/qml/images/fontawesome/solid/lock-open.svg"
|
||||
|
||||
anchors.right: parent.right
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 1 * gcd.themeScale
|
||||
anchors.rightMargin: (20 * gcd.themeScale) + parent.height
|
||||
height: parent.height * 0.5
|
||||
width: height
|
||||
|
||||
visible: type == "profile"
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Contact
|
||||
ProgressBar { // LOADING ?
|
||||
id: loadingProgress
|
||||
property bool running
|
||||
|
@ -118,14 +165,26 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
|||
hoverEnabled: true
|
||||
|
||||
onClicked: {
|
||||
if (displayName != "me") {
|
||||
gcd.broadcast("ResetMessagePane")
|
||||
isActive = true
|
||||
theStack.pane = theStack.messagePane
|
||||
gcd.loadMessagesPane(handle)
|
||||
if (handle.length == 32) {
|
||||
gcd.requestGroupSettings(handle)
|
||||
}
|
||||
if (type == "contact") {
|
||||
if (displayName != "me") {
|
||||
gcd.broadcast("ResetMessagePane")
|
||||
isActive = true
|
||||
theStack.pane = theStack.messagePane
|
||||
gcd.loadMessagesPane(handle)
|
||||
badge = 0
|
||||
if (handle.length == 32) {
|
||||
gcd.requestGroupSettings(handle)
|
||||
}
|
||||
}
|
||||
} else if (type == "profile") {
|
||||
gcd.broadcast("ResetMessagePane")
|
||||
gcd.broadcast("ResetProfile")
|
||||
gcd.selectedProfile = handle
|
||||
gcd.loadProfile(handle)
|
||||
parentStack.pane = parentStack.profilePane
|
||||
} else if (type == "button") { // Add profile button
|
||||
profileAddEditPane.reset()
|
||||
parentStack.pane = parentStack.addEditProfilePane
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -138,6 +197,27 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
|||
}
|
||||
}
|
||||
|
||||
SimpleButton {// Edit BUTTON
|
||||
id: btnEdit
|
||||
icon: "solid/user-edit"
|
||||
|
||||
anchors.right: parent.right
|
||||
|
||||
//rectUnread.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 1 * gcd.themeScale
|
||||
anchors.rightMargin: 20 * gcd.themeScale
|
||||
height: parent.height * 0.75
|
||||
|
||||
visible: type == "profile"
|
||||
|
||||
|
||||
onClicked: {
|
||||
profileAddEditPane.load(handle, displayName, tag)
|
||||
parentStack.pane = parentStack.addEditProfilePane
|
||||
}
|
||||
}
|
||||
|
||||
Connections { // UPDATE UNREAD MESSAGES COUNTER
|
||||
target: gcd
|
||||
|
||||
|
@ -145,17 +225,29 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
|||
isActive = false
|
||||
}
|
||||
|
||||
onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) {
|
||||
if (handle == _handle) {
|
||||
displayName = _displayName + (_blocked == true ? " (blocked)" : "")
|
||||
image = _image
|
||||
server = _server
|
||||
badge = _badge
|
||||
status = _status
|
||||
trusted = _trusted
|
||||
blocked = _blocked
|
||||
onUpdateContactStatus: function(_handle, _status, _loading) {
|
||||
if (handle == _handle) {
|
||||
status = _status
|
||||
loadingProgress.visible = loadingProgress.running = loading = _loading
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateContactBlocked: function(_handle, _blocked) {
|
||||
if (handle == _handle) {
|
||||
blocked = _blocked
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateContactDisplayName: function(_handle, _displayName) {
|
||||
if (handle == _handle) {
|
||||
displayName = _displayName + (_blocked == true ? " (blocked)" : "")
|
||||
}
|
||||
}
|
||||
|
||||
onIncContactUnreadCount: function(handle) {
|
||||
if (handle == _handle && gcd.selectedConversation != handle) {
|
||||
badge++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ Item {
|
|||
|
||||
|
||||
onClicked: {
|
||||
gcd.createContact(from)
|
||||
gcd.broadcast("ResetMessagePane")
|
||||
theStack.pane = theStack.messagePane
|
||||
gcd.loadMessagesPane(from)
|
||||
|
|
|
@ -19,6 +19,22 @@ ColumnLayout {
|
|||
property string onion
|
||||
|
||||
|
||||
SimpleButton {// BACK BUTTON
|
||||
id: btnBack
|
||||
icon: "solid/arrow-circle-left"
|
||||
anchors.left: parent.left
|
||||
//anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 2
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 2
|
||||
onClicked: function() {
|
||||
gcd.selectedProfile = "none"
|
||||
gcd.reloadProfileList()
|
||||
parentStack.pane = parentStack.managementPane
|
||||
theStack.pane = theStack.emptyPane
|
||||
}
|
||||
}
|
||||
|
||||
Item{ height: 6 }
|
||||
|
||||
Item { // PROFILE IMAGE
|
||||
|
@ -106,7 +122,7 @@ ColumnLayout {
|
|||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
onUpdated: {
|
||||
gcd.updateNick(lblNick.text)
|
||||
gcd.updateNick(onion, lblNick.text)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,8 +246,6 @@ ColumnLayout {
|
|||
lblNick.text = _nick
|
||||
onion = _onion
|
||||
image = _image
|
||||
parentStack.currentIndex = 1
|
||||
splashPane.running = false
|
||||
}
|
||||
|
||||
onTorStatus: function(code, str) {
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
import QtGraphicalEffects 1.0
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.4
|
||||
import QtQuick.Controls.Material 2.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
|
||||
onClicked: {
|
||||
forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
Flickable { // Profile List
|
||||
id: sv
|
||||
clip: true
|
||||
Layout.minimumHeight: 100
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumWidth: parent.width
|
||||
Layout.maximumWidth: parent.width
|
||||
contentWidth: colContacts.width
|
||||
contentHeight: colContacts.height
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
maximumFlickVelocity: 400
|
||||
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: ScrollBar.AlwaysOn
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: colContacts
|
||||
width: root.width
|
||||
spacing: 0
|
||||
|
||||
Connections { // ADD/REMOVE CONTACT ENTRIES
|
||||
target: gcd
|
||||
|
||||
onAddProfile: function(handle, displayName, image, tag) {
|
||||
|
||||
// don't add duplicates
|
||||
console.log("ProfileList onAddProfile for: " + handle)
|
||||
for (var i = 0; i < profilesModel.count; i++) {
|
||||
if (profilesModel.get(i)["_handle"] == handle) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// find index for insert (sort by onion)
|
||||
var index = profilesModel.count-1
|
||||
for (var i = 0; i < profilesModel.count-1; i++) {
|
||||
if (profilesModel.get(i)["_handle"] > handle) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
profilesModel.insert(index,
|
||||
{
|
||||
"_handle": handle,
|
||||
"_displayName": displayName,
|
||||
"_image": image,
|
||||
"_type": "profile",
|
||||
"_tag": tag
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
onRemoveProfile: function(handle) {
|
||||
for(var i = 0; i < profilesModel.count; i++){
|
||||
if(profilesModel.get(i)["_handle"] == handle) {
|
||||
console.log("deleting contact " + profilesModel.get(i)["_handle"])
|
||||
profilesModel.remove(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
onResetProfileList: function() {
|
||||
profilesModel.clear()
|
||||
profilesModel.append({
|
||||
_handle: "",
|
||||
_displayName: qsTr("add-new-profile-btn"),
|
||||
_image: "qrc:/qml/images/fontawesome/solid/user-plus.svg",
|
||||
_type: "button",
|
||||
_tag: ","
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ListModel { // Profile OBJECTS ARE STORED HERE ...
|
||||
id: profilesModel
|
||||
|
||||
ListElement {
|
||||
_handle: ""
|
||||
_displayName: qsTr("add-new-profile-btn")
|
||||
_image: "qrc:/qml/images/fontawesome/solid/user-plus.svg"
|
||||
_type: "button"
|
||||
_tag: ""
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: profilesModel // ... AND DISPLAYED HERE
|
||||
delegate: ContactRow {
|
||||
handle: _handle
|
||||
displayName: _displayName
|
||||
image: _image
|
||||
server: ""
|
||||
badge: 0
|
||||
status: 0
|
||||
blocked: false
|
||||
loading: false
|
||||
type: _type
|
||||
tag: _tag
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import QtQuick 2.7
|
||||
|
||||
import QtQuick.Controls 2.13
|
||||
|
||||
|
||||
RadioButton {
|
||||
id: control
|
||||
|
||||
property real size: 12
|
||||
spacing: 0
|
||||
|
||||
indicator: Rectangle {
|
||||
width: 16 * gcd.themeScale
|
||||
height: 16 * gcd.themeScale
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
radius: 9
|
||||
border.width: 1
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: control.checked
|
||||
color: "black"
|
||||
radius: 9
|
||||
anchors.margins: 4
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ import QtQuick.Controls 2.4
|
|||
import QtQuick.Controls.Material 2.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import "controls" as Awesome
|
||||
import "../fonts/Twemoji.js" as T
|
||||
|
||||
Rectangle {
|
||||
|
|
|
@ -4,7 +4,6 @@ import QtQuick.Controls 2.4
|
|||
import QtQuick.Controls.Material 2.0
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
import "controls" as Awesome
|
||||
import "../fonts/Twemoji.js" as T
|
||||
|
||||
Rectangle { // OVERHEAD BAR ON STACK PANE
|
||||
|
@ -21,6 +20,7 @@ Rectangle { // OVERHEAD BAR ON STACK PANE
|
|||
property alias aux: btnAux
|
||||
property alias back: btnBack
|
||||
property alias membership: btnMembership
|
||||
property string stack: "profile" // profile(theStack) or management(parentStack)
|
||||
|
||||
|
||||
SimpleButton {// BACK BUTTON
|
||||
|
@ -29,7 +29,13 @@ Rectangle { // OVERHEAD BAR ON STACK PANE
|
|||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.leftMargin: 6
|
||||
onClicked: theStack.pane = theStack.emptyPane
|
||||
onClicked: {
|
||||
if (stack == "profile") {
|
||||
theStack.pane = theStack.emptyPane
|
||||
} else {
|
||||
parentStack.pane = parentStack.managementPane
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScalingLabel { // TEXT
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import QtQuick 2.7
|
||||
|
||||
import QtQuick.Controls 2.13
|
||||
|
||||
|
||||
TextField {
|
||||
color: "black"
|
||||
font.pointSize: 10 * gcd.themeScale
|
||||
width: 100
|
||||
|
||||
background: Rectangle {
|
||||
radius: 2
|
||||
color: windowItem.cwtch_background_color
|
||||
border.color: windowItem.cwtch_color
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
**
|
||||
** Copyright (c) 2014 Ricardo do Valle Flores de Oliveira
|
||||
**
|
||||
** $BEGIN_LICENSE:MIT$
|
||||
** 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.
|
||||
**
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Controls.Styles 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
|
||||
Button {
|
||||
id: button
|
||||
property string icon
|
||||
property color color: "black"
|
||||
property font font
|
||||
|
||||
style: ButtonStyle {
|
||||
id: buttonstyle
|
||||
property font font: button.font
|
||||
property color foregroundColor: button.color
|
||||
|
||||
background: Item {
|
||||
Rectangle {
|
||||
id: baserect
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
label: Item {
|
||||
implicitWidth: row.implicitWidth
|
||||
implicitHeight: row.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
anchors.centerIn: parent
|
||||
spacing: 15
|
||||
|
||||
Text {
|
||||
color: buttonstyle.foregroundColor
|
||||
font.pointSize: buttonstyle.font.pointSize * 2
|
||||
font.family: awesome.family
|
||||
renderType: Text.NativeRendering
|
||||
text: awesome.loaded ? icon : ""
|
||||
visible: !(icon === "")
|
||||
}
|
||||
Text {
|
||||
color: buttonstyle.foregroundColor
|
||||
font: buttonstyle.font
|
||||
renderType: Text.NativeRendering
|
||||
text: control.text
|
||||
visible: !(control.text === "")
|
||||
|
||||
Layout.alignment: Qt.AlignBottom
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** The MIT License (MIT)
|
||||
**
|
||||
** Copyright (c) 2014 Ricardo do Valle Flores de Oliveira
|
||||
**
|
||||
** $BEGIN_LICENSE:MIT$
|
||||
** 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.
|
||||
**
|
||||
** $END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.0
|
||||
import QtQuick.Layouts 1.0
|
||||
|
||||
Text {
|
||||
id: root
|
||||
|
||||
property alias spacing: row.spacing
|
||||
property alias text: content.text
|
||||
property color color: "black"
|
||||
property font font
|
||||
property string icon
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
Text {
|
||||
color: root.color
|
||||
font.pointSize: root.font.pointSize
|
||||
font.family: awesome.family
|
||||
renderType: Text.NativeRendering
|
||||
text: awesome.loaded ? icon : ""
|
||||
}
|
||||
|
||||
Text {
|
||||
id: content
|
||||
color: root.color
|
||||
font.pointSize: root.font.pointSize
|
||||
renderType: Text.NativeRendering
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue