From f3a453f5a6b65b28d975a59d2849ac3e196a3769 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 25 Sep 2019 14:23:20 -0700 Subject: [PATCH 01/28] dev builds use seperate profiles --- go/characters/appEventListener.go | 7 +++++-- main.go | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/go/characters/appEventListener.go b/go/characters/appEventListener.go index 0c328d90..d1ffcbc7 100644 --- a/go/characters/appEventListener.go +++ b/go/characters/appEventListener.go @@ -56,8 +56,11 @@ func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool 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) + if gcd.Version() == "development" { + the.CwtchApp.CreatePeer("tester", the.AppPassword) + } else { + the.CwtchApp.CreatePeer("alice", the.AppPassword) + } } case event.ReloadDone: diff --git a/main.go b/main.go index 02b06105..fa3e32dd 100644 --- a/main.go +++ b/main.go @@ -57,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") @@ -74,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 From ab92e68628ad2afa5298df06640e4d2ca3e42ecf Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Thu, 26 Sep 2019 16:36:35 -0700 Subject: [PATCH 02/28] ping ACN status on window acticate (mostly for android) --- go/characters/appEventListener.go | 2 +- go/gothings/gcd.go | 6 ++++++ main.go | 1 + qml/main.qml | 11 +++++++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/go/characters/appEventListener.go b/go/characters/appEventListener.go index d1ffcbc7..8685ad97 100644 --- a/go/characters/appEventListener.go +++ b/go/characters/appEventListener.go @@ -73,7 +73,7 @@ func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool } onion := e.Data[event.Identity] - the.CwtchApp.AddPeerPlugin(onion, plugins.CONTACTRETRY) + the.CwtchApp.AddPeerPlugin(onion, plugins.CONNECTIONRETRY) the.Peer = the.CwtchApp.GetPeer(onion) the.EventBus = the.CwtchApp.GetEventBus(onion) diff --git a/go/gothings/gcd.go b/go/gothings/gcd.go index d9e8f2e4..cda95b9c 100644 --- a/go/gothings/gcd.go +++ b/go/gothings/gcd.go @@ -79,6 +79,7 @@ type GrandCentralDispatcher struct { _ func(locale string) `signal:"setLocale,auto"` _ func() `signal:"allowUnknownPeers,auto"` _ func() `signal:"blockUnknownPeers,auto"` + _ func() `signal:"onActivate,auto"` } func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { @@ -581,6 +582,11 @@ func (this *GrandCentralDispatcher) setLocale(locale string) { } +func (this *GrandCentralDispatcher) onActivate() { + log.Debugln("onActivate") + the.CwtchApp.QueryACNStatus() +} + func (this *GrandCentralDispatcher) SetLocale_helper(locale string) { core.QCoreApplication_RemoveTranslator(this.Translator) this.Translator = core.NewQTranslator(nil) diff --git a/main.go b/main.go index fa3e32dd..483e3e3a 100644 --- a/main.go +++ b/main.go @@ -118,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() } diff --git a/qml/main.qml b/qml/main.qml index d4584884..a7ab5d20 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -250,4 +250,15 @@ ApplicationWindow { theStack.title = str } } + + Connections { + target: Qt.application + onStateChanged: function() { + // https://doc.qt.io/qt-5/qt.html#ApplicationState-enum + if (Qt.application.state == 4) { + // Active + gcd.onActivate() + } + } + } } From b80500da352a43bac393500a349385e462a60571 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Thu, 3 Oct 2019 17:14:40 -0700 Subject: [PATCH 03/28] update go modules, drone try to use --- .drone.yml | 21 ++++++++++++++------- go.mod | 5 +++-- go.sum | 6 ++++++ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/.drone.yml b/.drone.yml index 9a72ab71..2570509f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -18,11 +18,13 @@ pipeline: - 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 + - export GO111MODULE=on + - 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 +46,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 @@ -73,11 +77,14 @@ pipeline: - 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 + - rm -r vendor/github.com/therecipe/qt/ - find -iname 'moc*' | xargs rm - find -iname 'rcc*' | xargs rm - - go get -d + - export GO111MODULE=on + - 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 diff --git a/go.mod b/go.mod index c963c578..984f5c37 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,13 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.3.0 + cwtch.im/cwtch v0.3.1 git.openprivacy.ca/openprivacy/libricochet-go v1.0.6 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-20190824160953-615e084bab56 + github.com/therecipe/qt v0.0.0-20191002095216-73192f6811d0 + github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 // indirect 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 diff --git a/go.sum b/go.sum index f9263af6..9d9db4b4 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ cwtch.im/cwtch v0.3.0 h1:RFZyc2m9BowFNdngBs7GcQE41w75jMp3Ku5zEE92v9I= cwtch.im/cwtch v0.3.0/go.mod h1:8tmtp3c7fccWw9H7s9u6E8GD2PKI3ar21i0fjN8pzd0= +cwtch.im/cwtch v0.3.1 h1:C0DLIrOqpNs5aecKTjNJZhpMq4/EvWNmLiKklIS8RTM= +cwtch.im/cwtch v0.3.1/go.mod h1:8tmtp3c7fccWw9H7s9u6E8GD2PKI3ar21i0fjN8pzd0= cwtch.im/tapir v0.1.10 h1:V+TkmwXNd6gySZqlVw468wMYEkmDwMSyvhkkpOfUw7w= cwtch.im/tapir v0.1.10/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= git.openprivacy.ca/openprivacy/libricochet-go v1.0.4/go.mod h1:yMSG1gBaP4f1U+RMZXN85d29D39OK5s8aTpyVRoH5FY= @@ -44,6 +46,10 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P 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-20191002095216-73192f6811d0 h1:Y4I5RGVEZubcplYny6zwqg0G7WUC6gw+esy+2cPCruA= +github.com/therecipe/qt v0.0.0-20191002095216-73192f6811d0/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us= +github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 h1:SC92QLlGwzwrT3Xi5YzKvtVXFIriPP0Ui1AOIZgDyh8= +github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0/go.mod h1:mH55Ek7AZcdns5KPp99O0bg+78el64YCYWHiQKrOdt4= 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= From a75f7778bee2e20529e1ab5e07f0872c29531e91 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 4 Oct 2019 14:15:01 -0700 Subject: [PATCH 04/28] make builds share vendor fetch --- .drone.yml | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/.drone.yml b/.drone.yml index 2570509f..0a53dbc6 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,6 +3,20 @@ workspace: path: ui pipeline: + fetch: + image: therecipe/qt:linux_static + environment: + - QT_DIR=/opt/Qt + - QT_DOCKER='true' + - QT_API=5.13.0 + - GO111MODULE=on + - GOCACHE=/media/sf_GOPATH1/src/cwtch.im/ui/.cache/go-build + commands: + - 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 + - git fetch --tags + - go mod download + - $QT_DIR/$QT_API/gcc_64/bin/lrelease ui.pro build-linux: mem_limit: 3G image: therecipe/qt:linux_static @@ -14,16 +28,14 @@ pipeline: - QT_DIR=/opt/Qt - QT_DOCKER='true' - QT_API=5.13.0 + - export GO111MODULE=on + - GOCACHE=/media/sf_GOPATH1/src/cwtch.im/ui/.cache/go-build 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 - - export GO111MODULE=on - - 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` + - rm -rf vendor - go mod vendor - qtdeploy -ldflags "-X main.buildVer=$VERSION -X main.buildDate=$BUILDDATE" build linux - cp README.md deploy/linux @@ -47,12 +59,11 @@ pipeline: - ANDROID_NDK_DIR=/home/user/android-ndk-r18b - ANDROID_SDK_DIR=/home/user/android-sdk-linux - GO111MODULE=on + - GOCACHE=/media/sf_GOPATH1/src/cwtch.im/ui/.cache/go-build commands: - 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 - - rm -r vendor/ - make clean - - go mod download - export VERSION=`git describe --tags` - export BUILDDATE=`date +%G-%m-%d-%H-%M` - go mod vendor @@ -73,15 +84,12 @@ pipeline: - QT_DIR=/opt/Qt - QT_DOCKER='true' - QT_API=5.13.0 + - export GO111MODULE=on + - GOCACHE=/media/sf_GOPATH1/src/cwtch.im/ui/.cache/go-build 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 - - rm -r vendor/github.com/therecipe/qt/ - - find -iname 'moc*' | xargs rm - - find -iname 'rcc*' | xargs rm - - export GO111MODULE=on - - go mod download + - make clean - export VERSION=`git describe --tags` - export BUILDDATE=`date +%G-%m-%d-%H-%M` - go mod vendor From 1773e52cbde58d43457ac77eff39d3b3c7ac7ce1 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 4 Oct 2019 14:44:45 -0700 Subject: [PATCH 05/28] undo drone builds share fetch --- .drone.yml | 29 +++++++++-------------------- README.md | 8 ++++---- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/.drone.yml b/.drone.yml index 0a53dbc6..f48637a2 100644 --- a/.drone.yml +++ b/.drone.yml @@ -3,20 +3,6 @@ workspace: path: ui pipeline: - fetch: - image: therecipe/qt:linux_static - environment: - - QT_DIR=/opt/Qt - - QT_DOCKER='true' - - QT_API=5.13.0 - - GO111MODULE=on - - GOCACHE=/media/sf_GOPATH1/src/cwtch.im/ui/.cache/go-build - commands: - - 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 - - git fetch --tags - - go mod download - - $QT_DIR/$QT_API/gcc_64/bin/lrelease ui.pro build-linux: mem_limit: 3G image: therecipe/qt:linux_static @@ -28,14 +14,15 @@ pipeline: - QT_DIR=/opt/Qt - QT_DOCKER='true' - QT_API=5.13.0 - - export GO111MODULE=on - - GOCACHE=/media/sf_GOPATH1/src/cwtch.im/ui/.cache/go-build + - GO111MODULE=on commands: - 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 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` - - rm -rf vendor - go mod vendor - qtdeploy -ldflags "-X main.buildVer=$VERSION -X main.buildDate=$BUILDDATE" build linux - cp README.md deploy/linux @@ -59,11 +46,12 @@ pipeline: - ANDROID_NDK_DIR=/home/user/android-ndk-r18b - ANDROID_SDK_DIR=/home/user/android-sdk-linux - GO111MODULE=on - - GOCACHE=/media/sf_GOPATH1/src/cwtch.im/ui/.cache/go-build commands: - 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 + - rm -r vendor/ - make clean + - go mod download - export VERSION=`git describe --tags` - export BUILDDATE=`date +%G-%m-%d-%H-%M` - go mod vendor @@ -84,12 +72,13 @@ pipeline: - QT_DIR=/opt/Qt - QT_DOCKER='true' - QT_API=5.13.0 - - export GO111MODULE=on - - GOCACHE=/media/sf_GOPATH1/src/cwtch.im/ui/.cache/go-build + - GO111MODULE=on commands: - 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 + - rm -r vendor - make clean + - go mod download - export VERSION=`git describe --tags` - export BUILDDATE=`date +%G-%m-%d-%H-%M` - go mod vendor diff --git a/README.md b/README.md index 96870753..0979608e 100644 --- a/README.md +++ b/README.md @@ -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 From 3fd5755ed9dbea055ba032e4919de0e7feac03c0 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 8 Oct 2019 11:02:34 -0700 Subject: [PATCH 06/28] makefile updates with linux and android targets and go mod support --- Makefile | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 60504756..96ffb095 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,19 @@ -all: +.PHONY: linux android -default: all +default: linux + +all: clean linux android + +linux: + go mod download + qtdeploy build linux | pv + +android: + go mod download + go mod vendor + qtdeploy -docker build android | pv clean: + rm -r vendor || true find -iname "moc*" | xargs rm find -iname "rcc*" | xargs rm From ee30a3227d396b1a9b45251e1687a80ff372e8fc Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Mon, 21 Oct 2019 12:19:18 -0700 Subject: [PATCH 07/28] remove frontend message storage --- go.mod | 2 +- go.sum | 4 +++ go/characters/incominglistener.go | 14 ++------ go/constants/attributes.go | 4 +++ go/gobjects/contact.go | 2 +- go/gothings/gcd.go | 50 +++++++++++++-------------- go/gothings/uistate.go | 57 ++++--------------------------- go/the/globals.go | 10 ------ qml/main.qml | 2 -- 9 files changed, 45 insertions(+), 100 deletions(-) create mode 100644 go/constants/attributes.go diff --git a/go.mod b/go.mod index 984f5c37..c412f01e 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.3.1 + cwtch.im/cwtch v0.3.2 git.openprivacy.ca/openprivacy/libricochet-go v1.0.6 github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect github.com/kr/pretty v0.1.0 // indirect diff --git a/go.sum b/go.sum index 9d9db4b4..c576e4e6 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,12 @@ cwtch.im/cwtch v0.3.0 h1:RFZyc2m9BowFNdngBs7GcQE41w75jMp3Ku5zEE92v9I= cwtch.im/cwtch v0.3.0/go.mod h1:8tmtp3c7fccWw9H7s9u6E8GD2PKI3ar21i0fjN8pzd0= cwtch.im/cwtch v0.3.1 h1:C0DLIrOqpNs5aecKTjNJZhpMq4/EvWNmLiKklIS8RTM= cwtch.im/cwtch v0.3.1/go.mod h1:8tmtp3c7fccWw9H7s9u6E8GD2PKI3ar21i0fjN8pzd0= +cwtch.im/cwtch v0.3.2 h1:JxoauToMckHjmQz3QCmI7XG9pun1tF3pV/o5ziuqV1A= +cwtch.im/cwtch v0.3.2/go.mod h1:4b2qGW5bZKm4CwYxqc0+4pgpDU0LjjyoihC8a/ezOoQ= cwtch.im/tapir v0.1.10 h1:V+TkmwXNd6gySZqlVw468wMYEkmDwMSyvhkkpOfUw7w= cwtch.im/tapir v0.1.10/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= +cwtch.im/tapir v0.1.11 h1:JLm1MIYq4VXKzhj68+P8OuVPllAU9U6G0DtUor2fbc4= +cwtch.im/tapir v0.1.11/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= 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= diff --git a/go/characters/incominglistener.go b/go/characters/incominglistener.go index c2f6697d..55ada538 100644 --- a/go/characters/incominglistener.go +++ b/go/characters/incominglistener.go @@ -41,16 +41,8 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { } 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) + uiState.Acknowledge(e.Data[event.EventID]) + case event.NewMessageFromGroup: //event.TimestampReceived, event.TimestampSent, event.Data, event.GroupID, event.RemotePeer var name string var exists bool @@ -92,7 +84,7 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { 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]) + uiState.AddSendMessageError(e.Data[event.RemotePeer], e.Data[event.EventID], e.Data[event.Error]) case event.PeerStateChange: cxnState := connections.ConnectionStateToType[e.Data[event.ConnectionState]] diff --git a/go/constants/attributes.go b/go/constants/attributes.go new file mode 100644 index 00000000..a95dc873 --- /dev/null +++ b/go/constants/attributes.go @@ -0,0 +1,4 @@ +package constants + +const Nick = "nick" +const LastRead = "last-read" diff --git a/go/gobjects/contact.go b/go/gobjects/contact.go index c038c1de..933ca39b 100644 --- a/go/gobjects/contact.go +++ b/go/gobjects/contact.go @@ -5,7 +5,7 @@ type Contact struct { DisplayName string Image string Server string - Badge int + Badge int // # of unread Status int Trusted bool Blocked bool diff --git a/go/gothings/gcd.go b/go/gothings/gcd.go index cda95b9c..a06bfc56 100644 --- a/go/gothings/gcd.go +++ b/go/gothings/gcd.go @@ -148,12 +148,6 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { false, false, }) - - ackID := new(the.AckId) - ackID.ID = mID - ackID.Ack = false - ackID.Peer = to - the.AcknowledgementIDs.Store(mID, ackID) } } @@ -232,42 +226,48 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { } // ELSE LOAD CONTACT contact, _ := the.Peer.GetProfile().GetContact(handle) + var nick string if contact != nil { - nick, _ := contact.GetAttribute("nick") + nick, _ = contact.GetAttribute("nick") if nick == "" { - this.SetToolbarTitle(handle) - } else { - this.SetToolbarTitle(nick) + nick = handle } } + 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 + var displayname string + ctc := the.Peer.GetContact(messages[i].PeerID) + if ctc != nil { + var exists bool + displayname, exists = ctc.GetAttribute("nick") + if !exists || displayname == "" { + displayname = messages[i].PeerID + } + } else { + displayname = 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, + string(messages[i].Signature), + fromMe, messages[i].Timestamp.Format(constants.TIME_FORMAT), - acked, - messages[i].Error, + messages[i].Acknowledged, + messages[i].Error != "", ) - } } diff --git a/go/gothings/uistate.go b/go/gothings/uistate.go index 8f55c800..30d284a0 100644 --- a/go/gothings/uistate.go +++ b/go/gothings/uistate.go @@ -14,11 +14,10 @@ import ( 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{}} + uis = InterfaceState{gcd, sync.Map{}} return } @@ -109,20 +108,6 @@ func (this *InterfaceState) GetContact(handle string) *gobjects.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) @@ -132,18 +117,6 @@ func (this *InterfaceState) AddSendMessageError(peer string, signature string, e 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. @@ -159,34 +132,18 @@ func (this *InterfaceState) AddMessage(m *gobjects.Message) { 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) + cif, found := this.contacts.Load(handle) + if found { + c := cif.(*gobjects.Contact) + if contact != nil { 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) } + this.parentGcd.UpdateContact(c.Handle, c.DisplayName, c.Image, c.Server, c.Badge, c.Status, c.Trusted, c.Blocked, c.Loading) + } } diff --git a/go/the/globals.go b/go/the/globals.go index 013a76cb..b4813824 100644 --- a/go/the/globals.go +++ b/go/the/globals.go @@ -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 diff --git a/qml/main.qml b/qml/main.qml index a7ab5d20..71b3db84 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -131,8 +131,6 @@ ApplicationWindow { running: true } } - - RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR /* anchors.left: ratio >= 0.92 ? parent.left : toolbar.right anchors.top: ratio >= 0.92 ? toolbar.bottom : parent.top From 5bea6bdc474c59ffea4389c1d3abd6da89425da1 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 25 Oct 2019 12:26:25 -0700 Subject: [PATCH 08/28] remove 'badge'/unread treacking state from go code; now uses attributes to store in back end and maintain in front end --- go/characters/appEventListener.go | 5 ++- go/characters/incominglistener.go | 4 +- go/gobjects/contact.go | 1 - go/gothings/gcd.go | 31 ++++++++-------- go/gothings/uistate.go | 62 +++++++++++++++++++++++++------ qml/overlays/BulletinOverlay.qml | 2 +- qml/overlays/ChatOverlay.qml | 2 +- qml/overlays/ListOverlay.qml | 2 +- qml/widgets/ContactRow.qml | 10 ++++- 9 files changed, 82 insertions(+), 37 deletions(-) diff --git a/go/characters/appEventListener.go b/go/characters/appEventListener.go index 8685ad97..2ea4fec7 100644 --- a/go/characters/appEventListener.go +++ b/go/characters/appEventListener.go @@ -87,7 +87,7 @@ func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool contacts := the.Peer.GetContacts() for i := range contacts { contact, _ := the.Peer.GetProfile().GetContact(contacts[i]) - displayName, _ := contact.GetAttribute("nick") + displayName, _ := contact.GetAttribute(constants.Nick) gcd.UIState.AddContact(&gobjects.Contact{ Handle: contacts[i], @@ -102,10 +102,11 @@ func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool groups := the.Peer.GetGroups() for i := range groups { group := the.Peer.GetGroup(groups[i]) - nick, exists := group.GetAttribute("nick") + nick, exists := group.GetAttribute(constants.Nick) if !exists { nick = group.GroupID[:12] } + // Only join servers for active and explicitly accepted groups. gcd.UIState.AddContact(&gobjects.Contact{ Handle: group.GroupID, diff --git a/go/characters/incominglistener.go b/go/characters/incominglistener.go index 55ada538..0a639075 100644 --- a/go/characters/incominglistener.go +++ b/go/characters/incominglistener.go @@ -3,6 +3,7 @@ package characters import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/protocol/connections" + "cwtch.im/ui/go/constants" "cwtch.im/ui/go/cwutil" "cwtch.im/ui/go/gobjects" "cwtch.im/ui/go/gothings" @@ -48,7 +49,7 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { var exists bool ctc := the.Peer.GetContact(e.Data[event.RemotePeer]) if ctc != nil { - name, exists = ctc.GetAttribute("nick") + name, exists = ctc.GetAttribute(constants.Nick) if !exists || name == "" { name = e.Data[event.RemotePeer] } @@ -114,7 +115,6 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { e.Data[event.RemotePeer], cwutil.RandomProfileImage(e.Data[event.RemotePeer]), "", - 0, int(cxnState), false, false, diff --git a/go/gobjects/contact.go b/go/gobjects/contact.go index 933ca39b..75807d56 100644 --- a/go/gobjects/contact.go +++ b/go/gobjects/contact.go @@ -5,7 +5,6 @@ type Contact struct { DisplayName string Image string Server string - Badge int // # of unread Status int Trusted bool Blocked bool diff --git a/go/gothings/gcd.go b/go/gothings/gcd.go index a06bfc56..22802c1d 100644 --- a/go/gothings/gcd.go +++ b/go/gothings/gcd.go @@ -31,7 +31,8 @@ type GrandCentralDispatcher struct { // 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, displayName, image, server string, status int, trusted bool, blocked bool, loading bool) `signal:"UpdateContact"` + _ func(handle string) `signal:"IncContactUnreadCount"` _ func(handle string) `signal:"RemoveContact"` _ func(handle, key, value string) `signal:"UpdateContactAttribute"` @@ -171,20 +172,18 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { cwutil.RandomProfileImage(handle), "", 0, - 0, false, false, false, }) } else { - c.Badge = 0 this.UIState.UpdateContact(handle) } if len(handle) == 32 { // LOAD GROUP group := the.Peer.GetGroup(handle) tl := group.GetTimeline() - nick, _ := group.GetAttribute("nick") + nick, _ := group.GetAttribute(constants.Nick) if nick == "" { this.SetToolbarTitle(handle) } else { @@ -201,7 +200,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { var exists bool ctc := the.Peer.GetContact(tl[i].PeerID) if ctc != nil { - name, exists = ctc.GetAttribute("nick") + name, exists = ctc.GetAttribute(constants.Nick) if !exists || name == "" { name = tl[i].PeerID } @@ -228,7 +227,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { contact, _ := the.Peer.GetProfile().GetContact(handle) var nick string if contact != nil { - nick, _ = contact.GetAttribute("nick") + nick, _ = contact.GetAttribute(constants.Nick) if nick == "" { nick = handle } @@ -248,7 +247,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { ctc := the.Peer.GetContact(messages[i].PeerID) if ctc != nil { var exists bool - displayname, exists = ctc.GetAttribute("nick") + displayname, exists = ctc.GetAttribute(constants.Nick) if !exists || displayname == "" { displayname = messages[i].PeerID } @@ -300,7 +299,7 @@ func (this *GrandCentralDispatcher) requestPeerSettings() { 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) @@ -317,10 +316,10 @@ func (this *GrandCentralDispatcher) savePeerSettings(onion, nick string) { return } - contact.SetAttribute("nick", nick) + contact.SetAttribute(constants.Nick, nick) the.EventBus.Publish(event.NewEvent(event.SetPeerAttribute, map[event.Field]string{ event.RemotePeer: onion, - event.Key: "nick", + event.Key: constants.Nick, event.Data: nick, })) @@ -338,13 +337,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 { @@ -363,10 +362,10 @@ func (this *GrandCentralDispatcher) saveGroupSettings(groupID, nick string) { return } - group.SetAttribute("nick", nick) + group.SetAttribute(constants.Nick, nick) the.EventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{ event.GroupID: groupID, - event.Key: "nick", + event.Key: constants.Nick, event.Data: nick, })) @@ -480,10 +479,10 @@ func (this *GrandCentralDispatcher) createGroup(server, groupName string) { }) group := the.Peer.GetGroup(groupID) - group.SetAttribute("nick", groupName) + group.SetAttribute(constants.Nick, groupName) the.EventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{ event.GroupID: groupID, - event.Key: "nick", + event.Key: constants.Nick, event.Data: groupName, })) diff --git a/go/gothings/uistate.go b/go/gothings/uistate.go index 30d284a0..2bac7a83 100644 --- a/go/gothings/uistate.go +++ b/go/gothings/uistate.go @@ -1,6 +1,7 @@ package gothings import ( + "cwtch.im/cwtch/model" "cwtch.im/ui/go/constants" "cwtch.im/ui/go/cwutil" "cwtch.im/ui/go/gobjects" @@ -11,6 +12,39 @@ import ( "time" ) +type Attributable interface { + GetAttribute(string) (string, bool) + SetAttribute(string, string) +} + +// initLastReadTime checks and gets the Attributable's LastRead time or sets it to now +func initLastReadTime(attr Attributable) time.Time { + lastRead := time.Now() + lastReadVal, ok := attr.GetAttribute(constants.LastRead) + if ok { + err := lastRead.UnmarshalText([]byte(lastReadVal)) + if err != nil { + lastRead = time.Now() + } + } else { + lastReadVal, _ := lastRead.MarshalText() + attr.SetAttribute(constants.LastRead, string(lastReadVal)) + } + return 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) { + count++ + } else { + break + } + } + return count +} + type InterfaceState struct { parentGcd *GrandCentralDispatcher contacts sync.Map // string : *gobjects.Contact @@ -27,8 +61,14 @@ func (this *InterfaceState) Acknowledge(mID string) { func (this *InterfaceState) AddContact(c *gobjects.Contact) { if len(c.Handle) == 32 { // ADD GROUP + unread := 0 + group := the.Peer.GetGroup(c.Handle) + if group != nil { + lastRead := initLastReadTime(group) + unread = countUnread(group.Timeline.GetMessages(), lastRead) + } 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.parentGcd.AddContact(c.Handle, c.DisplayName, c.Image, c.Server, unread, c.Status, c.Trusted, c.Blocked, c.Loading) this.contacts.Store(c.Handle, c) } return @@ -38,9 +78,15 @@ func (this *InterfaceState) AddContact(c *gobjects.Contact) { return } + unread := 0 + contact := the.Peer.GetContact(c.Handle) + if contact != nil { + lastRead := initLastReadTime(contact) + unread = countUnread(contact.Timeline.GetMessages(), lastRead) + } 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) + this.parentGcd.AddContact(c.Handle, c.DisplayName, c.Image, c.Server, unread, 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) @@ -57,7 +103,7 @@ func (this *InterfaceState) GetContact(handle string) *gobjects.Contact { if len(handle) == 32 { group := the.Peer.GetGroup(handle) if group != nil { - nick, exists := group.GetAttribute("nick") + nick, exists := group.GetAttribute(constants.Nick) if !exists { nick = group.GroupID[:12] } @@ -67,7 +113,6 @@ func (this *InterfaceState) GetContact(handle string) *gobjects.Contact { cwutil.RandomGroupImage(handle), group.GroupServer, 0, - 0, group.Accepted, false, false, @@ -88,7 +133,6 @@ func (this *InterfaceState) GetContact(handle string) *gobjects.Contact { cwutil.RandomProfileImage(handle), "", 0, - 0, false, contact.Blocked, false, @@ -126,11 +170,7 @@ func (this *InterfaceState) AddMessage(m *gobjects.Message) { this.parentGcd.Acknowledged(m.MessageID) } } else { - c := this.GetContact(m.Handle) - if c != nil { - c.Badge++ - this.UpdateContact(c.Handle) - } + this.parentGcd.IncContactUnreadCount(m.Handle) } } @@ -142,7 +182,7 @@ func (this *InterfaceState) UpdateContact(handle string) { if contact != nil { 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) + this.parentGcd.UpdateContact(c.Handle, c.DisplayName, c.Image, c.Server, c.Status, c.Trusted, c.Blocked, c.Loading) } } diff --git a/qml/overlays/BulletinOverlay.qml b/qml/overlays/BulletinOverlay.qml index 1e1f3cd8..c8abe9a6 100644 --- a/qml/overlays/BulletinOverlay.qml +++ b/qml/overlays/BulletinOverlay.qml @@ -92,7 +92,7 @@ ColumnLayout { } } - onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) { + onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { if (gcd.currentOpenConversation == _handle) { if (_loading == true) { newposttitle.enabled = false diff --git a/qml/overlays/ChatOverlay.qml b/qml/overlays/ChatOverlay.qml index 061e14b5..59dfefa4 100644 --- a/qml/overlays/ChatOverlay.qml +++ b/qml/overlays/ChatOverlay.qml @@ -110,7 +110,7 @@ ColumnLayout { messagesListView.positionViewAtEnd() } - onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) { + onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { if (gcd.currentOpenConversation == _handle) { // Group is Synced OR p2p is Authenticated if ( (_handle.length == 32 && _status == 4) || _status == 3) { diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 5304ac28..1515a4f1 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -96,7 +96,7 @@ ColumnLayout { } } - onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) { + onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { if (gcd.currentOpenConversation == _handle) { if (_loading == true) { newposttitle.enabled = false diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 3b4f2661..a3f3f9e9 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -123,6 +123,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY isActive = true theStack.pane = theStack.messagePane gcd.loadMessagesPane(handle) + badge = 0 if (handle.length == 32) { gcd.requestGroupSettings(handle) } @@ -145,17 +146,22 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY isActive = false } - onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) { + onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { if (handle == _handle) { displayName = _displayName + (_blocked == true ? " (blocked)" : "") image = _image server = _server - badge = _badge status = _status trusted = _trusted blocked = _blocked loadingProgress.visible = loadingProgress.running = loading = _loading } } + + onIncContactUnreadCount: function(handle) { + if (handle == _handle) { + badge++ + } + } } } From 1bf2910b4d439a728289882dab3638c5a547e828 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 25 Oct 2019 12:26:25 -0700 Subject: [PATCH 09/28] remove 'badge'/unread treacking state from go code; now uses attributes to store in back end and maintain in front end --- go/characters/appEventListener.go | 5 ++- go/characters/incominglistener.go | 4 +- go/gobjects/contact.go | 1 - go/gothings/gcd.go | 33 +++++++------- go/gothings/uistate.go | 74 ++++++++++++++++++++++++++----- qml/overlays/BulletinOverlay.qml | 2 +- qml/overlays/ChatOverlay.qml | 2 +- qml/overlays/ListOverlay.qml | 2 +- qml/widgets/ContactRow.qml | 10 ++++- 9 files changed, 95 insertions(+), 38 deletions(-) diff --git a/go/characters/appEventListener.go b/go/characters/appEventListener.go index 8685ad97..2ea4fec7 100644 --- a/go/characters/appEventListener.go +++ b/go/characters/appEventListener.go @@ -87,7 +87,7 @@ func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool contacts := the.Peer.GetContacts() for i := range contacts { contact, _ := the.Peer.GetProfile().GetContact(contacts[i]) - displayName, _ := contact.GetAttribute("nick") + displayName, _ := contact.GetAttribute(constants.Nick) gcd.UIState.AddContact(&gobjects.Contact{ Handle: contacts[i], @@ -102,10 +102,11 @@ func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool groups := the.Peer.GetGroups() for i := range groups { group := the.Peer.GetGroup(groups[i]) - nick, exists := group.GetAttribute("nick") + nick, exists := group.GetAttribute(constants.Nick) if !exists { nick = group.GroupID[:12] } + // Only join servers for active and explicitly accepted groups. gcd.UIState.AddContact(&gobjects.Contact{ Handle: group.GroupID, diff --git a/go/characters/incominglistener.go b/go/characters/incominglistener.go index 55ada538..0a639075 100644 --- a/go/characters/incominglistener.go +++ b/go/characters/incominglistener.go @@ -3,6 +3,7 @@ package characters import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/protocol/connections" + "cwtch.im/ui/go/constants" "cwtch.im/ui/go/cwutil" "cwtch.im/ui/go/gobjects" "cwtch.im/ui/go/gothings" @@ -48,7 +49,7 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { var exists bool ctc := the.Peer.GetContact(e.Data[event.RemotePeer]) if ctc != nil { - name, exists = ctc.GetAttribute("nick") + name, exists = ctc.GetAttribute(constants.Nick) if !exists || name == "" { name = e.Data[event.RemotePeer] } @@ -114,7 +115,6 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { e.Data[event.RemotePeer], cwutil.RandomProfileImage(e.Data[event.RemotePeer]), "", - 0, int(cxnState), false, false, diff --git a/go/gobjects/contact.go b/go/gobjects/contact.go index 933ca39b..75807d56 100644 --- a/go/gobjects/contact.go +++ b/go/gobjects/contact.go @@ -5,7 +5,6 @@ type Contact struct { DisplayName string Image string Server string - Badge int // # of unread Status int Trusted bool Blocked bool diff --git a/go/gothings/gcd.go b/go/gothings/gcd.go index a06bfc56..0caf4499 100644 --- a/go/gothings/gcd.go +++ b/go/gothings/gcd.go @@ -31,7 +31,8 @@ type GrandCentralDispatcher struct { // 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, displayName, image, server string, status int, trusted bool, blocked bool, loading bool) `signal:"UpdateContact"` + _ func(handle string) `signal:"IncContactUnreadCount"` _ func(handle string) `signal:"RemoveContact"` _ func(handle, key, value string) `signal:"UpdateContactAttribute"` @@ -171,20 +172,19 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { cwutil.RandomProfileImage(handle), "", 0, - 0, false, false, false, }) } else { - c.Badge = 0 this.UIState.UpdateContact(handle) } if len(handle) == 32 { // LOAD GROUP group := the.Peer.GetGroup(handle) tl := group.GetTimeline() - nick, _ := group.GetAttribute("nick") + nick, _ := group.GetAttribute(constants.Nick) + updateLastReadTime(group) if nick == "" { this.SetToolbarTitle(handle) } else { @@ -201,7 +201,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { var exists bool ctc := the.Peer.GetContact(tl[i].PeerID) if ctc != nil { - name, exists = ctc.GetAttribute("nick") + name, exists = ctc.GetAttribute(constants.Nick) if !exists || name == "" { name = tl[i].PeerID } @@ -228,11 +228,12 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { contact, _ := the.Peer.GetProfile().GetContact(handle) var nick string if contact != nil { - nick, _ = contact.GetAttribute("nick") + nick, _ = contact.GetAttribute(constants.Nick) if nick == "" { nick = handle } } + updateLastReadTime(contact) this.SetToolbarTitle(nick) peer := the.Peer.GetContact(handle) @@ -248,7 +249,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { ctc := the.Peer.GetContact(messages[i].PeerID) if ctc != nil { var exists bool - displayname, exists = ctc.GetAttribute("nick") + displayname, exists = ctc.GetAttribute(constants.Nick) if !exists || displayname == "" { displayname = messages[i].PeerID } @@ -300,7 +301,7 @@ func (this *GrandCentralDispatcher) requestPeerSettings() { 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) @@ -317,10 +318,10 @@ func (this *GrandCentralDispatcher) savePeerSettings(onion, nick string) { return } - contact.SetAttribute("nick", nick) + contact.SetAttribute(constants.Nick, nick) the.EventBus.Publish(event.NewEvent(event.SetPeerAttribute, map[event.Field]string{ event.RemotePeer: onion, - event.Key: "nick", + event.Key: constants.Nick, event.Data: nick, })) @@ -338,13 +339,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 { @@ -363,10 +364,10 @@ func (this *GrandCentralDispatcher) saveGroupSettings(groupID, nick string) { return } - group.SetAttribute("nick", nick) + group.SetAttribute(constants.Nick, nick) the.EventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{ event.GroupID: groupID, - event.Key: "nick", + event.Key: constants.Nick, event.Data: nick, })) @@ -480,10 +481,10 @@ func (this *GrandCentralDispatcher) createGroup(server, groupName string) { }) group := the.Peer.GetGroup(groupID) - group.SetAttribute("nick", groupName) + group.SetAttribute(constants.Nick, groupName) the.EventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{ event.GroupID: groupID, - event.Key: "nick", + event.Key: constants.Nick, event.Data: groupName, })) diff --git a/go/gothings/uistate.go b/go/gothings/uistate.go index 30d284a0..14dae73d 100644 --- a/go/gothings/uistate.go +++ b/go/gothings/uistate.go @@ -1,6 +1,7 @@ package gothings import ( + "cwtch.im/cwtch/model" "cwtch.im/ui/go/constants" "cwtch.im/ui/go/cwutil" "cwtch.im/ui/go/gobjects" @@ -11,6 +12,45 @@ import ( "time" ) +type Attributable interface { + GetAttribute(string) (string, bool) + SetAttribute(string, string) +} + +// initLastReadTime checks and gets the Attributable's LastRead time or sets it to now +func initLastReadTime(attr Attributable) time.Time { + lastRead := time.Now() + lastReadVal, ok := attr.GetAttribute(constants.LastRead) + if ok { + err := lastRead.UnmarshalText([]byte(lastReadVal)) + if err != nil { + lastRead = time.Now() + } + } else { + lastReadVal, _ := lastRead.MarshalText() + attr.SetAttribute(constants.LastRead, string(lastReadVal)) + } + return lastRead +} + +func updateLastReadTime(attr Attributable) { + lastRead := time.Now() + lastReadVal, _ := lastRead.MarshalText() + attr.SetAttribute(constants.LastRead, string(lastReadVal)) +} + +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) { + count++ + } else { + break + } + } + return count +} + type InterfaceState struct { parentGcd *GrandCentralDispatcher contacts sync.Map // string : *gobjects.Contact @@ -27,8 +67,14 @@ func (this *InterfaceState) Acknowledge(mID string) { func (this *InterfaceState) AddContact(c *gobjects.Contact) { if len(c.Handle) == 32 { // ADD GROUP + unread := 0 + group := the.Peer.GetGroup(c.Handle) + if group != nil { + lastRead := initLastReadTime(group) + unread = countUnread(group.Timeline.GetMessages(), lastRead) + } 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.parentGcd.AddContact(c.Handle, c.DisplayName, c.Image, c.Server, unread, c.Status, c.Trusted, c.Blocked, c.Loading) this.contacts.Store(c.Handle, c) } return @@ -38,9 +84,15 @@ func (this *InterfaceState) AddContact(c *gobjects.Contact) { return } + unread := 0 + contact := the.Peer.GetContact(c.Handle) + if contact != nil { + lastRead := initLastReadTime(contact) + unread = countUnread(contact.Timeline.GetMessages(), lastRead) + } 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) + this.parentGcd.AddContact(c.Handle, c.DisplayName, c.Image, c.Server, unread, 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) @@ -57,7 +109,7 @@ func (this *InterfaceState) GetContact(handle string) *gobjects.Contact { if len(handle) == 32 { group := the.Peer.GetGroup(handle) if group != nil { - nick, exists := group.GetAttribute("nick") + nick, exists := group.GetAttribute(constants.Nick) if !exists { nick = group.GroupID[:12] } @@ -67,7 +119,6 @@ func (this *InterfaceState) GetContact(handle string) *gobjects.Contact { cwutil.RandomGroupImage(handle), group.GroupServer, 0, - 0, group.Accepted, false, false, @@ -88,7 +139,6 @@ func (this *InterfaceState) GetContact(handle string) *gobjects.Contact { cwutil.RandomProfileImage(handle), "", 0, - 0, false, contact.Blocked, false, @@ -115,7 +165,11 @@ func (this *InterfaceState) AddSendMessageError(peer string, signature string, e } func (this *InterfaceState) AddMessage(m *gobjects.Message) { - this.GetContact(m.From) + if len(m.Handle) == 32 { // GROUP + updateLastReadTime(the.Peer.GetGroup(m.Handle)) + } else { + updateLastReadTime(the.Peer.GetContact(m.Handle)) + } // If we have this group loaded already if this.parentGcd.CurrentOpenConversation() == m.Handle { @@ -126,11 +180,7 @@ func (this *InterfaceState) AddMessage(m *gobjects.Message) { this.parentGcd.Acknowledged(m.MessageID) } } else { - c := this.GetContact(m.Handle) - if c != nil { - c.Badge++ - this.UpdateContact(c.Handle) - } + this.parentGcd.IncContactUnreadCount(m.Handle) } } @@ -142,7 +192,7 @@ func (this *InterfaceState) UpdateContact(handle string) { if contact != nil { 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) + this.parentGcd.UpdateContact(c.Handle, c.DisplayName, c.Image, c.Server, c.Status, c.Trusted, c.Blocked, c.Loading) } } diff --git a/qml/overlays/BulletinOverlay.qml b/qml/overlays/BulletinOverlay.qml index 1e1f3cd8..c8abe9a6 100644 --- a/qml/overlays/BulletinOverlay.qml +++ b/qml/overlays/BulletinOverlay.qml @@ -92,7 +92,7 @@ ColumnLayout { } } - onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) { + onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { if (gcd.currentOpenConversation == _handle) { if (_loading == true) { newposttitle.enabled = false diff --git a/qml/overlays/ChatOverlay.qml b/qml/overlays/ChatOverlay.qml index 061e14b5..59dfefa4 100644 --- a/qml/overlays/ChatOverlay.qml +++ b/qml/overlays/ChatOverlay.qml @@ -110,7 +110,7 @@ ColumnLayout { messagesListView.positionViewAtEnd() } - onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) { + onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { if (gcd.currentOpenConversation == _handle) { // Group is Synced OR p2p is Authenticated if ( (_handle.length == 32 && _status == 4) || _status == 3) { diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 5304ac28..1515a4f1 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -96,7 +96,7 @@ ColumnLayout { } } - onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) { + onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { if (gcd.currentOpenConversation == _handle) { if (_loading == true) { newposttitle.enabled = false diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 3b4f2661..a3f3f9e9 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -123,6 +123,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY isActive = true theStack.pane = theStack.messagePane gcd.loadMessagesPane(handle) + badge = 0 if (handle.length == 32) { gcd.requestGroupSettings(handle) } @@ -145,17 +146,22 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY isActive = false } - onUpdateContact: function(_handle, _displayName, _image, _server, _badge, _status, _trusted, _blocked, _loading) { + onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { if (handle == _handle) { displayName = _displayName + (_blocked == true ? " (blocked)" : "") image = _image server = _server - badge = _badge status = _status trusted = _trusted blocked = _blocked loadingProgress.visible = loadingProgress.running = loading = _loading } } + + onIncContactUnreadCount: function(handle) { + if (handle == _handle) { + badge++ + } + } } } From f0099acb2bdc3a5aa81096fee86a423e85a2272e Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 1 Nov 2019 17:40:06 -0700 Subject: [PATCH 10/28] remove uistate's state; remove contact; remove trusted --- go.mod | 2 +- go.sum | 10 ++ go/characters/appEventListener.go | 28 +---- go/characters/incominglistener.go | 43 ++----- go/constants/attributes.go | 1 + go/cwutil/utils.go | 3 +- go/gobjects/contact.go | 12 -- go/gothings/gcd.go | 166 ++++++------------------- go/gothings/uistate.go | 192 ++++++++++++----------------- qml/overlays/BulletinOverlay.qml | 6 +- qml/overlays/ChatOverlay.qml | 4 +- qml/overlays/ListOverlay.qml | 6 +- qml/overlays/MembershipOverlay.qml | 1 - qml/widgets/ContactList.qml | 4 +- qml/widgets/ContactPicture.qml | 2 +- qml/widgets/ContactRow.qml | 29 +++-- 16 files changed, 165 insertions(+), 344 deletions(-) delete mode 100644 go/gobjects/contact.go diff --git a/go.mod b/go.mod index c412f01e..a3e05401 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.3.2 + cwtch.im/cwtch v0.3.3 git.openprivacy.ca/openprivacy/libricochet-go v1.0.6 github.com/gopherjs/gopherjs v0.0.0-20190812055157-5d271430af9f // indirect github.com/kr/pretty v0.1.0 // indirect diff --git a/go.sum b/go.sum index c576e4e6..ead507ab 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ cwtch.im/cwtch v0.3.1 h1:C0DLIrOqpNs5aecKTjNJZhpMq4/EvWNmLiKklIS8RTM= cwtch.im/cwtch v0.3.1/go.mod h1:8tmtp3c7fccWw9H7s9u6E8GD2PKI3ar21i0fjN8pzd0= cwtch.im/cwtch v0.3.2 h1:JxoauToMckHjmQz3QCmI7XG9pun1tF3pV/o5ziuqV1A= cwtch.im/cwtch v0.3.2/go.mod h1:4b2qGW5bZKm4CwYxqc0+4pgpDU0LjjyoihC8a/ezOoQ= +cwtch.im/cwtch v0.3.3 h1:mAypnkTCehej5ebSEzl43nPufsyyXLNz/dw2RWOO+Wk= +cwtch.im/cwtch v0.3.3/go.mod h1:I95rbE3aK8uic7LsMOB1lfJDSzlNsRUP0/5cFCLkD0Y= cwtch.im/tapir v0.1.10 h1:V+TkmwXNd6gySZqlVw468wMYEkmDwMSyvhkkpOfUw7w= cwtch.im/tapir v0.1.10/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= cwtch.im/tapir v0.1.11 h1:JLm1MIYq4VXKzhj68+P8OuVPllAU9U6G0DtUor2fbc4= @@ -14,6 +16,7 @@ git.openprivacy.ca/openprivacy/libricochet-go v1.0.6/go.mod h1:yMSG1gBaP4f1U+RMZ 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= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cretz/bine v0.1.0 h1:1/fvhLE+fk0bPzjdO5Ci+0ComYxEMuB1JhM4X5skT3g= github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -25,6 +28,7 @@ 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/gordonklaus/ineffassign v0.0.0-20190601041439-ed7b1b5ee0f8/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= 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= @@ -61,14 +65,17 @@ golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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 +85,10 @@ 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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191031220737-6d8f1af9ccc0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go/characters/appEventListener.go b/go/characters/appEventListener.go index 2ea4fec7..b87abe17 100644 --- a/go/characters/appEventListener.go +++ b/go/characters/appEventListener.go @@ -5,7 +5,6 @@ import ( "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" @@ -86,36 +85,13 @@ func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool contacts := the.Peer.GetContacts() for i := range contacts { - contact, _ := the.Peer.GetProfile().GetContact(contacts[i]) - displayName, _ := contact.GetAttribute(constants.Nick) - - gcd.UIState.AddContact(&gobjects.Contact{ - Handle: contacts[i], - DisplayName: displayName, - Image: cwutil.RandomProfileImage(contacts[i]), - Trusted: contact.Trusted, - Blocked: contact.Blocked, - Loading: false, - }) + gcd.UIState.AddContact(contacts[i]) } groups := the.Peer.GetGroups() for i := range groups { - group := the.Peer.GetGroup(groups[i]) - nick, exists := group.GetAttribute(constants.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, - }) + gcd.UIState.AddContact(groups[i]) } if e.Data[event.Status] != "running" { diff --git a/go/characters/incominglistener.go b/go/characters/incominglistener.go index 0a639075..b45a97be 100644 --- a/go/characters/incominglistener.go +++ b/go/characters/incominglistener.go @@ -73,14 +73,7 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { 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, - }) + uiState.AddContact(gid) } case event.SendMessageToGroupError: uiState.AddSendMessageError(e.Data[event.GroupServer], e.Data[event.Signature], e.Data[event.Error]) @@ -90,37 +83,21 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { 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 { + if _, exists := the.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 { the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false) + uiState.AddContact(e.Data[event.RemotePeer]) } } // 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]), - "", - int(cxnState), - false, - false, - false, - }) - } + uiState.UpdateContactStatus(contact.Onion, int(cxnState), false) + } case event.ServerStateChange: serverOnion := e.Data[event.GroupServer] @@ -128,15 +105,13 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { 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) + group.State = e.Data[event.ConnectionState] + loading := false if state == connections.AUTHENTICATED { - uiState.GetContact(group.GroupID).Loading = true - } else { - uiState.GetContact(group.GroupID).Loading = false + loading = true } - uiState.UpdateContact(group.GroupID) + uiState.UpdateContactStatus(group.GroupID, int(state), loading) } else { log.Errorf("found group that is nil :/") } diff --git a/go/constants/attributes.go b/go/constants/attributes.go index a95dc873..10065d64 100644 --- a/go/constants/attributes.go +++ b/go/constants/attributes.go @@ -2,3 +2,4 @@ package constants const Nick = "nick" const LastRead = "last-read" +const Picture = "picture" diff --git a/go/cwutil/utils.go b/go/cwutil/utils.go index 53bcf1d2..e3f6490a 100644 --- a/go/cwutil/utils.go +++ b/go/cwutil/utils.go @@ -3,7 +3,6 @@ package cwutil 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 "qrc:/qml/images/servers/" + choices[int(barr[0])%len(choices)] + ".png" diff --git a/go/gobjects/contact.go b/go/gobjects/contact.go deleted file mode 100644 index 75807d56..00000000 --- a/go/gobjects/contact.go +++ /dev/null @@ -1,12 +0,0 @@ -package gobjects - -type Contact struct { - Handle string - DisplayName string - Image string - Server string - Status int - Trusted bool - Blocked bool - Loading bool -} diff --git a/go/gothings/gcd.go b/go/gothings/gcd.go index 0caf4499..46169e35 100644 --- a/go/gothings/gcd.go +++ b/go/gothings/gcd.go @@ -2,6 +2,7 @@ package gothings import ( "cwtch.im/cwtch/event" + "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" "cwtch.im/ui/go/cwutil" "github.com/therecipe/qt/qml" @@ -30,11 +31,13 @@ type GrandCentralDispatcher struct { _ string `property:"buildDate"` // 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, status int, trusted bool, blocked bool, loading bool) `signal:"UpdateContact"` - _ func(handle string) `signal:"IncContactUnreadCount"` - _ 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) `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"` @@ -94,16 +97,13 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { return } - if len(this.CurrentOpenConversation()) == 32 { // SEND TO GROUP + if isGroup(this.CurrentOpenConversation()) { if !the.Peer.GetGroup(this.CurrentOpenConversation()).Accepted { err := the.Peer.AcceptInvite(this.CurrentOpenConversation()) 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 @@ -127,13 +127,6 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { 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() mID = the.Peer.SendMessageToPeer(to, message) @@ -163,28 +156,20 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { } this.ClearMessages() this.SetCurrentOpenConversation(handle) - c := this.UIState.GetContact(handle) - if c == nil { - this.UIState.AddContact(&gobjects.Contact{ - handle, - handle, - cwutil.RandomProfileImage(handle), - "", - 0, - false, - false, - false, - }) - } else { - 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(constants.Nick) - updateLastReadTime(group) + updateLastReadTime(group.GroupID) if nick == "" { this.SetToolbarTitle(handle) } else { @@ -226,6 +211,9 @@ 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(constants.Nick) @@ -233,7 +221,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { nick = handle } } - updateLastReadTime(contact) + updateLastReadTime(contact.Onion) this.SetToolbarTitle(nick) peer := the.Peer.GetContact(handle) @@ -286,11 +274,7 @@ 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() { @@ -312,23 +296,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(constants.Nick, nick) - the.EventBus.Publish(event.NewEvent(event.SetPeerAttribute, map[event.Field]string{ - event.RemotePeer: onion, - event.Key: constants.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) { @@ -357,24 +326,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(constants.Nick, nick) - the.EventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{ - event.GroupID: groupID, - event.Key: constants.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) { @@ -446,12 +399,7 @@ func (this *GrandCentralDispatcher) importString(str string) { return //TODO: bring them to the duplicate } - this.UIState.AddContact(&gobjects.Contact{ - Handle: onion, - DisplayName: name, - Image: cwutil.RandomProfileImage(onion), - Trusted: true, - }) + this.UIState.AddContact(onion) } func (this *GrandCentralDispatcher) popup(str string) { @@ -472,21 +420,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.UIState.AddContact(groupID) - group := the.Peer.GetGroup(groupID) - group.SetAttribute(constants.Nick, groupName) - the.EventBus.Publish(event.NewEvent(event.SetGroupAttribute, map[event.Field]string{ - event.GroupID: groupID, - event.Key: constants.Nick, - event.Data: groupName, - })) + the.Peer.SetGroupAttribute(groupID, constants.Nick, groupName) the.Peer.JoinServer(server) } @@ -496,7 +432,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) { @@ -505,7 +441,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) { @@ -528,54 +464,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.UIState.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) diff --git a/go/gothings/uistate.go b/go/gothings/uistate.go index 14dae73d..745b1952 100644 --- a/go/gothings/uistate.go +++ b/go/gothings/uistate.go @@ -2,41 +2,68 @@ package gothings import ( "cwtch.im/cwtch/model" + "cwtch.im/cwtch/protocol/connections" "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 Attributable interface { - GetAttribute(string) (string, bool) - SetAttribute(string, string) +func isGroup(id string) bool { + return len(id) == 32 +} + +func isPeer(id string) bool { + return len(id) == 56 +} + +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(attr Attributable) time.Time { - lastRead := time.Now() - lastReadVal, ok := attr.GetAttribute(constants.LastRead) - if ok { - err := lastRead.UnmarshalText([]byte(lastReadVal)) - if err != nil { - lastRead = time.Now() - } - } else { - lastReadVal, _ := lastRead.MarshalText() - attr.SetAttribute(constants.LastRead, string(lastReadVal)) - } +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 updateLastReadTime(attr Attributable) { - lastRead := time.Now() - lastReadVal, _ := lastRead.MarshalText() - attr.SetAttribute(constants.LastRead, string(lastReadVal)) +func initProfilePicture(id string) string { + log.Infof("initProfilePic: %v\n", id) + if isGroup(id) { + return getWithSetDefault(id, constants.Picture, cwutil.RandomGroupImage(id)) + } else { + return getWithSetDefault(id, constants.Picture, cwutil.RandomProfileImage(id)) + } +} + +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 { @@ -53,11 +80,10 @@ func countUnread(messages []model.Message, lastRead time.Time) int { type InterfaceState struct { parentGcd *GrandCentralDispatcher - contacts sync.Map // string : *gobjects.Contact } func NewUIState(gcd *GrandCentralDispatcher) (uis InterfaceState) { - uis = InterfaceState{gcd, sync.Map{}} + uis = InterfaceState{gcd} return } @@ -65,96 +91,39 @@ func (this *InterfaceState) Acknowledge(mID string) { this.parentGcd.Acknowledged(mID) } -func (this *InterfaceState) AddContact(c *gobjects.Contact) { - if len(c.Handle) == 32 { // ADD GROUP - unread := 0 - group := the.Peer.GetGroup(c.Handle) +func (this *InterfaceState) AddContact(Handle string) { + if isGroup(Handle) { + group := the.Peer.GetGroup(Handle) if group != nil { - lastRead := initLastReadTime(group) - unread = countUnread(group.Timeline.GetMessages(), lastRead) - } - if _, found := this.contacts.Load(c.Handle); !found { - this.parentGcd.AddContact(c.Handle, c.DisplayName, c.Image, c.Server, unread, c.Status, c.Trusted, c.Blocked, c.Loading) - this.contacts.Store(c.Handle, c) + lastRead := initLastReadTime(group.GroupID) + unread := countUnread(group.Timeline.GetMessages(), lastRead) + picture := initProfilePicture(Handle) + nick, exists := group.GetAttribute(constants.Nick) + if !exists { + nick = Handle + } + + this.parentGcd.AddContact(Handle, nick, picture, group.GroupServer, unread, int(connections.ConnectionStateToType[group.State]), false, false) } return - } else if len(c.Handle) != 56 { - log.Errorf("sorry, unable to handle AddContact(%v)", c.Handle) + } else if !isPeer(Handle) { + log.Errorf("sorry, unable to handle AddContact(%v)", Handle) debug.PrintStack() return } - unread := 0 - contact := the.Peer.GetContact(c.Handle) + contact := the.Peer.GetContact(Handle) if contact != nil { - lastRead := initLastReadTime(contact) - unread = countUnread(contact.Timeline.GetMessages(), lastRead) - } - 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, unread, 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) + lastRead := initLastReadTime(contact.Onion) + unread := countUnread(contact.Timeline.GetMessages(), lastRead) + picture := initProfilePicture(Handle) + nick, exists := contact.GetAttribute(constants.Nick) + if !exists { + nick = 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(constants.Nick) - if !exists { - nick = group.GroupID[:12] - } - this.AddContact(&gobjects.Contact{ - handle, - nick, - cwutil.RandomGroupImage(handle), - group.GroupServer, - 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, - false, - contact.Blocked, - false, - }) - } else if contact == nil { - //log.Errorf("Attempting to add non existent contact to ui %v", handle) - } - } + this.parentGcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false) } - - 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) { @@ -165,11 +134,7 @@ func (this *InterfaceState) AddSendMessageError(peer string, signature string, e } func (this *InterfaceState) AddMessage(m *gobjects.Message) { - if len(m.Handle) == 32 { // GROUP - updateLastReadTime(the.Peer.GetGroup(m.Handle)) - } else { - updateLastReadTime(the.Peer.GetContact(m.Handle)) - } + updateLastReadTime(m.Handle) // If we have this group loaded already if this.parentGcd.CurrentOpenConversation() == m.Handle { @@ -184,17 +149,12 @@ func (this *InterfaceState) AddMessage(m *gobjects.Message) { } } -func (this *InterfaceState) UpdateContact(handle string) { - contact := the.Peer.GetContact(handle) - cif, found := this.contacts.Load(handle) - if found { - c := cif.(*gobjects.Contact) - if contact != nil { - c.Blocked = contact.Blocked - } - this.parentGcd.UpdateContact(c.Handle, c.DisplayName, c.Image, c.Server, c.Status, c.Trusted, c.Blocked, c.Loading) +func (this *InterfaceState) UpdateContactDisplayName(handle string, name string) { + this.parentGcd.UpdateContactDisplayName(handle, name) +} - } +func (this *InterfaceState) UpdateContactStatus(handle string, status int, loading bool) { + this.parentGcd.UpdateContactStatus(handle, status, loading) } func (this *InterfaceState) UpdateContactAttribute(handle, key, value string) { diff --git a/qml/overlays/BulletinOverlay.qml b/qml/overlays/BulletinOverlay.qml index c8abe9a6..f914acb6 100644 --- a/qml/overlays/BulletinOverlay.qml +++ b/qml/overlays/BulletinOverlay.qml @@ -87,12 +87,12 @@ 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, _status, _trusted, _blocked, _loading) { + onUpdateContactStatus: function(_handle, _status, _loading) { if (gcd.currentOpenConversation == _handle) { if (_loading == true) { newposttitle.enabled = false diff --git a/qml/overlays/ChatOverlay.qml b/qml/overlays/ChatOverlay.qml index 59dfefa4..b2c6d9ae 100644 --- a/qml/overlays/ChatOverlay.qml +++ b/qml/overlays/ChatOverlay.qml @@ -110,10 +110,10 @@ ColumnLayout { messagesListView.positionViewAtEnd() } - onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { + onUpdateContactStatus: function(_handle, _status, _loading) { if (gcd.currentOpenConversation == _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 { diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 1515a4f1..6f983722 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -87,16 +87,16 @@ 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, _status, _trusted, _blocked, _loading) { + onUpdateContactStatus: function(_handle, _status, _loading) { if (gcd.currentOpenConversation == _handle) { if (_loading == true) { newposttitle.enabled = false diff --git a/qml/overlays/MembershipOverlay.qml b/qml/overlays/MembershipOverlay.qml index d35caef8..b9297717 100644 --- a/qml/overlays/MembershipOverlay.qml +++ b/qml/overlays/MembershipOverlay.qml @@ -105,7 +105,6 @@ ColumnLayout { handle: _handle displayName: _displayName image: _image - trusted: true blocked: false background: false } diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index d13fdd3d..c0b81efc 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -47,7 +47,7 @@ ColumnLayout { Connections { // ADD/REMOVE CONTACT ENTRIES target: gcd - onAddContact: function(handle, displayName, image, server, badge, status, trusted, blocked, loading) { + onAddContact: function(handle, displayName, image, server, badge, status, blocked, loading) { contactsModel.append({ "_handle": handle, "_displayName": displayName + (blocked ? " (blocked)" : "" ), @@ -55,7 +55,6 @@ ColumnLayout { "_server": server, "_badge": badge, "_status": status, - "_trusted": trusted, "_blocked": blocked, "_loading": loading, "_loading": loading @@ -86,7 +85,6 @@ ColumnLayout { server: _server badge: _badge status: _status - trusted: _trusted blocked: _blocked loading: _loading } diff --git a/qml/widgets/ContactPicture.qml b/qml/widgets/ContactPicture.qml index 5281cdb3..42971be5 100644 --- a/qml/widgets/ContactPicture.qml +++ b/qml/widgets/ContactPicture.qml @@ -68,7 +68,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 diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index a3f3f9e9..5f497ec5 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -22,7 +22,6 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY property int badge property bool isActive property bool isHover - property bool trusted property bool blocked property bool loading property alias status: imgProfile.status @@ -52,7 +51,6 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY 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 @@ -146,17 +144,24 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY isActive = false } - onUpdateContact: function(_handle, _displayName, _image, _server, _status, _trusted, _blocked, _loading) { - if (handle == _handle) { - displayName = _displayName + (_blocked == true ? " (blocked)" : "") - image = _image - server = _server - 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) { From 0964363b89a69280c5cb051f5fdca1eebe9d75c4 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 5 Nov 2019 14:12:40 -0800 Subject: [PATCH 11/28] fixes for adding/imorting contacts; remove unused 'letter' class --- go/characters/incominglistener.go | 4 ++++ go/gobjects/letter.go | 7 ------- go/gothings/gcd.go | 19 +++++++++++++++---- main.go | 2 -- qml/widgets/ContactList.qml | 6 ++++++ qml/widgets/Message.qml | 1 + 6 files changed, 26 insertions(+), 13 deletions(-) delete mode 100644 go/gobjects/letter.go diff --git a/go/characters/incominglistener.go b/go/characters/incominglistener.go index b45a97be..ce183c2a 100644 --- a/go/characters/incominglistener.go +++ b/go/characters/incominglistener.go @@ -22,6 +22,7 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { the.EventBus.Subscribe(event.SendMessageToPeerError, q) the.EventBus.Subscribe(event.ServerStateChange, q) the.EventBus.Subscribe(event.PeerStateChange, q) + the.EventBus.Subscribe(event.PeerCreated, q) subscribed <- true for { @@ -75,6 +76,9 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { if err == nil && group != nil { uiState.AddContact(gid) } + case event.PeerCreated: + onion := e.Data[event.RemotePeer] + uiState.AddContact(onion) case event.SendMessageToGroupError: uiState.AddSendMessageError(e.Data[event.GroupServer], e.Data[event.Signature], e.Data[event.Error]) case event.SendMessageToPeerError: diff --git a/go/gobjects/letter.go b/go/gobjects/letter.go deleted file mode 100644 index fc35c26f..00000000 --- a/go/gobjects/letter.go +++ /dev/null @@ -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 -} diff --git a/go/gothings/gcd.go b/go/gothings/gcd.go index 46169e35..115abc64 100644 --- a/go/gothings/gcd.go +++ b/go/gothings/gcd.go @@ -19,10 +19,9 @@ import ( type GrandCentralDispatcher struct { core.QObject - OutgoingMessages chan gobjects.Letter - UIState InterfaceState - QMLEngine *qml.QQmlApplicationEngine - Translator *core.QTranslator + UIState InterfaceState + QMLEngine *qml.QQmlApplicationEngine + Translator *core.QTranslator _ string `property:"os"` _ string `property:"currentOpenConversation"` @@ -66,6 +65,7 @@ type GrandCentralDispatcher struct { _ 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"` @@ -339,6 +339,14 @@ func (this *GrandCentralDispatcher) broadcast(signal string) { } } +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") @@ -397,6 +405,9 @@ 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(onion) diff --git a/main.go b/main.go index 483e3e3a..2c7e3320 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ 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/the" @@ -139,7 +138,6 @@ func mainUi(flagLocal bool, flagClientUI bool) { 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) diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index c0b81efc..5b88d033 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -48,6 +48,12 @@ ColumnLayout { target: gcd onAddContact: function(handle, displayName, image, server, badge, status, blocked, loading) { + for (var i = 0; i < contactsModel.count; i++) { + if (contactsModel.get(i)["_handle"] == handle) { + return + } + } + contactsModel.append({ "_handle": handle, "_displayName": displayName + (blocked ? " (blocked)" : "" ), diff --git a/qml/widgets/Message.qml b/qml/widgets/Message.qml index d1f1ca86..bea69643 100644 --- a/qml/widgets/Message.qml +++ b/qml/widgets/Message.qml @@ -62,6 +62,7 @@ Item { onClicked: { + gcd.createContact(from) gcd.broadcast("ResetMessagePane") theStack.pane = theStack.messagePane gcd.loadMessagesPane(from) From d6eebe82bd326ab75b4c22b6d1826ddde754e550 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 6 Nov 2019 12:32:16 -0800 Subject: [PATCH 12/28] fix race condition in ui/gcd vs cwtch app start --- go/gothings/gcd.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/go/gothings/gcd.go b/go/gothings/gcd.go index 115abc64..85085a74 100644 --- a/go/gothings/gcd.go +++ b/go/gothings/gcd.go @@ -506,7 +506,9 @@ func (this *GrandCentralDispatcher) setLocale(locale string) { func (this *GrandCentralDispatcher) onActivate() { log.Debugln("onActivate") - the.CwtchApp.QueryACNStatus() + if the.CwtchApp != nil { + the.CwtchApp.QueryACNStatus() + } } func (this *GrandCentralDispatcher) SetLocale_helper(locale string) { From 35fa0adb67b637c5a165a4888ea60a63feb264e3 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 8 Nov 2019 13:34:12 -0800 Subject: [PATCH 13/28] rename packages to better align with their purposes now --- go/gobjects/message.go | 16 --- .../appHandler.go} | 20 ++-- .../peerHandler.go} | 61 +++--------- go/{gothings => ui}/android/CwtchActivity.go | 0 go/{gothings => ui}/gcd.go | 71 +++----------- go/{gothings/uistate.go => ui/manager.go} | 97 +++++++++++++------ go/{cwutil => ui}/utils.go | 2 +- main.go | 16 +-- 8 files changed, 117 insertions(+), 166 deletions(-) delete mode 100644 go/gobjects/message.go rename go/{characters/appEventListener.go => handlers/appHandler.go} (86%) rename go/{characters/incominglistener.go => handlers/peerHandler.go} (60%) rename go/{gothings => ui}/android/CwtchActivity.go (100%) rename go/{gothings => ui}/gcd.go (92%) rename go/{gothings/uistate.go => ui/manager.go} (50%) rename go/{cwutil => ui}/utils.go (99%) diff --git a/go/gobjects/message.go b/go/gobjects/message.go deleted file mode 100644 index 01284ffb..00000000 --- a/go/gobjects/message.go +++ /dev/null @@ -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 -} diff --git a/go/characters/appEventListener.go b/go/handlers/appHandler.go similarity index 86% rename from go/characters/appEventListener.go rename to go/handlers/appHandler.go index b87abe17..6b2ce139 100644 --- a/go/characters/appEventListener.go +++ b/go/handlers/appHandler.go @@ -1,18 +1,17 @@ -package characters +package handlers import ( "cwtch.im/cwtch/app/plugins" "cwtch.im/cwtch/event" "cwtch.im/ui/go/constants" - "cwtch.im/ui/go/cwutil" - "cwtch.im/ui/go/gothings" "cwtch.im/ui/go/the" + "cwtch.im/ui/go/ui" "git.openprivacy.ca/openprivacy/libricochet-go/log" "os" "strconv" ) -func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool) { +func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { q := event.NewQueue() the.AppBus.Subscribe(event.NewPeer, q) the.AppBus.Subscribe(event.PeerError, q) @@ -78,20 +77,25 @@ func AppEventListener(gcd *gothings.GrandCentralDispatcher, subscribed chan bool the.EventBus = the.CwtchApp.GetEventBus(onion) incSubscribed := make(chan bool) - go IncomingListener(&gcd.UIState, incSubscribed) + go PeerHandler(&gcd.UIManager, incSubscribed) <-incSubscribed - gcd.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, cwutil.RandomProfileImage(the.Peer.GetProfile().Onion)) + pic, exists := the.Peer.GetAttribute(constants.Picture) + if !exists { + pic = ui.RandomProfileImage(the.Peer.GetProfile().Onion) + the.Peer.SetAttribute(constants.Picture, pic) + } + gcd.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, pic) contacts := the.Peer.GetContacts() for i := range contacts { - gcd.UIState.AddContact(contacts[i]) + gcd.UIManager.AddContact(contacts[i]) } groups := the.Peer.GetGroups() for i := range groups { // Only join servers for active and explicitly accepted groups. - gcd.UIState.AddContact(groups[i]) + gcd.UIManager.AddContact(groups[i]) } if e.Data[event.Status] != "running" { diff --git a/go/characters/incominglistener.go b/go/handlers/peerHandler.go similarity index 60% rename from go/characters/incominglistener.go rename to go/handlers/peerHandler.go index ce183c2a..ad0a70a9 100644 --- a/go/characters/incominglistener.go +++ b/go/handlers/peerHandler.go @@ -1,18 +1,15 @@ -package characters +package handlers import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/protocol/connections" - "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" + "cwtch.im/ui/go/ui" "git.openprivacy.ca/openprivacy/libricochet-go/log" "time" ) -func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { +func PeerHandler(uiManager *ui.Manager, subscribed chan bool) { q := event.NewQueue() the.EventBus.Subscribe(event.NewMessageFromPeer, q) the.EventBus.Subscribe(event.PeerAcknowledgement, q) @@ -31,76 +28,48 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { 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, - }) + uiManager.AddMessage(e.Data[event.RemotePeer], e.Data[event.RemotePeer], e.Data[event.Data], false, e.EventID, ts, true) if the.Peer.GetContact(e.Data[event.RemotePeer]) == nil { the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false) } case event.PeerAcknowledgement: - uiState.Acknowledge(e.Data[event.EventID]) + uiManager.Acknowledge(e.Data[event.EventID]) 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(constants.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, - }) + uiManager.AddMessage(e.Data[event.GroupID], e.Data[event.RemotePeer], e.Data[event.Data], e.Data[event.RemotePeer] == the.Peer.GetProfile().Onion, e.Data[event.Signature], ts, true) 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(gid) + uiManager.AddContact(gid) } case event.PeerCreated: onion := e.Data[event.RemotePeer] - uiState.AddContact(onion) + uiManager.AddContact(onion) case event.SendMessageToGroupError: - uiState.AddSendMessageError(e.Data[event.GroupServer], e.Data[event.Signature], e.Data[event.Error]) + uiManager.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.EventID], e.Data[event.Error]) + 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.Peer it's new. Only add once Authed + // if it's not in the.PeerHandler it's new. Only add once Authed if _, exists := the.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 { the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false) - uiState.AddContact(e.Data[event.RemotePeer]) + uiManager.AddContact(e.Data[event.RemotePeer]) } } - // if it's in the.Peer its either existing and needs an update or not in the UI and needs to be added + // 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 := the.Peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; exists { contact.State = e.Data[event.ConnectionState] - uiState.UpdateContactStatus(contact.Onion, int(cxnState), false) + uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false) } case event.ServerStateChange: @@ -115,7 +84,7 @@ func IncomingListener(uiState *gothings.InterfaceState, subscribed chan bool) { if state == connections.AUTHENTICATED { loading = true } - uiState.UpdateContactStatus(group.GroupID, int(state), loading) + uiManager.UpdateContactStatus(group.GroupID, int(state), loading) } else { log.Errorf("found group that is nil :/") } diff --git a/go/gothings/android/CwtchActivity.go b/go/ui/android/CwtchActivity.go similarity index 100% rename from go/gothings/android/CwtchActivity.go rename to go/ui/android/CwtchActivity.go diff --git a/go/gothings/gcd.go b/go/ui/gcd.go similarity index 92% rename from go/gothings/gcd.go rename to go/ui/gcd.go index 85085a74..af42c749 100644 --- a/go/gothings/gcd.go +++ b/go/ui/gcd.go @@ -1,13 +1,11 @@ -package gothings +package ui import ( "cwtch.im/cwtch/event" "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" - "cwtch.im/ui/go/cwutil" "github.com/therecipe/qt/qml" - "cwtch.im/ui/go/gobjects" "cwtch.im/ui/go/the" "encoding/base32" "git.openprivacy.ca/openprivacy/libricochet-go/log" @@ -19,7 +17,7 @@ import ( type GrandCentralDispatcher struct { core.QObject - UIState InterfaceState + UIManager Manager QMLEngine *qml.QQmlApplicationEngine Translator *core.QTranslator @@ -109,18 +107,7 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { var err error mID, err = the.Peer.SendMessageToGroupTracked(this.CurrentOpenConversation(), message) - this.UIState.AddMessage(&gobjects.Message{ - this.CurrentOpenConversation(), - "me", - "", - message, - "", - true, - mID, - time.Now(), - false, - false, - }) + this.UIManager.AddMessage(this.CurrentOpenConversation(), "me", message, true, mID, time.Now(), false) if err != nil { this.InvokePopup("failed to send message " + err.Error()) @@ -130,18 +117,7 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { to := this.CurrentOpenConversation() mID = the.Peer.SendMessageToPeer(to, message) - this.UIState.AddMessage(&gobjects.Message{ - to, - "me", - "", - message, - "", - true, - mID, - time.Now(), - false, - false, - }) + this.UIManager.AddMessage(to, "me", message, true, mID, time.Now(), false) } } @@ -182,24 +158,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(constants.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), @@ -233,24 +201,15 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { from = "me" } - var displayname string - ctc := the.Peer.GetContact(messages[i].PeerID) - if ctc != nil { - var exists bool - displayname, exists = ctc.GetAttribute(constants.Nick) - if !exists || displayname == "" { - displayname = messages[i].PeerID - } - } else { - displayname = messages[i].PeerID - } + displayname := getOrDefault(messages[i].PeerID, constants.Nick, messages[i].PeerID) + image := getProfilePic(messages[i].PeerID) this.AppendMessage( from, messages[i].PeerID, displayname, messages[i].Message, - cwutil.RandomProfileImage(handle), + image, string(messages[i].Signature), fromMe, messages[i].Timestamp.Format(constants.TIME_FORMAT), @@ -397,7 +356,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 } @@ -410,7 +369,7 @@ func (this *GrandCentralDispatcher) importString(str string) { the.Peer.PeerWithOnion(onion) } - this.UIState.AddContact(onion) + this.UIManager.AddContact(onion) } func (this *GrandCentralDispatcher) popup(str string) { @@ -431,7 +390,7 @@ func (this *GrandCentralDispatcher) createGroup(server, groupName string) { return } - this.UIState.AddContact(groupID) + this.UIManager.AddContact(groupID) the.Peer.SetGroupAttribute(groupID, constants.Nick, groupName) @@ -480,7 +439,7 @@ func (this *GrandCentralDispatcher) acceptGroup(groupID string) { func (this *GrandCentralDispatcher) setAttribute(onion, key, value string) { the.Peer.SetContactAttribute(onion, key, value) - this.UIState.UpdateContactAttribute(onion, key, value) + this.UIManager.UpdateContactAttribute(onion, key, value) } func (this *GrandCentralDispatcher) blockUnknownPeers() { diff --git a/go/gothings/uistate.go b/go/ui/manager.go similarity index 50% rename from go/gothings/uistate.go rename to go/ui/manager.go index 745b1952..bf4ee769 100644 --- a/go/gothings/uistate.go +++ b/go/ui/manager.go @@ -1,11 +1,9 @@ -package gothings +package ui import ( "cwtch.im/cwtch/model" "cwtch.im/cwtch/protocol/connections" "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" @@ -20,6 +18,21 @@ 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 @@ -49,11 +62,27 @@ func initLastReadTime(id string) time.Time { } func initProfilePicture(id string) string { - log.Infof("initProfilePic: %v\n", id) if isGroup(id) { - return getWithSetDefault(id, constants.Picture, cwutil.RandomGroupImage(id)) + return getWithSetDefault(id, constants.Picture, RandomGroupImage(id)) } else { - return getWithSetDefault(id, constants.Picture, cwutil.RandomProfileImage(id)) + 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 + } } } @@ -78,20 +107,19 @@ func countUnread(messages []model.Message, lastRead time.Time) int { return count } -type InterfaceState struct { - parentGcd *GrandCentralDispatcher +type Manager struct { + gcd *GrandCentralDispatcher } -func NewUIState(gcd *GrandCentralDispatcher) (uis InterfaceState) { - uis = InterfaceState{gcd} - return +func NewManager(gcd *GrandCentralDispatcher) Manager { + return Manager{gcd} } -func (this *InterfaceState) Acknowledge(mID string) { - this.parentGcd.Acknowledged(mID) +func (this *Manager) Acknowledge(mID string) { + this.gcd.Acknowledged(mID) } -func (this *InterfaceState) AddContact(Handle string) { +func (this *Manager) AddContact(Handle string) { if isGroup(Handle) { group := the.Peer.GetGroup(Handle) if group != nil { @@ -103,7 +131,7 @@ func (this *InterfaceState) AddContact(Handle string) { nick = Handle } - this.parentGcd.AddContact(Handle, nick, picture, group.GroupServer, unread, int(connections.ConnectionStateToType[group.State]), false, false) + this.gcd.AddContact(Handle, nick, picture, group.GroupServer, unread, int(connections.ConnectionStateToType[group.State]), false, false) } return } else if !isPeer(Handle) { @@ -122,41 +150,48 @@ func (this *InterfaceState) AddContact(Handle string) { nick = Handle } - this.parentGcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false) + this.gcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false) } } -func (this *InterfaceState) AddSendMessageError(peer string, signature string, err string) { +func (this *Manager) AddSendMessageError(peer string, signature string, err string) { 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) + this.gcd.GroupSendError(signature, err) } -func (this *InterfaceState) AddMessage(m *gobjects.Message) { - updateLastReadTime(m.Handle) +func (this *Manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) { + updateLastReadTime(handle) + + nick := getOrDefault(handle, constants.Nick, handle) + image := getProfilePic(handle) // If we have this group loaded already - if this.parentGcd.CurrentOpenConversation() == m.Handle { + if this.gcd.CurrentOpenConversation() == 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) + if !fromMe { + this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Format(constants.TIME_FORMAT), false, false) } else { - this.parentGcd.Acknowledged(m.MessageID) + if !Acknowledged { + this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Format(constants.TIME_FORMAT), false, false) + } else { + this.gcd.Acknowledged(messageID) + } } } else { - this.parentGcd.IncContactUnreadCount(m.Handle) + this.gcd.IncContactUnreadCount(handle) } } -func (this *InterfaceState) UpdateContactDisplayName(handle string, name string) { - this.parentGcd.UpdateContactDisplayName(handle, name) +func (this *Manager) UpdateContactDisplayName(handle string, name string) { + this.gcd.UpdateContactDisplayName(handle, name) } -func (this *InterfaceState) UpdateContactStatus(handle string, status int, loading bool) { - this.parentGcd.UpdateContactStatus(handle, status, loading) +func (this *Manager) UpdateContactStatus(handle string, status int, loading bool) { + this.gcd.UpdateContactStatus(handle, status, loading) } -func (this *InterfaceState) UpdateContactAttribute(handle, key, value string) { - this.parentGcd.UpdateContactAttribute(handle, key, value) +func (this *Manager) UpdateContactAttribute(handle, key, value string) { + this.gcd.UpdateContactAttribute(handle, key, value) } diff --git a/go/cwutil/utils.go b/go/ui/utils.go similarity index 99% rename from go/cwutil/utils.go rename to go/ui/utils.go index e3f6490a..6b382d4b 100644 --- a/go/cwutil/utils.go +++ b/go/ui/utils.go @@ -1,4 +1,4 @@ -package cwutil +package ui import ( "encoding/base32" diff --git a/main.go b/main.go index 2c7e3320..1b514f52 100644 --- a/main.go +++ b/main.go @@ -3,10 +3,10 @@ package main import ( libapp "cwtch.im/cwtch/app" "cwtch.im/cwtch/event/bridge" - "cwtch.im/ui/go/characters" - "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" @@ -32,7 +32,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() { @@ -128,7 +128,7 @@ func mainUi(flagLocal bool, flagClientUI bool) { app := gui.NewQGuiApplication(len(os.Args), os.Args) // our globals - gcd := gothings.NewGrandCentralDispatcher(nil) + gcd := ui.NewGrandCentralDispatcher(nil) gcd.SetOs(runtime.GOOS) if buildVer != "" { gcd.SetVersion(buildVer) @@ -137,7 +137,7 @@ func mainUi(flagLocal bool, flagClientUI bool) { gcd.SetVersion("development") gcd.SetBuildDate("now") } - gcd.UIState = gothings.NewUIState(gcd) + gcd.UIManager = ui.NewManager(gcd) //TODO: put theme stuff somewhere better gcd.SetThemeScale(1.0) @@ -232,7 +232,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 +255,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) <-subscribed } From 6dd346fe96806f5d905f38f1506fbea3c05119dd Mon Sep 17 00:00:00 2001 From: Sarah Jamie Lewis Date: Wed, 6 Nov 2019 10:48:57 -0800 Subject: [PATCH 14/28] Alert the user when there is no internet --- go.mod | 7 ++++--- go.sum | 22 ++++++++++++++++++++++ go/handlers/appHandler.go | 25 ++++++++++++++++++++++++- go/handlers/peerHandler.go | 14 ++++++++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index a3e05401..8e577562 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,13 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.3.3 - git.openprivacy.ca/openprivacy/libricochet-go v1.0.6 + cwtch.im/cwtch v0.3.6 + 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-20191002095216-73192f6811d0 + github.com/therecipe/qt v0.0.0-20191101232336-18864661ae4f + github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20191101232336-18864661ae4f // indirect github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 // indirect golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 // indirect golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect diff --git a/go.sum b/go.sum index ead507ab..fd141d81 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,12 @@ cwtch.im/cwtch v0.3.2 h1:JxoauToMckHjmQz3QCmI7XG9pun1tF3pV/o5ziuqV1A= cwtch.im/cwtch v0.3.2/go.mod h1:4b2qGW5bZKm4CwYxqc0+4pgpDU0LjjyoihC8a/ezOoQ= cwtch.im/cwtch v0.3.3 h1:mAypnkTCehej5ebSEzl43nPufsyyXLNz/dw2RWOO+Wk= cwtch.im/cwtch v0.3.3/go.mod h1:I95rbE3aK8uic7LsMOB1lfJDSzlNsRUP0/5cFCLkD0Y= +cwtch.im/cwtch v0.3.4 h1:EZzDE5kBvkWsNrlPg4+uQ0LKp5AMLfXr7cqXFUgnCc4= +cwtch.im/cwtch v0.3.4/go.mod h1:I95rbE3aK8uic7LsMOB1lfJDSzlNsRUP0/5cFCLkD0Y= +cwtch.im/cwtch v0.3.5 h1:L4hBYMRUSADodBz3gwIrYGXZf2V7HzTlzLUku9N3O9A= +cwtch.im/cwtch v0.3.5/go.mod h1:I95rbE3aK8uic7LsMOB1lfJDSzlNsRUP0/5cFCLkD0Y= +cwtch.im/cwtch v0.3.6 h1:a+BhoTpdlH5yI335HQDB7EEWZJqvNdoZnnjYLYne52Y= +cwtch.im/cwtch v0.3.6/go.mod h1:NU6Pm4RgHm97b03U83W7/NabmRvBxHQMxM8/kZnH12Y= cwtch.im/tapir v0.1.10 h1:V+TkmwXNd6gySZqlVw468wMYEkmDwMSyvhkkpOfUw7w= cwtch.im/tapir v0.1.10/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= cwtch.im/tapir v0.1.11 h1:JLm1MIYq4VXKzhj68+P8OuVPllAU9U6G0DtUor2fbc4= @@ -13,6 +19,11 @@ cwtch.im/tapir v0.1.11/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= 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.7 h1:yOD87eeuIFVY/2dsYzduSotE2eYOVd2pks4i7dKw+Ng= +git.openprivacy.ca/openprivacy/libricochet-go v1.0.7/go.mod h1:6I+vO9Aagv3/yUWv+e7A57H8tgXgR67FCjfSj9Pp970= +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/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= 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= @@ -22,6 +33,8 @@ github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmv github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -43,7 +56,10 @@ github.com/mattn/go-tty v0.0.0-20190424173100-523744f04859/go.mod h1:XPvLUNfbS4f 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= +github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= +github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -56,6 +72,10 @@ github.com/therecipe/qt v0.0.0-20190824160953-615e084bab56 h1:CAFR/rHptsl8gEP6ig github.com/therecipe/qt v0.0.0-20190824160953-615e084bab56/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us= github.com/therecipe/qt v0.0.0-20191002095216-73192f6811d0 h1:Y4I5RGVEZubcplYny6zwqg0G7WUC6gw+esy+2cPCruA= github.com/therecipe/qt v0.0.0-20191002095216-73192f6811d0/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us= +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.12.0 v0.0.0-20191101232336-18864661ae4f h1:035UB1g+zTNL+3BAKLLy8XwYzBL0dM+HwuDELYOoRoY= +github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20191101232336-18864661ae4f/go.mod h1:7m8PDYDEtEVqfjoUQc2UrFqhG0CDmoVJjRlQxexndFc= github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 h1:SC92QLlGwzwrT3Xi5YzKvtVXFIriPP0Ui1AOIZgDyh8= github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0/go.mod h1:mH55Ek7AZcdns5KPp99O0bg+78el64YCYWHiQKrOdt4= golang.org/x/crypto v0.0.0-20190128193316-c7b33c32a30b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -87,7 +107,9 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191031220737-6d8f1af9ccc0 h1:+o3suEKE/4hCUt6qjV8SDcVZhz2dO8UWlHliCa+4bvg= golang.org/x/tools v0.0.0-20191031220737-6d8f1af9ccc0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191108175616-46f5a7f28bf0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= diff --git a/go/handlers/appHandler.go b/go/handlers/appHandler.go index 6b2ce139..50f2cd5e 100644 --- a/go/handlers/appHandler.go +++ b/go/handlers/appHandler.go @@ -9,6 +9,7 @@ import ( "git.openprivacy.ca/openprivacy/libricochet-go/log" "os" "strconv" + "time" ) func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { @@ -17,13 +18,33 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { 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) + 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) @@ -34,7 +55,7 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { message = "Connecting to network" } else if percent < 100 { statuscode = 2 - message = "Establishng Tor circut" + message = "Establishing Tor circuit" } else if percent == 100 { statuscode = 3 message = "tor appears to be running just fine!" @@ -44,6 +65,7 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { } gcd.TorStatus(statuscode, message) + case event.PeerError: // current only case log.Errorf("couldn't load profiles: %v", e.Data[event.Error]) @@ -72,6 +94,7 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { onion := e.Data[event.Identity] the.CwtchApp.AddPeerPlugin(onion, plugins.CONNECTIONRETRY) + the.CwtchApp.AddPeerPlugin(onion, plugins.NETWORKCHECK) the.Peer = the.CwtchApp.GetPeer(onion) the.EventBus = the.CwtchApp.GetEventBus(onion) diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index ad0a70a9..497f28f7 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -20,12 +20,26 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) { the.EventBus.Subscribe(event.ServerStateChange, q) the.EventBus.Subscribe(event.PeerStateChange, q) the.EventBus.Subscribe(event.PeerCreated, q) + the.EventBus.Subscribe(event.NetworkStatus, 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) From 606f7c7eb672db0f1fd5f7f0531f0f01b5c8532a Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Thu, 14 Nov 2019 17:13:42 -0800 Subject: [PATCH 15/28] contactList: new messages make contact float to top --- go/ui/gcd.go | 14 +++++----- go/ui/manager.go | 12 ++++++-- qml/widgets/ContactList.qml | 55 +++++++++++++++++++++++++++++-------- 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/go/ui/gcd.go b/go/ui/gcd.go index af42c749..3ff20789 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -28,13 +28,13 @@ type GrandCentralDispatcher struct { _ string `property:"buildDate"` // contact list stuff - _ func(handle, displayName, image, server string, badge, status int, blocked bool, loading bool) `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"` + _ 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"` diff --git a/go/ui/manager.go b/go/ui/manager.go index bf4ee769..11423539 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -119,6 +119,14 @@ func (this *Manager) Acknowledge(mID string) { 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()) +} + func (this *Manager) AddContact(Handle string) { if isGroup(Handle) { group := the.Peer.GetGroup(Handle) @@ -131,7 +139,7 @@ func (this *Manager) AddContact(Handle string) { nick = Handle } - this.gcd.AddContact(Handle, nick, picture, group.GroupServer, unread, int(connections.ConnectionStateToType[group.State]), false, false) + this.gcd.AddContact(Handle, nick, picture, group.GroupServer, unread, int(connections.ConnectionStateToType[group.State]), false, false, getLastMessageTime(&group.Timeline)) } return } else if !isPeer(Handle) { @@ -150,7 +158,7 @@ func (this *Manager) AddContact(Handle string) { nick = Handle } - this.gcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false) + this.gcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false, getLastMessageTime(&contact.Timeline)) } } diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index 5b88d033..bcacf3be 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -47,24 +47,36 @@ ColumnLayout { Connections { // ADD/REMOVE CONTACT ENTRIES target: gcd - onAddContact: function(handle, displayName, image, server, badge, status, blocked, 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 } } - contactsModel.append({ - "_handle": handle, - "_displayName": displayName + (blocked ? " (blocked)" : "" ), - "_image": image, - "_server": server, - "_badge": badge, - "_status": status, - "_blocked": blocked, - "_loading": loading, - "_loading": loading - }) + 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) { @@ -74,6 +86,18 @@ ColumnLayout { contactsModel.remove(i) return } + } + } + + 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) + } } } } @@ -95,6 +119,13 @@ ColumnLayout { loading: _loading } } + + } } + + } + + + From bac4accb1b800db06714367137f71e613edd5efe Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 22 Oct 2019 11:55:16 -0700 Subject: [PATCH 16/28] profile manager: screen, unlock, select, back to Also fixing issues on switching profiles and maintaining ui state around contact ordering and unread count --- go/constants/attributes.go | 1 + go/handlers/appHandler.go | 74 +++++++++++-------------- go/handlers/peerHandler.go | 44 ++++++++------- go/ui/gcd.go | 44 +++++++++++++++ go/ui/manager.go | 27 +++++++-- i18n/translation_de.ts | 35 +++++++++--- i18n/translation_en.qm | Bin 4561 -> 4954 bytes i18n/translation_en.ts | 37 ++++++++++--- i18n/translation_fr.ts | 35 +++++++++--- i18n/translation_pt.ts | 35 +++++++++--- main.go | 2 +- qml.qrc | 1 + qml/main.qml | 24 +++++++- qml/panes/ProfileManagerPane.qml | 84 ++++++++++++++++++++++++++++ qml/widgets/ContactList.qml | 5 ++ qml/widgets/ContactRow.qml | 71 ++++++++++++++++-------- qml/widgets/MyProfile.qml | 16 +++++- qml/widgets/ProfileList.qml | 92 +++++++++++++++++++++++++++++++ 18 files changed, 505 insertions(+), 122 deletions(-) create mode 100644 qml/panes/ProfileManagerPane.qml create mode 100644 qml/widgets/ProfileList.qml diff --git a/go/constants/attributes.go b/go/constants/attributes.go index 10065d64..0868de16 100644 --- a/go/constants/attributes.go +++ b/go/constants/attributes.go @@ -3,3 +3,4 @@ package constants const Nick = "nick" const LastRead = "last-read" const Picture = "picture" +const DefaultPassword = "default-password" diff --git a/go/handlers/appHandler.go b/go/handlers/appHandler.go index 50f2cd5e..7baa03d6 100644 --- a/go/handlers/appHandler.go +++ b/go/handlers/appHandler.go @@ -12,7 +12,7 @@ import ( "time" ) -func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { +func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingFirst bool) { q := event.NewQueue() the.AppBus.Subscribe(event.NewPeer, q) the.AppBus.Subscribe(event.PeerError, q) @@ -21,9 +21,12 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { the.AppBus.Subscribe(event.NetworkStatus, q) the.AppBus.Subscribe(event.ReloadDone, q) subscribed <- true + loadingV1Accounts := !reloadingFirst networkOffline := false - timeSinceLastSuccess := time.Unix(0,0) + timeSinceLastSuccess := time.Unix(0, 0) + + gcd.Loaded() for { e := q.Next() @@ -74,65 +77,54 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) { 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") - if gcd.Version() == "development" { - the.CwtchApp.CreatePeer("tester", the.AppPassword) + + if loadingV1Accounts { + loadingV1Accounts = false + // TODO: only an error if other profiles are not loaded + if len(the.CwtchApp.ListPeers()) == 0 { + log.Infoln("couldn't load your config file. attempting to create one now") + if gcd.Version() == "development" { + the.CwtchApp.CreatePeer("tester", the.AppPassword) + } else { + the.CwtchApp.CreatePeer("alice", the.AppPassword) + } + } } else { - the.CwtchApp.CreatePeer("alice", the.AppPassword) + gcd.ErrorLoaded0() } } case event.ReloadDone: if the.Peer == nil { + loadingV1Accounts = true the.CwtchApp.LoadProfiles(the.AppPassword) } case event.NewPeer: - if the.Peer != nil { - continue - } onion := e.Data[event.Identity] + peer := the.CwtchApp.GetPeer(onion) + + if loadingV1Accounts { + the.CwtchApp.GetPeer(onion).SetAttribute(constants.DefaultPassword, "true") + loadingV1Accounts = false + } + + log.Infof("NewPeer for %v\n", onion) + gcd.UIManager.AddProfile(onion) the.CwtchApp.AddPeerPlugin(onion, plugins.CONNECTIONRETRY) the.CwtchApp.AddPeerPlugin(onion, plugins.NETWORKCHECK) - the.Peer = the.CwtchApp.GetPeer(onion) - the.EventBus = the.CwtchApp.GetEventBus(onion) - incSubscribed := make(chan bool) - go PeerHandler(&gcd.UIManager, incSubscribed) + go PeerHandler(onion, &gcd.UIManager, incSubscribed) <-incSubscribed - pic, exists := the.Peer.GetAttribute(constants.Picture) - if !exists { - pic = ui.RandomProfileImage(the.Peer.GetProfile().Onion) - the.Peer.SetAttribute(constants.Picture, pic) - } - gcd.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, pic) - - contacts := the.Peer.GetContacts() - for i := range contacts { - gcd.UIManager.AddContact(contacts[i]) - } - - groups := the.Peer.GetGroups() - for i := range groups { - // Only join servers for active and explicitly accepted groups. - gcd.UIManager.AddContact(groups[i]) - } - if e.Data[event.Status] != "running" { - the.CwtchApp.LaunchPeers() + peer.Listen() + peer.StartPeersConnections() + peer.StartGroupConnections() } - // 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) + blockUnkownPeers, exists := peer.GetProfile().GetAttribute(constants.BlockUnknownPeersSetting) if exists && blockUnkownPeers == "true" { the.EventBus.Publish(event.NewEvent(event.BlockUnknownPeers, map[event.Field]string{})) } diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index 497f28f7..a753db98 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -9,18 +9,20 @@ import ( "time" ) -func PeerHandler(uiManager *ui.Manager, subscribed chan bool) { +func PeerHandler(onion string, uiManager *ui.Manager, subscribed chan bool) { + peer := the.CwtchApp.GetPeer(onion) + eventBus := the.CwtchApp.GetEventBus(onion) 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) - the.EventBus.Subscribe(event.PeerCreated, q) - the.EventBus.Subscribe(event.NetworkStatus, q) + 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) subscribed <- true @@ -43,8 +45,8 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) { 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 the.Peer.GetContact(e.Data[event.RemotePeer]) == nil { - the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false) + if peer.GetContact(e.Data[event.RemotePeer]) == nil { + peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false) } case event.PeerAcknowledgement: @@ -52,10 +54,10 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) { 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] == the.Peer.GetProfile().Onion, e.Data[event.Signature], ts, true) + 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 := the.Peer.GetProfile().ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer]) - group := the.Peer.GetGroup(gid) + 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) } @@ -70,18 +72,18 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) { cxnState := connections.ConnectionStateToType[e.Data[event.ConnectionState]] // if it's not in the.PeerHandler it's new. Only add once Authed - if _, exists := the.Peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; !exists { + 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 { - the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false) + 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 := the.Peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; exists { + if contact, exists := peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; exists { contact.State = e.Data[event.ConnectionState] uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false) @@ -89,9 +91,9 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) { case event.ServerStateChange: serverOnion := e.Data[event.GroupServer] state := connections.ConnectionStateToType[e.Data[event.ConnectionState]] - groups := the.Peer.GetGroups() + groups := peer.GetGroups() for _, groupID := range groups { - group := the.Peer.GetGroup(groupID) + group := peer.GetGroup(groupID) if group != nil && group.GroupServer == serverOnion { group.State = e.Data[event.ConnectionState] loading := false diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 3ff20789..1c3c8e7b 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -27,6 +27,12 @@ type GrandCentralDispatcher struct { _ string `property:"version"` _ string `property:"buildDate"` + // profile management stuff + _ func() `signal:"Loaded"` + _ func(handle, displayname, image string) `signal:"AddProfile"` + _ func() `signal:"ErrorLoaded0"` + _ func() `signal:"ResetProfile"` + // contact list stuff _ func(handle, displayName, image, server string, badge, status int, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"` _ func(handle, displayName string) `signal:"UpdateContactDisplayName"` @@ -82,6 +88,8 @@ type GrandCentralDispatcher struct { _ func() `signal:"allowUnknownPeers,auto"` _ func() `signal:"blockUnknownPeers,auto"` _ func() `signal:"onActivate,auto"` + _ func(password string) `signal:"unlockProfiles,auto"` + _ func(handle string) `signal:"loadProfile,auto"` } func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { @@ -295,6 +303,8 @@ func (this *GrandCentralDispatcher) broadcast(signal string) { log.Debugf("unhandled broadcast signal: %v", signal) case "ResetMessagePane": this.ResetMessagePane() + case "ResetProfile": + this.ResetProfile() } } @@ -477,3 +487,37 @@ 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.UIManager.AddContact(contacts[i]) + } + + groups := the.Peer.GetGroups() + for i := range groups { + // Only join servers for active and explicitly accepted groups. + this.UIManager.AddContact(groups[i]) + } + + // load ui preferences + this.RequestSettings() + locale, exists := the.Peer.GetProfile().GetAttribute(constants.LocaleSetting) + if exists { + this.SetLocale_helper(locale) + } +} diff --git a/go/ui/manager.go b/go/ui/manager.go index 11423539..b2f0ad34 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -98,7 +98,7 @@ func updateLastReadTime(id string) { 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) { + if messages[i].Timestamp.After(lastRead) || messages[i].Timestamp.Equal(lastRead) { count++ } else { break @@ -115,6 +115,25 @@ func NewManager(gcd *GrandCentralDispatcher) Manager { return Manager{gcd} } +func (this *Manager) AddProfile(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) + } + log.Infof("AddProfile %v %v %v\n", handle, nick, pic) + this.gcd.AddProfile(handle, nick, pic) + } +} + func (this *Manager) Acknowledge(mID string) { this.gcd.Acknowledged(mID) } @@ -170,13 +189,12 @@ func (this *Manager) AddSendMessageError(peer string, signature string, err stri } func (this *Manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) { - updateLastReadTime(handle) - nick := getOrDefault(handle, constants.Nick, handle) image := getProfilePic(handle) // If we have this group loaded already if this.gcd.CurrentOpenConversation() == handle { + 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) @@ -187,9 +205,8 @@ func (this *Manager) AddMessage(handle string, from string, message string, from this.gcd.Acknowledged(messageID) } } - } else { - this.gcd.IncContactUnreadCount(handle) } + this.gcd.IncContactUnreadCount(handle) } func (this *Manager) UpdateContactDisplayName(handle string, name string) { diff --git a/i18n/translation_de.ts b/i18n/translation_de.ts index e7a778a9..5398cd7a 100644 --- a/i18n/translation_de.ts +++ b/i18n/translation_de.ts @@ -145,18 +145,18 @@ Klicken, um DM zu senden - + could-not-send-msg-error Could not send this message Nachricht konnte nicht gesendet werden - + acknowledged-label bestätigt - + pending-label Bestätigung ausstehend @@ -164,25 +164,25 @@ MyProfile - + copy-btn Button for copying profile onion address to clipboard Kopieren - + copied-clipboard-notification Copied to clipboard in die Zwischenablage kopiert - + new-group-btn create new group button Neue Gruppe anlegen - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." Adresse hier hinzufügen, um einen Kontakt aufzunehmen @@ -273,6 +273,27 @@ löschen + + ProfileManagerPane + + + enter-profile-password + Please enter password: + + + + + error-0-profiles-loaded-for-password + 0 profiles loaded with that password + + + + + unlock + Unlock + + + SettingsPane diff --git a/i18n/translation_en.qm b/i18n/translation_en.qm index 6ec2aa99957749a8126d16bce2bc9488f4c7423e..5149964b83b8349c7af5bf0d86c42bebe3b366e0 100644 GIT binary patch delta 803 zcma)2T}TvB6#iysc6QvE9e>!A#0Q}ek_Hs|dB~rjjFf~ee;%S9hMlWz4l~2dmI~q) zR0@J*AQ6F4P$Yr$Y|_^_=i|`UMc~4v!>8sQ-9) z`sQ^&I24}kd=BJ(2roH=$7HcgqdC!0v8PrAGNC;|BJB*KQY3zRNDuN@ z+b`3AMya?YP6SQT;iF%velHEk1%UWnnyDdo{JS73^Es1~OC)4hRB0b3WBt(`m&O34 zFM6kU5XjjZz1J{KyeFeSdVeAVVL>(qcGEhNviFJBCT8Wij>n|9Qi;D_CW5&jhVClb z!3kPtiE?e2h(g^#R1PSQR(*#zV)_6HiLS)v>KDm*zgpGWOdGwe9?7S7u1VEztq>p7 zzFNO;Uy9btoz6~htwN=O7C11VBMm0`U7~+l979jD2&K98mSgHkR%fKr&8#b(<;9m$ zrW^^FVD!zSyOfUQ6Tqe1SwlT^!lv3-CoN=Fnz@?e+HEP5X^!rBowjTELkbcB*r;22 zJ987d#W*~tDz+lV7$+nJxfkU6UVv+z*(+1|1UuqZ_xV}uRm8<8KW%+#U90LQZGwZy7 zc?=BPCaf3B&oD6XRj@uP0P=aRvZ?6;b#oQ7)!6efFfchoXbwgQ&Ha$=`(B_pcNzP1 z(L4qQ&WjvTv0oV&c*{AuxrG=Q*bZ}?umd`g`5uJkS-`nt$0MM9tXw?lQyJft!F1-^{aUD$pFJN(jv(#&hrw$V0ZgiQPblaP8o|>UkGvZV;bM zScoDpT&nnD1c3?|gZUEIzXKY`z}IdMQYetgz#t&8`4VFo%jP(?(;S;)1pYAr020Gy Ai2wiq diff --git a/i18n/translation_en.ts b/i18n/translation_en.ts index 97294b40..2f41ac53 100644 --- a/i18n/translation_en.ts +++ b/i18n/translation_en.ts @@ -103,7 +103,7 @@ Update - + Update @@ -145,18 +145,18 @@ Click to DM - + could-not-send-msg-error Could not send this message Could not send this message - + acknowledged-label Acknowledged - + pending-label Pending @@ -164,25 +164,25 @@ MyProfile - + copy-btn Button for copying profile onion address to clipboard Copy - + copied-clipboard-notification Copied to clipboard Copied to clipboard - + new-group-btn create new group button Create new group - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." ... paste an address here to add a contact... @@ -273,6 +273,27 @@ Delete + + ProfileManagerPane + + + enter-profile-password + Please enter password: + Please enter password + + + + error-0-profiles-loaded-for-password + 0 profiles loaded with that password + 0 profiles loaded with that password + + + + unlock + Unlock + Unlock + + SettingsPane diff --git a/i18n/translation_fr.ts b/i18n/translation_fr.ts index 0cdbf03a..f8fe744e 100644 --- a/i18n/translation_fr.ts +++ b/i18n/translation_fr.ts @@ -145,18 +145,18 @@ Envoyer un message privé - + could-not-send-msg-error Could not send this message Impossible d'envoyer ce message - + acknowledged-label Confirmé - + pending-label En attente @@ -164,25 +164,25 @@ MyProfile - + copy-btn Button for copying profile onion address to clipboard Copier - + copied-clipboard-notification Copied to clipboard Copié dans le presse-papier - + new-group-btn create new group button Créer un nouveau groupe - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." ... coller une adresse ici pour ajouter un contact... @@ -273,6 +273,27 @@ Effacer + + ProfileManagerPane + + + enter-profile-password + Please enter password: + + + + + error-0-profiles-loaded-for-password + 0 profiles loaded with that password + + + + + unlock + Unlock + + + SettingsPane diff --git a/i18n/translation_pt.ts b/i18n/translation_pt.ts index 72acd517..a5f828f2 100644 --- a/i18n/translation_pt.ts +++ b/i18n/translation_pt.ts @@ -145,18 +145,18 @@ Clique para DM - + could-not-send-msg-error Could not send this message Não deu para enviar esta mensagem - + acknowledged-label Confirmada - + pending-label Pendente @@ -164,25 +164,25 @@ MyProfile - + copy-btn Button for copying profile onion address to clipboard Copiar - + copied-clipboard-notification Copied to clipboard Copiado - + new-group-btn create new group button Criar novo grupo - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." … cole um endereço aqui para adicionar um contato… @@ -273,6 +273,27 @@ Deletar + + ProfileManagerPane + + + enter-profile-password + Please enter password: + + + + + error-0-profiles-loaded-for-password + 0 profiles loaded with that password + + + + + unlock + Unlock + + + SettingsPane diff --git a/main.go b/main.go index 1b514f52..b6da2e69 100644 --- a/main.go +++ b/main.go @@ -255,7 +255,7 @@ func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, client if !service { the.AppBus = the.CwtchApp.GetPrimaryBus() subscribed := make(chan bool) - go handlers.App(gcd, subscribed) + go handlers.App(gcd, subscribed, clientUI) <-subscribed } diff --git a/qml.qrc b/qml.qrc index b4097e8e..7f33a9a4 100644 --- a/qml.qrc +++ b/qml.qrc @@ -13,6 +13,7 @@ qml/panes/PeerSettingsPane.qml qml/panes/SettingsPane.qml qml/panes/SplashPane.qml + qml/panes/ProfileManagerPane.qml qml/styles/CwtchComboBoxStyle.qml qml/styles/CwtchExpandingButton.qml qml/styles/CwtchTextAreaStyle.qml diff --git a/qml/main.qml b/qml/main.qml index 71b3db84..dcc2a1b4 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -113,9 +113,14 @@ ApplicationWindow { StackLayout { id: parentStack - currentIndex: 0 anchors.fill: parent + currentIndex: 0 + readonly property int splashPane: 0 + readonly property int managementPane: 1 + readonly property int profilePane: 2 + property alias pane: parentStack.currentIndex + Rectangle { // Splash pane color: "#FFFFFF" //Layout.fillHeight: true @@ -131,6 +136,18 @@ ApplicationWindow { running: true } } + + Rectangle { // Profile login/management pane + anchors.fill: parent + visible: false + color: "#D2C0DD" + + ProfileManagerPane { + id: profilesPane + anchors.fill: parent + } + } + RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR /* anchors.left: ratio >= 0.92 ? parent.left : toolbar.right anchors.top: ratio >= 0.92 ? toolbar.bottom : parent.top @@ -247,6 +264,11 @@ ApplicationWindow { onSetToolbarTitle: function(str) { theStack.title = str } + + onLoaded: function() { + parentStack.pane = parentStack.managementPane + splashPane.running = false + } } Connections { diff --git a/qml/panes/ProfileManagerPane.qml b/qml/panes/ProfileManagerPane.qml new file mode 100644 index 00000000..cd0e1d0c --- /dev/null +++ b/qml/panes/ProfileManagerPane.qml @@ -0,0 +1,84 @@ +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" +import "../widgets/controls" +import "../styles" + + +ColumnLayout { + id: thecol + anchors.fill: parent + //leftPadding: 10 + //spacing: 5 + + 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 + } + + 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 + } + + 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 + + ProfileList { + anchors.fill: parent + } + } + + +} diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index bcacf3be..1552475d 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -99,6 +99,10 @@ ColumnLayout { contactsModel.move(i, 0, 1) } } + } + + onResetProfile: function() { + contactsModel.clear() } } @@ -117,6 +121,7 @@ ColumnLayout { status: _status blocked: _blocked loading: _loading + type: "contact" } } diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 5f497ec5..e6dd9e67 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -27,6 +27,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY property alias status: imgProfile.status property string server property bool background: true + property string type Rectangle { // CONTACT ENTRY BACKGROUND COLOR @@ -39,23 +40,42 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY ContactPicture { id: imgProfile - showStatus: true + showStatus: type == "contact" } - 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.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? @@ -116,15 +136,22 @@ 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) - badge = 0 - 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.loadProfile(handle) + parentStack.pane = parentStack.profilePane } } @@ -164,7 +191,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY } onIncContactUnreadCount: function(handle) { - if (handle == _handle) { + if (handle == _handle && gcd.currentOpenConversation != handle) { badge++ } } diff --git a/qml/widgets/MyProfile.qml b/qml/widgets/MyProfile.qml index 78599a5a..d634c69a 100644 --- a/qml/widgets/MyProfile.qml +++ b/qml/widgets/MyProfile.qml @@ -19,6 +19,20 @@ 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() { + parentStack.pane = parentStack.managementPane + theStack.pane = theStack.emptyPane + } + } + Item{ height: 6 } Item { // PROFILE IMAGE @@ -229,8 +243,6 @@ ColumnLayout { lblNick.text = _nick onion = _onion image = _image - parentStack.currentIndex = 1 - splashPane.running = false } onTorStatus: function(code, str) { diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml new file mode 100644 index 00000000..8e8db340 --- /dev/null +++ b/qml/widgets/ProfileList.qml @@ -0,0 +1,92 @@ +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) { + + console.log("ProfileList onAddProfile for: " + handle) + for (var i = 0; i < profilesModel.count; i++) { + if (profilesModel.get(i)["_handle"] == handle) { + return + } + } + + profilesModel.append({ + "_handle": handle, + "_displayName": displayName, + "_image": image + }) + } + + /* + 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 + } + } + }*/ + } + + ListModel { // Profile OBJECTS ARE STORED HERE ... + id: profilesModel + } + + Repeater { + model: profilesModel // ... AND DISPLAYED HERE + delegate: ContactRow { + handle: _handle + displayName: _displayName + image: _image + server: "" + badge: 0 + status: 0 + blocked: false + loading: false + type: "profile" + } + } + } + } +} From 7a7be2777b93b000c6100e0d8912a6e0a1d5e7c6 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 19 Nov 2019 13:36:28 -0800 Subject: [PATCH 17/28] gcd and manager now lock gcd to qml ui events for non selected profile --- go/handlers/appHandler.go | 4 +- go/handlers/peerHandler.go | 2 +- go/ui/gcd.go | 128 ++++++++++++++++++++----- go/ui/manager.go | 160 ++++++++++++++++++------------- main.go | 1 - qml/overlays/BulletinOverlay.qml | 2 +- qml/overlays/ChatOverlay.qml | 2 +- qml/overlays/ListOverlay.qml | 2 +- qml/panes/OverlayPane.qml | 14 +-- qml/widgets/ContactRow.qml | 3 +- qml/widgets/MyProfile.qml | 1 + 11 files changed, 216 insertions(+), 103 deletions(-) diff --git a/go/handlers/appHandler.go b/go/handlers/appHandler.go index 7baa03d6..f58a137a 100644 --- a/go/handlers/appHandler.go +++ b/go/handlers/appHandler.go @@ -109,13 +109,13 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingFirst bo } log.Infof("NewPeer for %v\n", onion) - gcd.UIManager.AddProfile(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.UIManager, incSubscribed) + go PeerHandler(onion, gcd.GetUiManager(peer.GetProfile().Onion), incSubscribed) <-incSubscribed if e.Data[event.Status] != "running" { diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index a753db98..cefe3d23 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -9,7 +9,7 @@ import ( "time" ) -func PeerHandler(onion string, uiManager *ui.Manager, subscribed chan bool) { +func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { peer := the.CwtchApp.GetPeer(onion) eventBus := the.CwtchApp.GetEventBus(onion) q := event.NewQueue() diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 1c3c8e7b..6d1d2593 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -5,6 +5,7 @@ import ( "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" "github.com/therecipe/qt/qml" + "sync" "cwtch.im/ui/go/the" "encoding/base32" @@ -17,15 +18,21 @@ import ( type GrandCentralDispatcher struct { core.QObject - UIManager Manager - QMLEngine *qml.QQmlApplicationEngine - Translator *core.QTranslator + uIManagers map[string]Manager + QMLEngine *qml.QQmlApplicationEngine + Translator *core.QTranslator + 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:"selectedProfile,auto"` + _ string `property:"selectedConversation,auto"` // profile management stuff _ func() `signal:"Loaded"` @@ -63,6 +70,8 @@ type GrandCentralDispatcher struct { _ func(onion, nick string, blocked bool) `signal:"SupplyPeerSettings"` // signals emitted from the ui (written in go, below) + //_ func(onion string) `signal:"setSelectedProfile,auto"` + //_ func(onion string) `signal:"setSelectedConversation,auto"` _ func(message string, mid string) `signal:"sendMessage,auto"` _ func(onion string) `signal:"blockPeer,auto"` _ func(onion string) `signal:"unblockPeer,auto"` @@ -90,6 +99,78 @@ type GrandCentralDispatcher struct { _ func() `signal:"onActivate,auto"` _ func(password string) `signal:"unlockProfiles,auto"` _ func(handle string) `signal:"loadProfile,auto"` + + _ func() `constructor:"init"` +} + +func (this *GrandCentralDispatcher) init() { + this.uIManagers = make(map[string]Manager) +} + +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) +} + +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) +} + +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) { @@ -98,14 +179,14 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { return } - if this.CurrentOpenConversation() == "" { + if this.SelectedConversation() == "" { this.InvokePopup("ui error") return } - if isGroup(this.CurrentOpenConversation()) { - 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 @@ -113,19 +194,19 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { } var err error - mID, err = the.Peer.SendMessageToGroupTracked(this.CurrentOpenConversation(), message) + mID, err = the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message) - this.UIManager.AddMessage(this.CurrentOpenConversation(), "me", message, true, mID, time.Now(), 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 { - to := this.CurrentOpenConversation() + to := this.SelectedConversation() mID = the.Peer.SendMessageToPeer(to, message) - this.UIManager.AddMessage(to, "me", message, true, mID, time.Now(), false) + this.GetUiManager(this.selectedProfile()).AddMessage(to, "me", message, true, mID, time.Now(), false) } } @@ -139,7 +220,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { return } this.ClearMessages() - this.SetCurrentOpenConversation(handle) + this.SetSelectedConversation(handle) if isGroup(handle) { // LOAD GROUP group := the.Peer.GetGroup(handle) @@ -245,17 +326,18 @@ func (this *GrandCentralDispatcher) saveSettings(zoom, locale string) { } 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(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 } @@ -379,7 +461,7 @@ func (this *GrandCentralDispatcher) importString(str string) { the.Peer.PeerWithOnion(onion) } - this.UIManager.AddContact(onion) + this.GetUiManager(this.selectedProfile()).AddContact(onion) } func (this *GrandCentralDispatcher) popup(str string) { @@ -400,7 +482,7 @@ func (this *GrandCentralDispatcher) createGroup(server, groupName string) { return } - this.UIManager.AddContact(groupID) + this.GetUiManager(this.selectedProfile()).AddContact(groupID) the.Peer.SetGroupAttribute(groupID, constants.Nick, groupName) @@ -449,7 +531,7 @@ func (this *GrandCentralDispatcher) acceptGroup(groupID string) { func (this *GrandCentralDispatcher) setAttribute(onion, key, value string) { the.Peer.SetContactAttribute(onion, key, value) - this.UIManager.UpdateContactAttribute(onion, key, value) + this.GetUiManager(this.selectedProfile()).UpdateContactAttribute(onion, key, value) } func (this *GrandCentralDispatcher) blockUnknownPeers() { @@ -505,13 +587,13 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) { contacts := the.Peer.GetContacts() for i := range contacts { - this.UIManager.AddContact(contacts[i]) + 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.UIManager.AddContact(groups[i]) + this.GetUiManager(this.selectedProfile()).AddContact(groups[i]) } // load ui preferences diff --git a/go/ui/manager.go b/go/ui/manager.go index b2f0ad34..40d735bf 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -107,15 +107,7 @@ func countUnread(messages []model.Message, lastRead time.Time) int { return count } -type Manager struct { - gcd *GrandCentralDispatcher -} - -func NewManager(gcd *GrandCentralDispatcher) Manager { - return Manager{gcd} -} - -func (this *Manager) AddProfile(handle string) { +func AddProfile(gcd *GrandCentralDispatcher, handle string) { peer := the.CwtchApp.GetPeer(handle) if peer != nil { nick := peer.GetProfile().Name @@ -130,12 +122,34 @@ func (this *Manager) AddProfile(handle string) { peer.SetAttribute(constants.Picture, pic) } log.Infof("AddProfile %v %v %v\n", handle, nick, pic) - this.gcd.AddProfile(handle, nick, pic) + gcd.AddProfile(handle, nick, pic) } } -func (this *Manager) Acknowledge(mID string) { - this.gcd.Acknowledged(mID) +type manager struct { + gcd *GrandCentralDispatcher + profile string +} + +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) + + UpdateContactDisplayName(handle string, name string) + UpdateContactStatus(handle string, status int, loading bool) + UpdateContactAttribute(handle, key, value string) +} + +func NewManager(profile string, gcd *GrandCentralDispatcher) Manager { + return &manager{gcd: gcd, profile: profile} +} + +func (this *manager) Acknowledge(mID string) { + this.gcd.DoIfProfile(this.profile, func() { + this.gcd.Acknowledged(mID) + }) } func getLastMessageTime(tl *model.Timeline) int { @@ -146,77 +160,93 @@ func getLastMessageTime(tl *model.Timeline) int { return int(tl.Messages[len(tl.Messages)-1].Timestamp.Unix()) } -func (this *Manager) AddContact(Handle string) { - if isGroup(Handle) { - group := the.Peer.GetGroup(Handle) - if group != nil { - lastRead := initLastReadTime(group.GroupID) - unread := countUnread(group.Timeline.GetMessages(), lastRead) +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 := group.GetAttribute(constants.Nick) + nick, exists := contact.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)) + this.gcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false, getLastMessageTime(&contact.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)) - } + }) } -func (this *Manager) AddSendMessageError(peer string, signature string, err string) { - 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) +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) + }) + }) } -func (this *Manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) { - nick := getOrDefault(handle, constants.Nick, handle) - image := getProfilePic(handle) +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() { - // If we have this group loaded already - if this.gcd.CurrentOpenConversation() == handle { - 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 { + 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 { - this.gcd.Acknowledged(messageID) + 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) + }) + this.gcd.IncContactUnreadCount(handle) + }) } -func (this *Manager) UpdateContactDisplayName(handle string, name string) { - this.gcd.UpdateContactDisplayName(handle, name) +func (this *manager) UpdateContactDisplayName(handle string, name string) { + this.gcd.DoIfProfile(this.profile, func() { + this.gcd.UpdateContactDisplayName(handle, name) + }) } -func (this *Manager) UpdateContactStatus(handle string, status int, loading bool) { - this.gcd.UpdateContactStatus(handle, status, loading) +func (this *manager) UpdateContactStatus(handle string, status int, loading bool) { + this.gcd.DoIfProfile(this.profile, func() { + this.gcd.UpdateContactStatus(handle, status, loading) + }) } -func (this *Manager) UpdateContactAttribute(handle, key, value string) { - this.gcd.UpdateContactAttribute(handle, key, value) +func (this *manager) UpdateContactAttribute(handle, key, value string) { + this.gcd.DoIfProfile(this.profile, func() { + this.gcd.UpdateContactAttribute(handle, key, value) + }) } diff --git a/main.go b/main.go index b6da2e69..2e5a587b 100644 --- a/main.go +++ b/main.go @@ -137,7 +137,6 @@ func mainUi(flagLocal bool, flagClientUI bool) { gcd.SetVersion("development") gcd.SetBuildDate("now") } - gcd.UIManager = ui.NewManager(gcd) //TODO: put theme stuff somewhere better gcd.SetThemeScale(1.0) diff --git a/qml/overlays/BulletinOverlay.qml b/qml/overlays/BulletinOverlay.qml index f914acb6..71e32c8d 100644 --- a/qml/overlays/BulletinOverlay.qml +++ b/qml/overlays/BulletinOverlay.qml @@ -93,7 +93,7 @@ ColumnLayout { } onUpdateContactStatus: function(_handle, _status, _loading) { - if (gcd.currentOpenConversation == _handle) { + if (gcd.selectedConversation == _handle) { if (_loading == true) { newposttitle.enabled = false newpostbody.enabled = false diff --git a/qml/overlays/ChatOverlay.qml b/qml/overlays/ChatOverlay.qml index b2c6d9ae..a6677e9a 100644 --- a/qml/overlays/ChatOverlay.qml +++ b/qml/overlays/ChatOverlay.qml @@ -111,7 +111,7 @@ ColumnLayout { } onUpdateContactStatus: function(_handle, _status, _loading) { - if (gcd.currentOpenConversation == _handle) { + if (gcd.selectedConversation == _handle) { // Group is Synced OR p2p is Authenticated if ( (_handle.length == 32 && _status == 4) || (_handle.length == 56 && _status == 3) ) { txtMessage.enabled = true diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 6f983722..40c8acb8 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -97,7 +97,7 @@ ColumnLayout { } onUpdateContactStatus: function(_handle, _status, _loading) { - if (gcd.currentOpenConversation == _handle) { + if (gcd.selectedConversation == _handle) { if (_loading == true) { newposttitle.enabled = false btnSend.enabled = false diff --git a/qml/panes/OverlayPane.qml b/qml/panes/OverlayPane.qml index 6ac6f185..d56d3462 100644 --- a/qml/panes/OverlayPane.qml +++ b/qml/panes/OverlayPane.qml @@ -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 } } diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index e6dd9e67..e5de3639 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -150,6 +150,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY } else if (type == "profile") { gcd.broadcast("ResetMessagePane") gcd.broadcast("ResetProfile") + gcd.selectedProfile = handle gcd.loadProfile(handle) parentStack.pane = parentStack.profilePane } @@ -191,7 +192,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY } onIncContactUnreadCount: function(handle) { - if (handle == _handle && gcd.currentOpenConversation != handle) { + if (handle == _handle && gcd.selectedConversation != handle) { badge++ } } diff --git a/qml/widgets/MyProfile.qml b/qml/widgets/MyProfile.qml index d634c69a..cdcd96e0 100644 --- a/qml/widgets/MyProfile.qml +++ b/qml/widgets/MyProfile.qml @@ -28,6 +28,7 @@ ColumnLayout { anchors.top: parent.top anchors.topMargin: 2 onClicked: function() { + gcd.selectedProfile = "none" parentStack.pane = parentStack.managementPane theStack.pane = theStack.emptyPane } From d3e178474df40aa868350c5b19ca5a1d313e1ef6 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 20 Nov 2019 13:05:50 -0800 Subject: [PATCH 18/28] show add profile button --- qml/widgets/ContactPicture.qml | 3 ++- qml/widgets/ContactRow.qml | 1 + qml/widgets/ProfileList.qml | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/qml/widgets/ContactPicture.qml b/qml/widgets/ContactPicture.qml index 42971be5..d8b9ead6 100644 --- a/qml/widgets/ContactPicture.qml +++ b/qml/widgets/ContactPicture.qml @@ -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 diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index e5de3639..88f3abf9 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -41,6 +41,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY ContactPicture { id: imgProfile showStatus: type == "contact" + button: type == "button" } ColumnLayout { diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml index 8e8db340..a2a4f147 100644 --- a/qml/widgets/ProfileList.qml +++ b/qml/widgets/ProfileList.qml @@ -50,10 +50,12 @@ ColumnLayout { } } - profilesModel.append({ + profilesModel.insert(profilesModel.count-1, + { "_handle": handle, "_displayName": displayName, - "_image": image + "_image": image, + "_type": "profile" }) } @@ -71,6 +73,13 @@ ColumnLayout { ListModel { // Profile OBJECTS ARE STORED HERE ... id: profilesModel + + ListElement { + _handle: "" + _displayName: qsTr("Add new profile") + _image: "qrc:/qml/images/fontawesome/solid/user-plus.svg" + _type: "button" + } } Repeater { @@ -84,7 +93,7 @@ ColumnLayout { status: 0 blocked: false loading: false - type: "profile" + type: _type } } } From 1b20c9ed02274a49d5aaca05562c485d2c402308 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 19 Nov 2019 13:36:28 -0800 Subject: [PATCH 19/28] gcd and manager now lock gcd to qml ui events for non selected profile --- go/handlers/appHandler.go | 4 +- go/handlers/peerHandler.go | 2 +- go/ui/gcd.go | 129 +++++++++++++++++++---- go/ui/manager.go | 174 +++++++++++++++++++------------ main.go | 1 - qml/overlays/BulletinOverlay.qml | 2 +- qml/overlays/ChatOverlay.qml | 2 +- qml/overlays/ListOverlay.qml | 2 +- qml/panes/OverlayPane.qml | 14 +-- qml/widgets/ContactRow.qml | 3 +- qml/widgets/MyProfile.qml | 1 + 11 files changed, 233 insertions(+), 101 deletions(-) diff --git a/go/handlers/appHandler.go b/go/handlers/appHandler.go index 7baa03d6..f58a137a 100644 --- a/go/handlers/appHandler.go +++ b/go/handlers/appHandler.go @@ -109,13 +109,13 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingFirst bo } log.Infof("NewPeer for %v\n", onion) - gcd.UIManager.AddProfile(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.UIManager, incSubscribed) + go PeerHandler(onion, gcd.GetUiManager(peer.GetProfile().Onion), incSubscribed) <-incSubscribed if e.Data[event.Status] != "running" { diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index a753db98..cefe3d23 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -9,7 +9,7 @@ import ( "time" ) -func PeerHandler(onion string, uiManager *ui.Manager, subscribed chan bool) { +func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { peer := the.CwtchApp.GetPeer(onion) eventBus := the.CwtchApp.GetEventBus(onion) q := event.NewQueue() diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 1c3c8e7b..ca066b5f 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -5,6 +5,7 @@ import ( "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" "github.com/therecipe/qt/qml" + "sync" "cwtch.im/ui/go/the" "encoding/base32" @@ -17,15 +18,23 @@ import ( type GrandCentralDispatcher struct { core.QObject - UIManager Manager 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:"selectedProfile,auto"` + _ string `property:"selectedConversation,auto"` // profile management stuff _ func() `signal:"Loaded"` @@ -90,6 +99,83 @@ type GrandCentralDispatcher struct { _ func() `signal:"onActivate,auto"` _ func(password string) `signal:"unlockProfiles,auto"` _ func(handle string) `signal:"loadProfile,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) { @@ -98,14 +184,14 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { return } - if this.CurrentOpenConversation() == "" { + if this.SelectedConversation() == "" { this.InvokePopup("ui error") return } - if isGroup(this.CurrentOpenConversation()) { - 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 @@ -113,19 +199,19 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { } var err error - mID, err = the.Peer.SendMessageToGroupTracked(this.CurrentOpenConversation(), message) + mID, err = the.Peer.SendMessageToGroupTracked(this.SelectedConversation(), message) - this.UIManager.AddMessage(this.CurrentOpenConversation(), "me", message, true, mID, time.Now(), 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 { - to := this.CurrentOpenConversation() + to := this.SelectedConversation() mID = the.Peer.SendMessageToPeer(to, message) - this.UIManager.AddMessage(to, "me", message, true, mID, time.Now(), false) + this.GetUiManager(this.selectedProfile()).AddMessage(to, "me", message, true, mID, time.Now(), false) } } @@ -139,7 +225,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { return } this.ClearMessages() - this.SetCurrentOpenConversation(handle) + this.SetSelectedConversation(handle) if isGroup(handle) { // LOAD GROUP group := the.Peer.GetGroup(handle) @@ -245,17 +331,18 @@ func (this *GrandCentralDispatcher) saveSettings(zoom, locale string) { } 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(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 } @@ -379,7 +466,7 @@ func (this *GrandCentralDispatcher) importString(str string) { the.Peer.PeerWithOnion(onion) } - this.UIManager.AddContact(onion) + this.GetUiManager(this.selectedProfile()).AddContact(onion) } func (this *GrandCentralDispatcher) popup(str string) { @@ -400,7 +487,7 @@ func (this *GrandCentralDispatcher) createGroup(server, groupName string) { return } - this.UIManager.AddContact(groupID) + this.GetUiManager(this.selectedProfile()).AddContact(groupID) the.Peer.SetGroupAttribute(groupID, constants.Nick, groupName) @@ -449,7 +536,7 @@ func (this *GrandCentralDispatcher) acceptGroup(groupID string) { func (this *GrandCentralDispatcher) setAttribute(onion, key, value string) { the.Peer.SetContactAttribute(onion, key, value) - this.UIManager.UpdateContactAttribute(onion, key, value) + this.GetUiManager(this.selectedProfile()).UpdateContactAttribute(onion, key, value) } func (this *GrandCentralDispatcher) blockUnknownPeers() { @@ -505,13 +592,13 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) { contacts := the.Peer.GetContacts() for i := range contacts { - this.UIManager.AddContact(contacts[i]) + 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.UIManager.AddContact(groups[i]) + this.GetUiManager(this.selectedProfile()).AddContact(groups[i]) } // load ui preferences diff --git a/go/ui/manager.go b/go/ui/manager.go index b2f0ad34..a23493dd 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -107,15 +107,8 @@ func countUnread(messages []model.Message, lastRead time.Time) int { return count } -type Manager struct { - gcd *GrandCentralDispatcher -} - -func NewManager(gcd *GrandCentralDispatcher) Manager { - return Manager{gcd} -} - -func (this *Manager) AddProfile(handle string) { +// 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 @@ -130,12 +123,41 @@ func (this *Manager) AddProfile(handle string) { peer.SetAttribute(constants.Picture, pic) } log.Infof("AddProfile %v %v %v\n", handle, nick, pic) - this.gcd.AddProfile(handle, nick, pic) + gcd.AddProfile(handle, nick, pic) } } -func (this *Manager) Acknowledge(mID string) { - this.gcd.Acknowledged(mID) +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) + + UpdateContactDisplayName(handle string, name string) + UpdateContactStatus(handle string, status int, loading bool) + UpdateContactAttribute(handle, key, value string) +} + +// 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 { @@ -146,77 +168,99 @@ func getLastMessageTime(tl *model.Timeline) int { return int(tl.Messages[len(tl.Messages)-1].Timestamp.Unix()) } -func (this *Manager) AddContact(Handle string) { - if isGroup(Handle) { - group := the.Peer.GetGroup(Handle) - if group != nil { - lastRead := initLastReadTime(group.GroupID) - unread := countUnread(group.Timeline.GetMessages(), lastRead) +// 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 := group.GetAttribute(constants.Nick) + nick, exists := contact.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)) + this.gcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false, getLastMessageTime(&contact.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)) - } + }) } -func (this *Manager) AddSendMessageError(peer string, signature string, err string) { - 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) +// 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) + }) + }) } -func (this *Manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) { - nick := getOrDefault(handle, constants.Nick, handle) - image := getProfilePic(handle) +// 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() { - // If we have this group loaded already - if this.gcd.CurrentOpenConversation() == handle { - 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 { + 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 { - this.gcd.Acknowledged(messageID) + 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) + }) + this.gcd.IncContactUnreadCount(handle) + }) } -func (this *Manager) UpdateContactDisplayName(handle string, name string) { - this.gcd.UpdateContactDisplayName(handle, name) +// 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) + }) } -func (this *Manager) UpdateContactStatus(handle string, status int, loading bool) { - this.gcd.UpdateContactStatus(handle, status, loading) +// 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) + }) } -func (this *Manager) UpdateContactAttribute(handle, key, value string) { - this.gcd.UpdateContactAttribute(handle, key, value) +// 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) + }) } diff --git a/main.go b/main.go index b6da2e69..2e5a587b 100644 --- a/main.go +++ b/main.go @@ -137,7 +137,6 @@ func mainUi(flagLocal bool, flagClientUI bool) { gcd.SetVersion("development") gcd.SetBuildDate("now") } - gcd.UIManager = ui.NewManager(gcd) //TODO: put theme stuff somewhere better gcd.SetThemeScale(1.0) diff --git a/qml/overlays/BulletinOverlay.qml b/qml/overlays/BulletinOverlay.qml index f914acb6..71e32c8d 100644 --- a/qml/overlays/BulletinOverlay.qml +++ b/qml/overlays/BulletinOverlay.qml @@ -93,7 +93,7 @@ ColumnLayout { } onUpdateContactStatus: function(_handle, _status, _loading) { - if (gcd.currentOpenConversation == _handle) { + if (gcd.selectedConversation == _handle) { if (_loading == true) { newposttitle.enabled = false newpostbody.enabled = false diff --git a/qml/overlays/ChatOverlay.qml b/qml/overlays/ChatOverlay.qml index b2c6d9ae..a6677e9a 100644 --- a/qml/overlays/ChatOverlay.qml +++ b/qml/overlays/ChatOverlay.qml @@ -111,7 +111,7 @@ ColumnLayout { } onUpdateContactStatus: function(_handle, _status, _loading) { - if (gcd.currentOpenConversation == _handle) { + if (gcd.selectedConversation == _handle) { // Group is Synced OR p2p is Authenticated if ( (_handle.length == 32 && _status == 4) || (_handle.length == 56 && _status == 3) ) { txtMessage.enabled = true diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 6f983722..40c8acb8 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -97,7 +97,7 @@ ColumnLayout { } onUpdateContactStatus: function(_handle, _status, _loading) { - if (gcd.currentOpenConversation == _handle) { + if (gcd.selectedConversation == _handle) { if (_loading == true) { newposttitle.enabled = false btnSend.enabled = false diff --git a/qml/panes/OverlayPane.qml b/qml/panes/OverlayPane.qml index 6ac6f185..d56d3462 100644 --- a/qml/panes/OverlayPane.qml +++ b/qml/panes/OverlayPane.qml @@ -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 } } diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index e6dd9e67..e5de3639 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -150,6 +150,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY } else if (type == "profile") { gcd.broadcast("ResetMessagePane") gcd.broadcast("ResetProfile") + gcd.selectedProfile = handle gcd.loadProfile(handle) parentStack.pane = parentStack.profilePane } @@ -191,7 +192,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY } onIncContactUnreadCount: function(handle) { - if (handle == _handle && gcd.currentOpenConversation != handle) { + if (handle == _handle && gcd.selectedConversation != handle) { badge++ } } diff --git a/qml/widgets/MyProfile.qml b/qml/widgets/MyProfile.qml index d634c69a..cdcd96e0 100644 --- a/qml/widgets/MyProfile.qml +++ b/qml/widgets/MyProfile.qml @@ -28,6 +28,7 @@ ColumnLayout { anchors.top: parent.top anchors.topMargin: 2 onClicked: function() { + gcd.selectedProfile = "none" parentStack.pane = parentStack.managementPane theStack.pane = theStack.emptyPane } From 541f4f7da44155ce9b6c58e3def3b47112076edf Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 29 Nov 2019 17:35:57 -0800 Subject: [PATCH 20/28] profile edit (name) and delete (non functioning cus cwtch doesnt support) ui --- go/handlers/appHandler.go | 2 +- go/ui/gcd.go | 48 +++++++++++++----- i18n/translation_de.ts | 95 +++++++++++++++++++++++++++++++++-- i18n/translation_en.qm | Bin 4954 -> 6217 bytes i18n/translation_en.ts | 95 +++++++++++++++++++++++++++++++++-- i18n/translation_fr.ts | 95 +++++++++++++++++++++++++++++++++-- i18n/translation_pt.ts | 95 +++++++++++++++++++++++++++++++++-- qml.qrc | 1 + qml/main.qml | 15 +++++- qml/widgets/ContactRow.qml | 45 +++++++++++++++-- qml/widgets/MyProfile.qml | 3 +- qml/widgets/ProfileList.qml | 12 ++++- qml/widgets/StackToolbar.qml | 9 +++- 13 files changed, 474 insertions(+), 41 deletions(-) diff --git a/go/handlers/appHandler.go b/go/handlers/appHandler.go index f58a137a..eded1282 100644 --- a/go/handlers/appHandler.go +++ b/go/handlers/appHandler.go @@ -121,7 +121,7 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingFirst bo if e.Data[event.Status] != "running" { peer.Listen() peer.StartPeersConnections() - peer.StartGroupConnections() + //peer.StartGroupConnections() } blockUnkownPeers, exists := peer.GetProfile().GetAttribute(constants.BlockUnknownPeersSetting) diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 69b3d010..8e441618 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -41,6 +41,8 @@ type GrandCentralDispatcher struct { _ func(handle, displayname, image string) `signal:"AddProfile"` _ func() `signal:"ErrorLoaded0"` _ func() `signal:"ResetProfile"` + _ func() `signal:"ResetProfileList"` + _ func(onion string) `signal:"deleteProfile"` // contact list stuff _ func(handle, displayName, image, server string, badge, status int, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"` @@ -72,8 +74,16 @@ type GrandCentralDispatcher struct { _ func(onion, nick string, blocked bool) `signal:"SupplyPeerSettings"` // signals emitted from the ui (written in go, below) - //_ func(onion string) `signal:"setSelectedProfile,auto"` - //_ func(onion string) `signal:"setSelectedConversation,auto"` + // 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, password string) `signal:"createProfile,auto"` + _ func(password string) `signal:"unlockProfiles,auto"` + _ func() `signal:"reloadProfileList,auto"` + // operating a profile _ func(message string, mid string) `signal:"sendMessage,auto"` _ func(onion string) `signal:"blockPeer,auto"` _ func(onion string) `signal:"unblockPeer,auto"` @@ -82,7 +92,6 @@ type GrandCentralDispatcher struct { _ 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"` @@ -95,12 +104,8 @@ 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() `signal:"onActivate,auto"` - _ func(password string) `signal:"unlockProfiles,auto"` - _ func(handle string) `signal:"loadProfile,auto"` _ func() `constructor:"init"` } @@ -475,11 +480,14 @@ 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) { @@ -610,3 +618,19 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) { this.SetLocale_helper(locale) } } + +func (this *GrandCentralDispatcher) createProfile(nick, password string) { + the.CwtchApp.CreatePeer(nick, password) +} + +func (this *GrandCentralDispatcher) reloadProfileList() { + this.ResetProfileList() + + for onion, _ := range the.CwtchApp.ListPeers() { + AddProfile(this, onion) + } +} + +func (this *GrandCentralDispatcher) deleteProfile(onion string) { + //the.CwtchApp. +} diff --git a/i18n/translation_de.ts b/i18n/translation_de.ts index 5398cd7a..47ebb17e 100644 --- a/i18n/translation_de.ts +++ b/i18n/translation_de.ts @@ -164,25 +164,25 @@ MyProfile - + copy-btn Button for copying profile onion address to clipboard Kopieren - + copied-clipboard-notification Copied to clipboard in die Zwischenablage kopiert - + new-group-btn create new group button Neue Gruppe anlegen - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." Adresse hier hinzufügen, um einen Kontakt aufzunehmen @@ -273,6 +273,91 @@ löschen + + ProfileAddEditPane + + + add-profile-title + + + + + edit-profile-title + + + + + profile-name + Profile name + + + + + + default-profile-name + default suggested profile name + + + + + profile-onion-label + Profile Onion + + + + + password1-label + Password + + + + + password2-label + Reenter password + + + + + create-profile-btn + Create Profile || Save Profile + + + + + save-profile-btn + + + + + password-error-match + Passwords do not match + + + + + delete-profile-btn + Delete Profile + + + + + delete-confirm-label + Type DELETE to confirm + + + + + delete-profile-confirm-btn + Really Delete Profile + + + + + delete-confirm-text + DELETE + + + ProfileManagerPane @@ -339,7 +424,7 @@ StackToolbar - + view-group-membership-tooltip View Group Membership diff --git a/i18n/translation_en.qm b/i18n/translation_en.qm index 5149964b83b8349c7af5bf0d86c42bebe3b366e0..f14e39c389862449728bb205aa54c4b2486ef7e1 100644 GIT binary patch delta 1533 zcma)*|4Y{vh-P(-DZ#r9luy&fDj>#cn%x)6-v? z5d9Pcc1VFC6bKb2k{R_wMNopGLRu7+^bbS{)o)QfyX%ISY%#WnefHVwc|Xtl`Mhsf z*js<#kq}<|dSh(4F7mAJ=9Bd!0FFke{2&21FF}|55un03T3>f`YWHgZ?_KKNS~G6> zP0cya0F;eTj|C3E(@U*$7~!C!taKW{O&t3JxIfV5$i15aC_n5Ry6Q*N_0fTbrXXGN zZGHuS9;U0STLJvv>513`z>f3u{Ow7Ak{V!R!> zV^mE~aE1-N{fwI56ry8}jh?uVS;yI1WB8k6xDeTHc5Wk#IEsD#(@<6K@nxQ(B+kQM zK7`mWp0!Uj_TX1;l4{)PUC>tW<&JlG1fzGuSI{yo0#w56Ds{@E*FZZ6c-4Xo1hN>@ zpn?Jk5J3XsMomzPcR4Qz(X^^0M2SQ-QIm)ZR|B!aO1mI%f~duKnNUt#k3a`f2=Jic zvle38G6Qi{C%i^XZoej5Q5n=Kp|1EQT_J%a_yI{X88V-;l-3LxEjfyvu*nt1E(q964b%KP2D&j@0fMt*8fZIQ z&B);)gI5-)i3DpRjQ6T_*+x#vKe|w{sX6m|GdV^6oG&2((O8Rn%X5 zS%-mv=|AIE<`@PB&IZQ2uC5FW47N<1Z0i^pI8vERgsw0!@a|%+m?Xf!ASgMpS51kp zjpg&Pa|{fum8|mu<}omEXR#hzv6O*&0}ETVrM^UI-7xk z&zD0=Mg(Y3Fh^AES0MkvWLri<{w;NVKucY?ws`?9<0_ln%P42XZ^$EZ{SD9!S0Oah zRvz8JO+e?$@GP1NRL)chp?Q3G4*mhzwv0Ei+YYGx-Q>TF8myT>S2k{TVhZQoEFogZ F0st}yOvC^H diff --git a/i18n/translation_en.ts b/i18n/translation_en.ts index 2f41ac53..575e96fb 100644 --- a/i18n/translation_en.ts +++ b/i18n/translation_en.ts @@ -164,25 +164,25 @@ MyProfile - + copy-btn Button for copying profile onion address to clipboard Copy - + copied-clipboard-notification Copied to clipboard Copied to clipboard - + new-group-btn create new group button Create new group - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." ... paste an address here to add a contact... @@ -273,6 +273,91 @@ Delete + + ProfileAddEditPane + + + add-profile-title + Add new profile + + + + edit-profile-title + Edit Profile + + + + profile-name + Profile name + Display name + + + + + default-profile-name + default suggested profile name + Alice + + + + profile-onion-label + Profile Onion + Onion + + + + password1-label + Password + Password + + + + password2-label + Reenter password + Password + + + + create-profile-btn + Create Profile || Save Profile + Create Profile + + + + save-profile-btn + Save Profile + + + + password-error-match + Passwords do not match + Passwords do not match + + + + delete-profile-btn + Delete Profile + Delete Profile + + + + delete-confirm-label + Type DELETE to confirm + Type DELETE to confirm + + + + delete-profile-confirm-btn + Really Delete Profile + Really Delete Profile + + + + delete-confirm-text + DELETE + DELETE + + ProfileManagerPane @@ -339,7 +424,7 @@ StackToolbar - + view-group-membership-tooltip View Group Membership View Group Membership diff --git a/i18n/translation_fr.ts b/i18n/translation_fr.ts index f8fe744e..157be8fa 100644 --- a/i18n/translation_fr.ts +++ b/i18n/translation_fr.ts @@ -164,25 +164,25 @@ MyProfile - + copy-btn Button for copying profile onion address to clipboard Copier - + copied-clipboard-notification Copied to clipboard Copié dans le presse-papier - + new-group-btn create new group button Créer un nouveau groupe - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." ... coller une adresse ici pour ajouter un contact... @@ -273,6 +273,91 @@ Effacer + + ProfileAddEditPane + + + add-profile-title + + + + + edit-profile-title + + + + + profile-name + Profile name + + + + + + default-profile-name + default suggested profile name + + + + + profile-onion-label + Profile Onion + + + + + password1-label + Password + + + + + password2-label + Reenter password + + + + + create-profile-btn + Create Profile || Save Profile + + + + + save-profile-btn + + + + + password-error-match + Passwords do not match + + + + + delete-profile-btn + Delete Profile + + + + + delete-confirm-label + Type DELETE to confirm + + + + + delete-profile-confirm-btn + Really Delete Profile + + + + + delete-confirm-text + DELETE + + + ProfileManagerPane @@ -339,7 +424,7 @@ StackToolbar - + view-group-membership-tooltip View Group Membership diff --git a/i18n/translation_pt.ts b/i18n/translation_pt.ts index a5f828f2..f7ab7e7a 100644 --- a/i18n/translation_pt.ts +++ b/i18n/translation_pt.ts @@ -164,25 +164,25 @@ MyProfile - + copy-btn Button for copying profile onion address to clipboard Copiar - + copied-clipboard-notification Copied to clipboard Copiado - + new-group-btn create new group button Criar novo grupo - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." … cole um endereço aqui para adicionar um contato… @@ -273,6 +273,91 @@ Deletar + + ProfileAddEditPane + + + add-profile-title + + + + + edit-profile-title + + + + + profile-name + Profile name + + + + + + default-profile-name + default suggested profile name + + + + + profile-onion-label + Profile Onion + + + + + password1-label + Password + + + + + password2-label + Reenter password + + + + + create-profile-btn + Create Profile || Save Profile + + + + + save-profile-btn + + + + + password-error-match + Passwords do not match + + + + + delete-profile-btn + Delete Profile + + + + + delete-confirm-label + Type DELETE to confirm + + + + + delete-profile-confirm-btn + Really Delete Profile + + + + + delete-confirm-text + DELETE + + + ProfileManagerPane @@ -339,7 +424,7 @@ StackToolbar - + view-group-membership-tooltip View Group Membership diff --git a/qml.qrc b/qml.qrc index 7f33a9a4..f7d266c6 100644 --- a/qml.qrc +++ b/qml.qrc @@ -14,6 +14,7 @@ qml/panes/SettingsPane.qml qml/panes/SplashPane.qml qml/panes/ProfileManagerPane.qml + qml/panes/ProfileAddEditPane.qml qml/styles/CwtchComboBoxStyle.qml qml/styles/CwtchExpandingButton.qml qml/styles/CwtchTextAreaStyle.qml diff --git a/qml/main.qml b/qml/main.qml index dcc2a1b4..0540d843 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -118,7 +118,8 @@ ApplicationWindow { currentIndex: 0 readonly property int splashPane: 0 readonly property int managementPane: 1 - readonly property int profilePane: 2 + readonly property int addEditProfilePane: 2 + readonly property int profilePane: 3 property alias pane: parentStack.currentIndex Rectangle { // Splash pane @@ -148,6 +149,18 @@ ApplicationWindow { } } + 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 anchors.top: ratio >= 0.92 ? toolbar.bottom : parent.top diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 88f3abf9..6e619d09 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -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,17 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY property int badge property bool isActive property bool isHover + property bool background: true + property string type // profile or contact or button + + // Profile + property bool defaultPassword + + // Contact property bool blocked property bool loading - property alias status: imgProfile.status - property string server - property bool background: true - property string type + property alias status: imgProfile.status + property string server Rectangle { // CONTACT ENTRY BACKGROUND COLOR @@ -106,6 +112,11 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY } } + // Profile + + + + // Contact ProgressBar { // LOADING ? id: loadingProgress property bool running @@ -154,6 +165,10 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY gcd.selectedProfile = handle gcd.loadProfile(handle) parentStack.pane = parentStack.profilePane + } else if (type == "button") { // Add profile button + profileAddEditPane.mode = "add" + profileAddEditPane.reset() + parentStack.pane = parentStack.addEditProfilePane } } @@ -166,6 +181,28 @@ 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.mode = "edit" + profileAddEditPane.load(handle, displayName, "") + parentStack.pane = parentStack.addEditProfilePane + } + } + Connections { // UPDATE UNREAD MESSAGES COUNTER target: gcd diff --git a/qml/widgets/MyProfile.qml b/qml/widgets/MyProfile.qml index cdcd96e0..dc7df543 100644 --- a/qml/widgets/MyProfile.qml +++ b/qml/widgets/MyProfile.qml @@ -29,6 +29,7 @@ ColumnLayout { anchors.topMargin: 2 onClicked: function() { gcd.selectedProfile = "none" + gcd.reloadProfileList() parentStack.pane = parentStack.managementPane theStack.pane = theStack.emptyPane } @@ -120,7 +121,7 @@ ColumnLayout { Layout.alignment: Qt.AlignHCenter onUpdated: { - gcd.updateNick(lblNick.text) + gcd.updateNick(onion, lblNick.text) } } diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml index a2a4f147..f039416f 100644 --- a/qml/widgets/ProfileList.qml +++ b/qml/widgets/ProfileList.qml @@ -69,6 +69,16 @@ ColumnLayout { } } }*/ + + onResetProfileList: function() { + profilesModel.clear() + profilesModel.append({ + _handle: "", + _displayName: qsTr("add-new-profile-btn"), + _image: "qrc:/qml/images/fontawesome/solid/user-plus.svg", + _type: "button", + }) + } } ListModel { // Profile OBJECTS ARE STORED HERE ... @@ -76,7 +86,7 @@ ColumnLayout { ListElement { _handle: "" - _displayName: qsTr("Add new profile") + _displayName: qsTr("add-new-profile-btn") _image: "qrc:/qml/images/fontawesome/solid/user-plus.svg" _type: "button" } diff --git a/qml/widgets/StackToolbar.qml b/qml/widgets/StackToolbar.qml index c3b917c0..3b9f91e5 100644 --- a/qml/widgets/StackToolbar.qml +++ b/qml/widgets/StackToolbar.qml @@ -21,6 +21,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 +30,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 From d77851a8358d40dc8c259925e5fbc6013a609591 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 10 Dec 2019 15:44:35 -0800 Subject: [PATCH 21/28] profile delete. profile version tagging. minor qml refactor (less importing widgets directly into qml namespace) --- go/constants/attributes.go | 3 ++ go/handlers/appHandler.go | 27 ++++------- go/handlers/peerHandler.go | 6 +++ go/ui/gcd.go | 33 +++++++------ go/ui/manager.go | 13 ++++- qml.qrc | 2 - qml/overlays/BulletinOverlay.qml | 7 ++- qml/overlays/ListOverlay.qml | 4 +- qml/panes/AddGroupPane.qml | 10 ++-- qml/panes/GroupSettingsPane.qml | 22 ++++----- qml/panes/PeerSettingsPane.qml | 16 +++---- qml/panes/ProfileAddEditPane.qml | 82 ++++++++++++++++++++++---------- qml/panes/ProfileManagerPane.qml | 10 ++-- qml/widgets/ContactRow.qml | 17 +++++++ qml/widgets/ProfileList.qml | 8 +++- qml/widgets/RadioButton.qml | 27 +++++++++++ qml/widgets/SimpleButton.qml | 1 - qml/widgets/StackToolbar.qml | 1 - qml/widgets/TextField.qml | 16 +++++++ qml/widgets/controls/Button.qml | 81 ------------------------------- qml/widgets/controls/Text.qml | 61 ------------------------ 21 files changed, 204 insertions(+), 243 deletions(-) create mode 100644 qml/widgets/RadioButton.qml create mode 100644 qml/widgets/TextField.qml delete mode 100644 qml/widgets/controls/Button.qml delete mode 100644 qml/widgets/controls/Text.qml diff --git a/go/constants/attributes.go b/go/constants/attributes.go index 0868de16..7471c265 100644 --- a/go/constants/attributes.go +++ b/go/constants/attributes.go @@ -4,3 +4,6 @@ const Nick = "nick" const LastRead = "last-read" const Picture = "picture" const DefaultPassword = "default-password" + +const ProfileTypeV1DefaultPassword = "v1-defaultPassword" +const ProfileTypeV1Password = "v1-userPassword" diff --git a/go/handlers/appHandler.go b/go/handlers/appHandler.go index f58a137a..a2085a21 100644 --- a/go/handlers/appHandler.go +++ b/go/handlers/appHandler.go @@ -1,6 +1,7 @@ package handlers import ( + "cwtch.im/cwtch/app" "cwtch.im/cwtch/app/plugins" "cwtch.im/cwtch/event" "cwtch.im/ui/go/constants" @@ -12,7 +13,7 @@ import ( "time" ) -func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingFirst bool) { +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) @@ -21,7 +22,6 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingFirst bo the.AppBus.Subscribe(event.NetworkStatus, q) the.AppBus.Subscribe(event.ReloadDone, q) subscribed <- true - loadingV1Accounts := !reloadingFirst networkOffline := false timeSinceLastSuccess := time.Unix(0, 0) @@ -77,35 +77,24 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingFirst bo case event.AppError: if e.Data[event.Error] == event.AppErrLoaded0 { - - if loadingV1Accounts { - loadingV1Accounts = false - // TODO: only an error if other profiles are not loaded - if len(the.CwtchApp.ListPeers()) == 0 { - log.Infoln("couldn't load your config file. attempting to create one now") - if gcd.Version() == "development" { - the.CwtchApp.CreatePeer("tester", the.AppPassword) - } else { - the.CwtchApp.CreatePeer("alice", the.AppPassword) - } - } + if reloadingAccounts { + reloadingAccounts = false } else { gcd.ErrorLoaded0() } } case event.ReloadDone: - if the.Peer == nil { - loadingV1Accounts = true + 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 loadingV1Accounts { - the.CwtchApp.GetPeer(onion).SetAttribute(constants.DefaultPassword, "true") - loadingV1Accounts = false + if tag, exists := peer.GetAttribute(app.AttributeTag); !exists || tag == "" { + peer.SetAttribute(app.AttributeTag, constants.ProfileTypeV1DefaultPassword) } log.Infof("NewPeer for %v\n", onion) diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index cefe3d23..9bef4364 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -9,6 +9,8 @@ import ( "time" ) +const ExitPeerHandlerEvent = 10000 + func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { peer := the.CwtchApp.GetPeer(onion) eventBus := the.CwtchApp.GetEventBus(onion) @@ -105,6 +107,10 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { log.Errorf("found group that is nil :/") } } + case event.DeletePeer: + log.Infof("PeerHandler got DeletePeer, SHUTTING down!\n") + uiManager.ReloadProfiles() + return } } } diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 8e441618..a1c158ae 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -37,12 +37,11 @@ type GrandCentralDispatcher struct { _ string `property:"selectedConversation,auto"` // profile management stuff - _ func() `signal:"Loaded"` - _ func(handle, displayname, image string) `signal:"AddProfile"` - _ func() `signal:"ErrorLoaded0"` - _ func() `signal:"ResetProfile"` - _ func() `signal:"ResetProfileList"` - _ func(onion string) `signal:"deleteProfile"` + _ func() `signal:"Loaded"` + _ func(handle, displayname, image, tag string) `signal:"AddProfile"` + _ func() `signal:"ErrorLoaded0"` + _ func() `signal:"ResetProfile"` + _ func() `signal:"ResetProfileList"` // contact list stuff _ func(handle, displayName, image, server string, badge, status int, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"` @@ -78,11 +77,12 @@ type GrandCentralDispatcher struct { _ 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, password string) `signal:"createProfile,auto"` - _ func(password string) `signal:"unlockProfiles,auto"` - _ func() `signal:"reloadProfileList,auto"` + _ 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"` // operating a profile _ func(message string, mid string) `signal:"sendMessage,auto"` _ func(onion string) `signal:"blockPeer,auto"` @@ -619,8 +619,12 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) { } } -func (this *GrandCentralDispatcher) createProfile(nick, password string) { - the.CwtchApp.CreatePeer(nick, password) +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) reloadProfileList() { @@ -632,5 +636,6 @@ func (this *GrandCentralDispatcher) reloadProfileList() { } func (this *GrandCentralDispatcher) deleteProfile(onion string) { - //the.CwtchApp. + log.Infof("deleteProfile %v\n", onion) + the.CwtchApp.DeletePeer(onion) } diff --git a/go/ui/manager.go b/go/ui/manager.go index a23493dd..809bef72 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -1,6 +1,7 @@ package ui import ( + "cwtch.im/cwtch/app" "cwtch.im/cwtch/model" "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" @@ -122,8 +123,10 @@ func AddProfile(gcd *GrandCentralDispatcher, handle string) { pic = RandomProfileImage(handle) peer.SetAttribute(constants.Picture, pic) } - log.Infof("AddProfile %v %v %v\n", handle, nick, pic) - gcd.AddProfile(handle, nick, pic) + + tag, _ := peer.GetAttribute(app.AttributeTag) + log.Infof("AddProfile %v %v %v %v\n", handle, nick, pic, tag) + gcd.AddProfile(handle, nick, pic, tag) } } @@ -143,6 +146,8 @@ type Manager interface { 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) @@ -244,6 +249,10 @@ func (this *manager) AddMessage(handle string, from string, message string, from }) } +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() { diff --git a/qml.qrc b/qml.qrc index f7d266c6..6713ce4b 100644 --- a/qml.qrc +++ b/qml.qrc @@ -29,9 +29,7 @@ qml/widgets/ScalingLabel.qml qml/widgets/SimpleButton.qml qml/widgets/StackToolbar.qml - qml/widgets/controls/Button.qml qml/widgets/controls/Loader.qml - qml/widgets/controls/Text.qml qml/widgets/controls/Variables.qml i18n/translation_de.qm i18n/translation_en.qm diff --git a/qml/overlays/BulletinOverlay.qml b/qml/overlays/BulletinOverlay.qml index 71e32c8d..47a4a64a 100644 --- a/qml/overlays/BulletinOverlay.qml +++ b/qml/overlays/BulletinOverlay.qml @@ -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" @@ -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" diff --git a/qml/overlays/ListOverlay.qml b/qml/overlays/ListOverlay.qml index 40c8acb8..3bfeebbd 100644 --- a/qml/overlays/ListOverlay.qml +++ b/qml/overlays/ListOverlay.qml @@ -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 @@ -204,7 +204,7 @@ ColumnLayout { style: CwtchTextFieldStyle{} } - SimpleButton { // SEND MESSAGE BUTTON + Widgets.SimpleButton { // SEND MESSAGE BUTTON id: btnSend icon: "regular/paper-plane" text: "add" diff --git a/qml/panes/AddGroupPane.qml b/qml/panes/AddGroupPane.qml index 048e8aaa..4a0e45de 100644 --- a/qml/panes/AddGroupPane.qml +++ b/qml/panes/AddGroupPane.qml @@ -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") diff --git a/qml/panes/GroupSettingsPane.qml b/qml/panes/GroupSettingsPane.qml index 7616158d..bb0499af 100644 --- a/qml/panes/GroupSettingsPane.qml +++ b/qml/panes/GroupSettingsPane.qml @@ -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") diff --git a/qml/panes/PeerSettingsPane.qml b/qml/panes/PeerSettingsPane.qml index a48b8f02..3b7927e5 100644 --- a/qml/panes/PeerSettingsPane.qml +++ b/qml/panes/PeerSettingsPane.qml @@ -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") diff --git a/qml/panes/ProfileAddEditPane.qml b/qml/panes/ProfileAddEditPane.qml index 97e371ef..b1ea05fa 100644 --- a/qml/panes/ProfileAddEditPane.qml +++ b/qml/panes/ProfileAddEditPane.qml @@ -1,13 +1,13 @@ import QtGraphicalEffects 1.0 import QtQuick 2.7 -import QtQuick.Controls 2.4 +import QtQuick.Controls 2.13 import QtQuick.Controls.Material 2.0 import QtQuick.Layouts 1.3 import QtQuick.Window 2.11 -import QtQuick.Controls 1.4 -import "../widgets" -import "../styles" + +import "../widgets" as Widgets +// import "../styles" ColumnLayout { // Add Profile Pane id: profileAddEditPane @@ -15,7 +15,7 @@ ColumnLayout { // Add Profile Pane property string mode // edit or add - StackToolbar { + Widgets.StackToolbar { id: stb text: mode == "add" ? qsTr("add-profile-title") : qsTr("edit-profile-title") aux.visible: false @@ -28,6 +28,7 @@ ColumnLayout { // Add Profile Pane txtPassword1.text = "" txtPassword2.text = "" deleteReset() + radioUsePassword.checked = true } function load(onion, name, pass) { @@ -64,59 +65,90 @@ ColumnLayout { // Add Profile Pane spacing: 5 width: profileAddEditPane.width - ScalingLabel { + Widgets.ScalingLabel { //: Onion text: qsTr("profile-onion-label") + ":" visible: mode == "edit" } - ScalingLabel { + Widgets.ScalingLabel { id: onionLabel visible: mode == "edit" } - ScalingLabel { + Widgets.ScalingLabel { //: Display name text: qsTr("profile-name") + ":" } - TextField { + Widgets.TextField { id: txtProfileName Layout.fillWidth: true - style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } + //style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } //: default suggested profile name text: qsTr("default-profile-name") + + } - ScalingLabel { + RowLayout { + //id: radioButtons + + Widgets.RadioButton { + id: radioUsePassword + checked: true + //: Password + text: qsTr("radio-use-password") + } + + Widgets.RadioButton { + id: radioNoPassword + //: Unencrypted (No password) + text: qsTr("radio-no-password") + } + } + + 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: passwordLabel //: Password text: qsTr("password1-label") + ":" + visible: radioUsePassword.checked } - TextField { + Widgets.TextField { id: txtPassword1 Layout.fillWidth: true - style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } + //style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } echoMode: TextInput.Password readOnly: mode == "edit" + visible: radioUsePassword.checked } - ScalingLabel { + Widgets.ScalingLabel { + id: passwordReLabel //: Reenter password text: qsTr("password2-label") + ":" + visible: radioUsePassword.checked } - TextField { + Widgets.TextField { id: txtPassword2 Layout.fillWidth: true - style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } + //style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } echoMode: TextInput.Password readOnly: mode == "edit" - + visible: radioUsePassword.checked } - SimpleButton { + Widgets.SimpleButton { //: Create Profile || Save Profile text: mode == "add" ? qsTr("create-profile-btn") : qsTr("save-profile-btn") @@ -125,7 +157,7 @@ ColumnLayout { // Add Profile Pane passwordErrorLabel.visible = true } else { if (mode == "add") { - gcd.createProfile(txtProfileName.text, txtPassword1.text) + gcd.createProfile(txtProfileName.text, radioNoPassword .checked, txtPassword1.text) } else { gcd.updateNick(onionLabel.text, txtProfileName.text) } @@ -135,7 +167,7 @@ ColumnLayout { // Add Profile Pane } } - ScalingLabel { + Widgets.ScalingLabel { id: passwordErrorLabel //: Passwords do not match text: qsTr("password-error-match") @@ -143,7 +175,7 @@ ColumnLayout { // Add Profile Pane color: "red" } - SimpleButton { + Widgets.SimpleButton { //: Delete Profile text: qsTr("delete-profile-btn") icon: "regular/trash-alt" @@ -158,21 +190,21 @@ ColumnLayout { // Add Profile Pane } } - ScalingLabel { + Widgets.ScalingLabel { id: deleteConfirmLabel //: Type DELETE to confirm text: qsTr("delete-confirm-label")+ ":" visible: false } - TextField { + Widgets.TextField { id: confirmDeleteTxt Layout.fillWidth: true - style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } + //style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } visible: false } - SimpleButton { + Widgets.SimpleButton { id: confirmDeleteBtn icon: "regular/trash-alt" diff --git a/qml/panes/ProfileManagerPane.qml b/qml/panes/ProfileManagerPane.qml index cd0e1d0c..58c0be03 100644 --- a/qml/panes/ProfileManagerPane.qml +++ b/qml/panes/ProfileManagerPane.qml @@ -8,7 +8,7 @@ import QtQuick.Window 2.11 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 -import "../widgets" +import "../widgets" as Widgets import "../widgets/controls" import "../styles" @@ -19,7 +19,7 @@ ColumnLayout { //leftPadding: 10 //spacing: 5 - ScalingLabel { + Widgets.ScalingLabel { anchors.horizontalCenter: parent.horizontalCenter wrapMode: TextEdit.Wrap //: Please enter password: @@ -33,7 +33,7 @@ ColumnLayout { echoMode: TextInput.Password } - ScalingLabel { + Widgets.ScalingLabel { id: error anchors.horizontalCenter: parent.horizontalCenter color: "red" @@ -42,7 +42,7 @@ ColumnLayout { visible: false } - SimpleButton { + Widgets.SimpleButton { id: "button" anchors.horizontalCenter: parent.horizontalCenter @@ -75,7 +75,7 @@ ColumnLayout { Layout.minimumWidth: Layout.maximumWidth //Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : 450 - ProfileList { + Widgets.ProfileList { anchors.fill: parent } } diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 6e619d09..50700e51 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -25,6 +25,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY property bool isHover property bool background: true property string type // profile or contact or button + property string tag // profile version/type // Profile property bool defaultPassword @@ -114,6 +115,22 @@ 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 diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml index f039416f..e435a65b 100644 --- a/qml/widgets/ProfileList.qml +++ b/qml/widgets/ProfileList.qml @@ -41,7 +41,7 @@ ColumnLayout { Connections { // ADD/REMOVE CONTACT ENTRIES target: gcd - onAddProfile: function(handle, displayName, image) { + onAddProfile: function(handle, displayName, image, tag) { console.log("ProfileList onAddProfile for: " + handle) for (var i = 0; i < profilesModel.count; i++) { @@ -55,7 +55,8 @@ ColumnLayout { "_handle": handle, "_displayName": displayName, "_image": image, - "_type": "profile" + "_type": "profile", + "_tag": tag }) } @@ -77,6 +78,7 @@ ColumnLayout { _displayName: qsTr("add-new-profile-btn"), _image: "qrc:/qml/images/fontawesome/solid/user-plus.svg", _type: "button", + _tag: "," }) } } @@ -89,6 +91,7 @@ ColumnLayout { _displayName: qsTr("add-new-profile-btn") _image: "qrc:/qml/images/fontawesome/solid/user-plus.svg" _type: "button" + _tag: "" } } @@ -104,6 +107,7 @@ ColumnLayout { blocked: false loading: false type: _type + tag: _tag } } } diff --git a/qml/widgets/RadioButton.qml b/qml/widgets/RadioButton.qml new file mode 100644 index 00000000..187822e6 --- /dev/null +++ b/qml/widgets/RadioButton.qml @@ -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 + } + } +} \ No newline at end of file diff --git a/qml/widgets/SimpleButton.qml b/qml/widgets/SimpleButton.qml index 2b3b67e8..25c6736d 100644 --- a/qml/widgets/SimpleButton.qml +++ b/qml/widgets/SimpleButton.qml @@ -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 { diff --git a/qml/widgets/StackToolbar.qml b/qml/widgets/StackToolbar.qml index 3b9f91e5..05ddc52e 100644 --- a/qml/widgets/StackToolbar.qml +++ b/qml/widgets/StackToolbar.qml @@ -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 diff --git a/qml/widgets/TextField.qml b/qml/widgets/TextField.qml new file mode 100644 index 00000000..6486f9d0 --- /dev/null +++ b/qml/widgets/TextField.qml @@ -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 + } +} \ No newline at end of file diff --git a/qml/widgets/controls/Button.qml b/qml/widgets/controls/Button.qml deleted file mode 100644 index 1c120a50..00000000 --- a/qml/widgets/controls/Button.qml +++ /dev/null @@ -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 - } - } - } - } -} diff --git a/qml/widgets/controls/Text.qml b/qml/widgets/controls/Text.qml deleted file mode 100644 index 8931eb17..00000000 --- a/qml/widgets/controls/Text.qml +++ /dev/null @@ -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 - } - } -} From 13239c094c1beb58b5b267ac1e56b34aab6040d3 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 17 Dec 2019 11:32:58 -0800 Subject: [PATCH 22/28] change password --- go/handlers/peerHandler.go | 11 +++ go/ui/gcd.go | 29 +++++-- go/ui/manager.go | 6 ++ qml/panes/ProfileAddEditPane.qml | 131 +++++++++++++++++++++++-------- qml/panes/ProfileManagerPane.qml | 1 + qml/widgets/ContactRow.qml | 4 +- 6 files changed, 140 insertions(+), 42 deletions(-) diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index 9bef4364..8838533d 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -1,8 +1,10 @@ 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" @@ -25,6 +27,8 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { 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 @@ -111,6 +115,13 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { log.Infof("PeerHandler got DeletePeer, SHUTTING down!\n") uiManager.ReloadProfiles() return + case event.ChangePasswordSuccess: + log.Infoln("ChangePAsswordSuccess!!!") + peer.SetAttribute(app.AttributeTag, constants.ProfileTypeV1Password) + uiManager.ChangePasswordResponse(false) + case event.ChangePasswordError: + uiManager.ChangePasswordResponse(true) } + } } diff --git a/go/ui/gcd.go b/go/ui/gcd.go index a1c158ae..445e6e7d 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -1,6 +1,7 @@ package ui import ( + "cwtch.im/cwtch/app" "cwtch.im/cwtch/event" "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" @@ -42,6 +43,7 @@ type GrandCentralDispatcher struct { _ 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, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"` @@ -77,12 +79,13 @@ type GrandCentralDispatcher struct { _ 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, 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"` @@ -627,6 +630,20 @@ func (this *GrandCentralDispatcher) createProfile(nick string, defaultPass bool, } } +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() diff --git a/go/ui/manager.go b/go/ui/manager.go index 809bef72..89ebc9ba 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -151,6 +151,8 @@ type Manager interface { 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 @@ -273,3 +275,7 @@ func (this *manager) UpdateContactAttribute(handle, key, value string) { this.gcd.UpdateContactAttribute(handle, key, value) }) } + +func (this *manager) ChangePasswordResponse(error bool) { + this.gcd.ChangePasswordResponse(error) +} diff --git a/qml/panes/ProfileAddEditPane.qml b/qml/panes/ProfileAddEditPane.qml index b1ea05fa..5285fc00 100644 --- a/qml/panes/ProfileAddEditPane.qml +++ b/qml/panes/ProfileAddEditPane.qml @@ -14,6 +14,10 @@ ColumnLayout { // Add Profile Pane 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 @@ -24,30 +28,37 @@ ColumnLayout { // Add Profile Pane } function reset() { + mode = "add" txtProfileName.text = qsTr("default-profile-name") + changingPassword = false txtPassword1.text = "" txtPassword2.text = "" - deleteReset() + deleting = false + deleteConfirmLabel.color = "black" + tag = "" + confirmDeleteTxt.text = "" radioUsePassword.checked = true + passwordChangeErrorLabel.visible = false } - function load(onion, name, pass) { + function load(userOnion, name, userTag) { + reset() + + mode = "edit" + tag = userTag + onion = userOnion + txtPassword1.text = "" + txtPassword2.text = "" onionLabel.text = onion txtProfileName.text = name - txtPassword1.text = pass - txtPassword2.text = pass - deleteReset() + if (tag == "v1-defaultPassword" || tag == "v1-default-password") { + radioNoPassword.checked = true + } else { + radioUsePassword.checked = true + } } - function deleteReset() { - deleteConfirmLabel.visible = false - deleteConfirmLabel.visible = false - deleteConfirmLabel.color = "black" - confirmDeleteTxt.visible = false - confirmDeleteTxt.text = "" - confirmDeleteBtn.visible = false - } Flickable { anchors.top: stb.bottom @@ -99,12 +110,20 @@ ColumnLayout { // Add Profile Pane 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 + } } } @@ -115,6 +134,20 @@ ColumnLayout { // Add Profile Pane 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 @@ -127,8 +160,11 @@ ColumnLayout { // Add Profile Pane Layout.fillWidth: true //style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } echoMode: TextInput.Password - readOnly: mode == "edit" visible: radioUsePassword.checked + + onTextEdited: { + changingPassword = true + } } @@ -144,28 +180,36 @@ ColumnLayout { // Add Profile Pane Layout.fillWidth: true //style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } echoMode: TextInput.Password - readOnly: mode == "edit" visible: radioUsePassword.checked } - Widgets.SimpleButton { + Widgets.SimpleButton { // ADD or SAVE button //: Create Profile || Save Profile text: mode == "add" ? qsTr("create-profile-btn") : qsTr("save-profile-btn") onClicked: { - if (txtPassword1.text != txtPassword2.text) { - passwordErrorLabel.visible = true + if (mode == "add") { + if (txtPassword1.text != txtPassword2.text) { + passwordErrorLabel.visible = true + } else { + gcd.createProfile(txtProfileName.text, radioNoPassword.checked, txtPassword1.text) + } } else { - if (mode == "add") { - gcd.createProfile(txtProfileName.text, radioNoPassword .checked, txtPassword1.text) - } else { - gcd.updateNick(onionLabel.text, txtProfileName.text) + 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) + return + } } - gcd.reloadProfileList() - parentStack.pane = parentStack.managementPane } + gcd.reloadProfileList() + parentStack.pane = parentStack.managementPane } - } + } Widgets.ScalingLabel { id: passwordErrorLabel @@ -175,6 +219,16 @@ ColumnLayout { // Add Profile Pane 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") @@ -183,10 +237,7 @@ ColumnLayout { // Add Profile Pane onClicked: { - deleteConfirmLabel.visible = true - deleteConfirmLabel.visible = true - confirmDeleteTxt.visible = true - confirmDeleteBtn.visible = true + deleting = true } } @@ -194,14 +245,14 @@ ColumnLayout { // Add Profile Pane id: deleteConfirmLabel //: Type DELETE to confirm text: qsTr("delete-confirm-label")+ ":" - visible: false + visible: deleting } Widgets.TextField { id: confirmDeleteTxt Layout.fillWidth: true //style: CwtchTextFieldStyle{ width: tehcol.width * 0.8 } - visible: false + visible: deleting } Widgets.SimpleButton { @@ -211,12 +262,13 @@ ColumnLayout { // Add Profile Pane //: Really Delete Profile text: qsTr("delete-profile-confirm-btn") color: "red" - visible: false + visible: deleting onClicked: { //: DELETE if (confirmDeleteTxt.text == qsTr("delete-confirm-text")) { - gcd.deleteProfile(onionLabel.text) + deleteConfirmLabel.color = "black" + gcd.deleteProfile(onion) gcd.reloadProfileList() parentStack.pane = parentStack.managementPane } else { @@ -229,4 +281,17 @@ ColumnLayout { // Add Profile Pane }//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 + } + } + } } \ No newline at end of file diff --git a/qml/panes/ProfileManagerPane.qml b/qml/panes/ProfileManagerPane.qml index 58c0be03..c288b4a4 100644 --- a/qml/panes/ProfileManagerPane.qml +++ b/qml/panes/ProfileManagerPane.qml @@ -31,6 +31,7 @@ ColumnLayout { anchors.horizontalCenter: parent.horizontalCenter style: CwtchTextFieldStyle{ width: thecol.width * 0.8 } echoMode: TextInput.Password + onAccepted: button.clicked() } Widgets.ScalingLabel { diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 50700e51..f74d838d 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -183,7 +183,6 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY gcd.loadProfile(handle) parentStack.pane = parentStack.profilePane } else if (type == "button") { // Add profile button - profileAddEditPane.mode = "add" profileAddEditPane.reset() parentStack.pane = parentStack.addEditProfilePane } @@ -214,8 +213,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY onClicked: { - profileAddEditPane.mode = "edit" - profileAddEditPane.load(handle, displayName, "") + profileAddEditPane.load(handle, displayName, tag) parentStack.pane = parentStack.addEditProfilePane } } From 7e0945583ec43c59d785ab5a722c1774497c8060 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 17 Dec 2019 13:33:38 -0800 Subject: [PATCH 23/28] go mod cwtch to 0.3.7; add profile list sorting by onion --- go.mod | 2 +- go.sum | 7 +++++++ go/handlers/peerHandler.go | 2 -- qml/widgets/ProfileList.qml | 12 +++++++++++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 8e577562..d3f1c870 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.3.6 + cwtch.im/cwtch v0.3.7 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 diff --git a/go.sum b/go.sum index fd141d81..803bb95d 100644 --- a/go.sum +++ b/go.sum @@ -12,10 +12,13 @@ cwtch.im/cwtch v0.3.5 h1:L4hBYMRUSADodBz3gwIrYGXZf2V7HzTlzLUku9N3O9A= cwtch.im/cwtch v0.3.5/go.mod h1:I95rbE3aK8uic7LsMOB1lfJDSzlNsRUP0/5cFCLkD0Y= cwtch.im/cwtch v0.3.6 h1:a+BhoTpdlH5yI335HQDB7EEWZJqvNdoZnnjYLYne52Y= cwtch.im/cwtch v0.3.6/go.mod h1:NU6Pm4RgHm97b03U83W7/NabmRvBxHQMxM8/kZnH12Y= +cwtch.im/cwtch v0.3.7 h1:kcNQqxQLT03DKbkyXJGMmoROLcIGlsjw8++gNVq2Dxg= +cwtch.im/cwtch v0.3.7/go.mod h1:wbhUXmJohgHOLx0pPx96LqsDK89PL6Ai+ABL8YC9ZC4= cwtch.im/tapir v0.1.10 h1:V+TkmwXNd6gySZqlVw468wMYEkmDwMSyvhkkpOfUw7w= cwtch.im/tapir v0.1.10/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= cwtch.im/tapir v0.1.11 h1:JLm1MIYq4VXKzhj68+P8OuVPllAU9U6G0DtUor2fbc4= cwtch.im/tapir v0.1.11/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= +cwtch.im/tapir v0.1.13/go.mod h1:1fu4d+cMCepVaMm5vHrp4N/romdjKR+R6P8cvdRXYtQ= 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= @@ -42,6 +45,8 @@ github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORR 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/gordonklaus/ineffassign v0.0.0-20190601041439-ed7b1b5ee0f8/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= +github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= +github.com/gtank/ristretto255 v0.1.1/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= @@ -53,6 +58,7 @@ 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/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= @@ -78,6 +84,7 @@ github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20191101232336 github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20191101232336-18864661ae4f/go.mod h1:7m8PDYDEtEVqfjoUQc2UrFqhG0CDmoVJjRlQxexndFc= github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 h1:SC92QLlGwzwrT3Xi5YzKvtVXFIriPP0Ui1AOIZgDyh8= 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/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= diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index 8838533d..bec562cb 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -11,8 +11,6 @@ import ( "time" ) -const ExitPeerHandlerEvent = 10000 - func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { peer := the.CwtchApp.GetPeer(onion) eventBus := the.CwtchApp.GetEventBus(onion) diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml index e435a65b..b7c1cc57 100644 --- a/qml/widgets/ProfileList.qml +++ b/qml/widgets/ProfileList.qml @@ -43,6 +43,7 @@ ColumnLayout { 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) { @@ -50,7 +51,16 @@ ColumnLayout { } } - profilesModel.insert(profilesModel.count-1, + // 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, From aa81c40e509216bf2afbfffcb1b1cda1291d2b10 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 17 Dec 2019 13:59:33 -0800 Subject: [PATCH 24/28] tranlations for edit and delete profiles --- go.sum | 5 +++ go/handlers/peerHandler.go | 1 - i18n/translation_de.ts | 76 +++++++++++++++++++++++++----------- i18n/translation_en.qm | Bin 6217 -> 6951 bytes i18n/translation_en.ts | 78 +++++++++++++++++++++++++------------ i18n/translation_fr.ts | 76 +++++++++++++++++++++++++----------- i18n/translation_pt.ts | 76 +++++++++++++++++++++++++----------- 7 files changed, 218 insertions(+), 94 deletions(-) diff --git a/go.sum b/go.sum index 803bb95d..6c5bef65 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,7 @@ cwtch.im/tapir v0.1.10 h1:V+TkmwXNd6gySZqlVw468wMYEkmDwMSyvhkkpOfUw7w= cwtch.im/tapir v0.1.10/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= cwtch.im/tapir v0.1.11 h1:JLm1MIYq4VXKzhj68+P8OuVPllAU9U6G0DtUor2fbc4= cwtch.im/tapir v0.1.11/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= +cwtch.im/tapir v0.1.13 h1:gGAtFyRnxLrs1SQRAeVXiO5MG2DUUTE621aTQQEJN7U= cwtch.im/tapir v0.1.13/go.mod h1:1fu4d+cMCepVaMm5vHrp4N/romdjKR+R6P8cvdRXYtQ= 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= @@ -45,7 +46,9 @@ github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORR 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/gordonklaus/ineffassign v0.0.0-20190601041439-ed7b1b5ee0f8/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= +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.1 h1:A+VVUhf73TS5HRfCnfMBqTBujkbwY3Fo8sRSFvL3cIg= github.com/gtank/ristretto255 v0.1.1/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= @@ -58,6 +61,7 @@ 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= @@ -84,6 +88,7 @@ github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20191101232336 github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20191101232336-18864661ae4f/go.mod h1:7m8PDYDEtEVqfjoUQc2UrFqhG0CDmoVJjRlQxexndFc= github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 h1:SC92QLlGwzwrT3Xi5YzKvtVXFIriPP0Ui1AOIZgDyh8= 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= diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index bec562cb..e64e6005 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -114,7 +114,6 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { uiManager.ReloadProfiles() return case event.ChangePasswordSuccess: - log.Infoln("ChangePAsswordSuccess!!!") peer.SetAttribute(app.AttributeTag, constants.ProfileTypeV1Password) uiManager.ChangePasswordResponse(false) case event.ChangePasswordError: diff --git a/i18n/translation_de.ts b/i18n/translation_de.ts index 47ebb17e..eb2209f8 100644 --- a/i18n/translation_de.ts +++ b/i18n/translation_de.ts @@ -36,18 +36,18 @@ BulletinOverlay - + new-bulletin-label Neue Meldung - + post-new-bulletin-label Post a new Bulletin Post Neue Meldung veröffentlichen - + title-placeholder title place holder text Titel... @@ -276,83 +276,113 @@ ProfileAddEditPane - + add-profile-title - + edit-profile-title - + profile-name - Profile name + Display name - - + + default-profile-name default suggested profile name - + profile-onion-label - Profile Onion + Onion - + + radio-use-password + Password + + + + + radio-no-password + Unencrypted (No password) + + + + + no-password-warning + Not using a password on this account means that all data stored locally will not be encrypted + + + + + current-password-label + Current Password + + + + password1-label Password - + password2-label Reenter password - + create-profile-btn Create Profile || Save Profile - + save-profile-btn - + password-error-match Passwords do not match - + + password-change-error + Error changing password: Supplied password rejected + + + + delete-profile-btn Delete Profile - + delete-confirm-label Type DELETE to confirm - + delete-profile-confirm-btn Really Delete Profile - + delete-confirm-text DELETE @@ -367,13 +397,13 @@ - + error-0-profiles-loaded-for-password 0 profiles loaded with that password - + unlock Unlock @@ -424,7 +454,7 @@ StackToolbar - + view-group-membership-tooltip View Group Membership diff --git a/i18n/translation_en.qm b/i18n/translation_en.qm index f14e39c389862449728bb205aa54c4b2486ef7e1..318e95517e94671d31babdbfeccdfa74919e67af 100644 GIT binary patch delta 872 zcmZWmT}TvB7(K3TyREzK?(FPpDlrJ!4{U_sLP0&45r~Q!J(LuTv$MLk&B%<~wl5h$ z5s1+?L=S&(W0r&i!TR>U2`(^G0JH-m`6OEyO=VXde;J4>`R0Dh-&$Ff*K=iz~ zx>f|lOWtG0e*m$lw_luMyvN#ClBg4Ud=JiE1~#|&W?OmfJMDY!qm+u9zSX&9O1fx` zmTc{frOKCurwmv8;+xL^UKe-Qj6dFWpBlRS6W7?rl`ro8Hvh~@6%hXJfAW*6VoQOb z`4|XJ1muzZr1=sU8UF-?P6S?kaFedb`c;ys47LaTADda$9h^+H(eikj)0mxZcDX9T z5)L2>gG+^mAxPFeVM{osWDP^rhT?;=Y36hzX)T88rdvX#uHlF1fNld>W+Kg^KAvTM zgZ!IZIpz%{d80Ec5!+9k>7W!Syv-J=aEhcd&vwp6$xdGx87_$(?AY#Xa<_Y`NFHUF zVW2Q!!}5%CY+o$Vn6ahx5-K^46qe;FZOAV6a>m%iFQVz|*5WzY(9&9;rH5;WQ#i;1 z!(o$1Vr#M;uKC>PaW82O)6XDRhA5KoW+O^+v=1<=Y!DJ%8*p6dm$g2R8dnWNH*77| zA>poDLN`=vn^_fC;t18)5A}~NGH!QX<$vZK>z2cYoJ{NSzl=L1Q$lq_D5kQe Ox;_*;H@y%!SM~>qs@C8D delta 291 zcmZ2(cG6&ih@b|8*zFt!2If``945G{vRn?gIR!#KP6B4pyntWpo1B0MFbHyY9 zpxlg!y=q#*tSq08onv5Ntz?y!Hf3NC_`*6bU>*YlcNXih6-yZySb|tDmY-o@5J{ML z*G!9l1N(H*JO&0XcJ`yDvl$qK&TvS{hycyJ#1R$ym4QJxo+CqK9gyEV*_F|ZNg!i# zEu%Q2^W?dVazVlyctozhVPIgm3Za>{^5_O`0-8LVXVKI?1_q`|2+iZev-N`n&?SsK z2mgR|;9ryG15~~)lYv2uVRIG} d6W`{O0?bU4lSI}|juVrbY$>{X^K!8U767PZPwfBz diff --git a/i18n/translation_en.ts b/i18n/translation_en.ts index 575e96fb..19d7121d 100644 --- a/i18n/translation_en.ts +++ b/i18n/translation_en.ts @@ -36,18 +36,18 @@ BulletinOverlay - + new-bulletin-label New Bulletin - + post-new-bulletin-label Post a new Bulletin Post Post new bulletin - + title-placeholder title place holder text title... @@ -276,83 +276,113 @@ ProfileAddEditPane - + add-profile-title Add new profile - + edit-profile-title Edit Profile - + profile-name - Profile name + Display name Display name - - + + default-profile-name default suggested profile name Alice - + profile-onion-label - Profile Onion + Onion Onion - + + radio-use-password + Password + Password + + + + radio-no-password + Unencrypted (No password) + Unencrypted (No password) + + + + no-password-warning + Not using a password on this account means that all data stored locally will not be encrypted + Not using a password on this account means that all data stored locally will not be encrypted + + + + current-password-label + Current Password + Current Password + + + password1-label Password Password - + password2-label Reenter password - Password + Reenter password - + create-profile-btn Create Profile || Save Profile Create Profile - + save-profile-btn Save Profile - + password-error-match Passwords do not match Passwords do not match - + + password-change-error + Error changing password: Supplied password rejected + Error changing password: Supplied password rejected + + + delete-profile-btn Delete Profile Delete Profile - + delete-confirm-label Type DELETE to confirm Type DELETE to confirm - + delete-profile-confirm-btn Really Delete Profile Really Delete Profile - + delete-confirm-text DELETE DELETE @@ -367,13 +397,13 @@ Please enter password - + error-0-profiles-loaded-for-password 0 profiles loaded with that password 0 profiles loaded with that password - + unlock Unlock Unlock @@ -424,7 +454,7 @@ StackToolbar - + view-group-membership-tooltip View Group Membership View Group Membership diff --git a/i18n/translation_fr.ts b/i18n/translation_fr.ts index 157be8fa..62b0b932 100644 --- a/i18n/translation_fr.ts +++ b/i18n/translation_fr.ts @@ -36,18 +36,18 @@ BulletinOverlay - + new-bulletin-label Nouveau bulletin - + post-new-bulletin-label Post a new Bulletin Post Envoyer un nouveau bulletin - + title-placeholder title place holder text titre... @@ -276,83 +276,113 @@ ProfileAddEditPane - + add-profile-title - + edit-profile-title - + profile-name - Profile name + Display name - - + + default-profile-name default suggested profile name - + profile-onion-label - Profile Onion + Onion - + + radio-use-password + Password + + + + + radio-no-password + Unencrypted (No password) + + + + + no-password-warning + Not using a password on this account means that all data stored locally will not be encrypted + + + + + current-password-label + Current Password + + + + password1-label Password - + password2-label Reenter password - + create-profile-btn Create Profile || Save Profile - + save-profile-btn - + password-error-match Passwords do not match - + + password-change-error + Error changing password: Supplied password rejected + + + + delete-profile-btn Delete Profile - + delete-confirm-label Type DELETE to confirm - + delete-profile-confirm-btn Really Delete Profile - + delete-confirm-text DELETE @@ -367,13 +397,13 @@ - + error-0-profiles-loaded-for-password 0 profiles loaded with that password - + unlock Unlock @@ -424,7 +454,7 @@ StackToolbar - + view-group-membership-tooltip View Group Membership diff --git a/i18n/translation_pt.ts b/i18n/translation_pt.ts index f7ab7e7a..f12bea3e 100644 --- a/i18n/translation_pt.ts +++ b/i18n/translation_pt.ts @@ -36,18 +36,18 @@ BulletinOverlay - + new-bulletin-label Novo Boletim - + post-new-bulletin-label Post a new Bulletin Post Postar novo boletim - + title-placeholder title place holder text título… @@ -276,83 +276,113 @@ ProfileAddEditPane - + add-profile-title - + edit-profile-title - + profile-name - Profile name + Display name - - + + default-profile-name default suggested profile name - + profile-onion-label - Profile Onion + Onion - + + radio-use-password + Password + + + + + radio-no-password + Unencrypted (No password) + + + + + no-password-warning + Not using a password on this account means that all data stored locally will not be encrypted + + + + + current-password-label + Current Password + + + + password1-label Password - + password2-label Reenter password - + create-profile-btn Create Profile || Save Profile - + save-profile-btn - + password-error-match Passwords do not match - + + password-change-error + Error changing password: Supplied password rejected + + + + delete-profile-btn Delete Profile - + delete-confirm-label Type DELETE to confirm - + delete-profile-confirm-btn Really Delete Profile - + delete-confirm-text DELETE @@ -367,13 +397,13 @@ - + error-0-profiles-loaded-for-password 0 profiles loaded with that password - + unlock Unlock @@ -424,7 +454,7 @@ StackToolbar - + view-group-membership-tooltip View Group Membership From a9041c648530937506c888c6802a931749091f61 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 17 Dec 2019 14:31:13 -0800 Subject: [PATCH 25/28] minor translations updates --- i18n/translation_de.ts | 9 +++++++++ i18n/translation_en.qm | Bin 6951 -> 7040 bytes i18n/translation_en.ts | 11 ++++++++++- i18n/translation_fr.ts | 9 +++++++++ i18n/translation_pt.ts | 9 +++++++++ qml.qrc | 5 ++++- 6 files changed, 41 insertions(+), 2 deletions(-) diff --git a/i18n/translation_de.ts b/i18n/translation_de.ts index eb2209f8..cd9416c4 100644 --- a/i18n/translation_de.ts +++ b/i18n/translation_de.ts @@ -388,6 +388,15 @@ + + ProfileList + + + + add-new-profile-btn + + + ProfileManagerPane diff --git a/i18n/translation_en.qm b/i18n/translation_en.qm index 318e95517e94671d31babdbfeccdfa74919e67af..41cfb9e30d3773c6d640991c944beaab4dcf32d3 100644 GIT binary patch delta 184 zcmZ2()?hwCL@~;*~tDz+lV7$+nJx zfg_d4MCb|wgQx@3Z`fqYX z#eQXA5KEhE%UI92eezyLdB#PP?=s3UiR(=M%b3k1$-X&@sYhh;L4FMt;lz{_-MrLt g-GZY0w9K4T-K3H{cA(|l0T8}VW^u{pk78yl0952PegFUf delta 160 zcmZoLUv4%*L@~; password2-label Reenter password - Reenter password + Reenter password @@ -388,6 +388,15 @@ DELETE + + ProfileList + + + + add-new-profile-btn + Add new profile + + ProfileManagerPane diff --git a/i18n/translation_fr.ts b/i18n/translation_fr.ts index 62b0b932..dd42812f 100644 --- a/i18n/translation_fr.ts +++ b/i18n/translation_fr.ts @@ -388,6 +388,15 @@ + + ProfileList + + + + add-new-profile-btn + + + ProfileManagerPane diff --git a/i18n/translation_pt.ts b/i18n/translation_pt.ts index f12bea3e..aa042b14 100644 --- a/i18n/translation_pt.ts +++ b/i18n/translation_pt.ts @@ -388,6 +388,15 @@ + + ProfileList + + + + add-new-profile-btn + + + ProfileManagerPane diff --git a/qml.qrc b/qml.qrc index 6713ce4b..6d290767 100644 --- a/qml.qrc +++ b/qml.qrc @@ -25,10 +25,13 @@ qml/widgets/FontAwesome.qml qml/widgets/InplaceEditText.qml qml/widgets/Message.qml - qml/widgets/MyProfile.qml qml/widgets/ScalingLabel.qml + qml/widgets/MyProfile.qml + qml/widgets/ProfileList.qml + qml/widgets/RadioButton.qml qml/widgets/SimpleButton.qml qml/widgets/StackToolbar.qml + qml/widgets/TextField.qml qml/widgets/controls/Loader.qml qml/widgets/controls/Variables.qml i18n/translation_de.qm From 8d9681e130218391026fe3c5c35c64675e8f24a8 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 17 Dec 2019 15:12:35 -0800 Subject: [PATCH 26/28] upgrade cwtch 0.3.8 (tapir 0.1.14) --- go.mod | 5 ++--- go.sum | 55 ++++++------------------------------------------------- 2 files changed, 8 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index d3f1c870..cf7a97d0 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,13 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.3.7 + 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-20191101232336-18864661ae4f - github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20191101232336-18864661ae4f // indirect - github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 // indirect + github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 // 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 diff --git a/go.sum b/go.sum index 6c5bef65..c623810b 100644 --- a/go.sum +++ b/go.sum @@ -1,55 +1,28 @@ -cwtch.im/cwtch v0.3.0 h1:RFZyc2m9BowFNdngBs7GcQE41w75jMp3Ku5zEE92v9I= -cwtch.im/cwtch v0.3.0/go.mod h1:8tmtp3c7fccWw9H7s9u6E8GD2PKI3ar21i0fjN8pzd0= -cwtch.im/cwtch v0.3.1 h1:C0DLIrOqpNs5aecKTjNJZhpMq4/EvWNmLiKklIS8RTM= -cwtch.im/cwtch v0.3.1/go.mod h1:8tmtp3c7fccWw9H7s9u6E8GD2PKI3ar21i0fjN8pzd0= -cwtch.im/cwtch v0.3.2 h1:JxoauToMckHjmQz3QCmI7XG9pun1tF3pV/o5ziuqV1A= -cwtch.im/cwtch v0.3.2/go.mod h1:4b2qGW5bZKm4CwYxqc0+4pgpDU0LjjyoihC8a/ezOoQ= -cwtch.im/cwtch v0.3.3 h1:mAypnkTCehej5ebSEzl43nPufsyyXLNz/dw2RWOO+Wk= -cwtch.im/cwtch v0.3.3/go.mod h1:I95rbE3aK8uic7LsMOB1lfJDSzlNsRUP0/5cFCLkD0Y= -cwtch.im/cwtch v0.3.4 h1:EZzDE5kBvkWsNrlPg4+uQ0LKp5AMLfXr7cqXFUgnCc4= -cwtch.im/cwtch v0.3.4/go.mod h1:I95rbE3aK8uic7LsMOB1lfJDSzlNsRUP0/5cFCLkD0Y= -cwtch.im/cwtch v0.3.5 h1:L4hBYMRUSADodBz3gwIrYGXZf2V7HzTlzLUku9N3O9A= -cwtch.im/cwtch v0.3.5/go.mod h1:I95rbE3aK8uic7LsMOB1lfJDSzlNsRUP0/5cFCLkD0Y= -cwtch.im/cwtch v0.3.6 h1:a+BhoTpdlH5yI335HQDB7EEWZJqvNdoZnnjYLYne52Y= -cwtch.im/cwtch v0.3.6/go.mod h1:NU6Pm4RgHm97b03U83W7/NabmRvBxHQMxM8/kZnH12Y= -cwtch.im/cwtch v0.3.7 h1:kcNQqxQLT03DKbkyXJGMmoROLcIGlsjw8++gNVq2Dxg= -cwtch.im/cwtch v0.3.7/go.mod h1:wbhUXmJohgHOLx0pPx96LqsDK89PL6Ai+ABL8YC9ZC4= -cwtch.im/tapir v0.1.10 h1:V+TkmwXNd6gySZqlVw468wMYEkmDwMSyvhkkpOfUw7w= -cwtch.im/tapir v0.1.10/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= -cwtch.im/tapir v0.1.11 h1:JLm1MIYq4VXKzhj68+P8OuVPllAU9U6G0DtUor2fbc4= -cwtch.im/tapir v0.1.11/go.mod h1:EuRYdVrwijeaGBQ4OijDDRHf7R2MDSypqHkSl5DxI34= -cwtch.im/tapir v0.1.13 h1:gGAtFyRnxLrs1SQRAeVXiO5MG2DUUTE621aTQQEJN7U= -cwtch.im/tapir v0.1.13/go.mod h1:1fu4d+cMCepVaMm5vHrp4N/romdjKR+R6P8cvdRXYtQ= +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.7 h1:yOD87eeuIFVY/2dsYzduSotE2eYOVd2pks4i7dKw+Ng= -git.openprivacy.ca/openprivacy/libricochet-go v1.0.7/go.mod h1:6I+vO9Aagv3/yUWv+e7A57H8tgXgR67FCjfSj9Pp970= 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/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= 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= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cretz/bine v0.1.0 h1:1/fvhLE+fk0bPzjdO5Ci+0ComYxEMuB1JhM4X5skT3g= github.com/cretz/bine v0.1.0/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dvyukov/go-fuzz v0.0.0-20191022152526-8cb203812681/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= -github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 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/gordonklaus/ineffassign v0.0.0-20190601041439-ed7b1b5ee0f8/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= 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.1 h1:A+VVUhf73TS5HRfCnfMBqTBujkbwY3Fo8sRSFvL3cIg= -github.com/gtank/ristretto255 v0.1.1/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o= +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= @@ -66,10 +39,8 @@ github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjK 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= -github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -78,15 +49,8 @@ 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-20191002095216-73192f6811d0 h1:Y4I5RGVEZubcplYny6zwqg0G7WUC6gw+esy+2cPCruA= -github.com/therecipe/qt v0.0.0-20191002095216-73192f6811d0/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us= 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.12.0 v0.0.0-20191101232336-18864661ae4f h1:035UB1g+zTNL+3BAKLLy8XwYzBL0dM+HwuDELYOoRoY= -github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20191101232336-18864661ae4f/go.mod h1:7m8PDYDEtEVqfjoUQc2UrFqhG0CDmoVJjRlQxexndFc= -github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 h1:SC92QLlGwzwrT3Xi5YzKvtVXFIriPP0Ui1AOIZgDyh8= 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= @@ -97,12 +61,10 @@ golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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= @@ -117,12 +79,7 @@ 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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191031220737-6d8f1af9ccc0 h1:+o3suEKE/4hCUt6qjV8SDcVZhz2dO8UWlHliCa+4bvg= -golang.org/x/tools v0.0.0-20191031220737-6d8f1af9ccc0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108175616-46f5a7f28bf0/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 9397131967005beeccd12174f50b5ca0a7d4b997 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 17 Dec 2019 17:18:16 -0800 Subject: [PATCH 27/28] fix password not match error; fix clearing add/edit pane on reset --- qml/panes/ProfileAddEditPane.qml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qml/panes/ProfileAddEditPane.qml b/qml/panes/ProfileAddEditPane.qml index 5285fc00..b0c3ef85 100644 --- a/qml/panes/ProfileAddEditPane.qml +++ b/qml/panes/ProfileAddEditPane.qml @@ -35,6 +35,9 @@ ColumnLayout { // Add Profile Pane txtPassword2.text = "" deleting = false deleteConfirmLabel.color = "black" + passwordErrorLabel.visible = false + txtCurrentPassword.text = "" + tag = "" confirmDeleteTxt.text = "" radioUsePassword.checked = true @@ -193,6 +196,8 @@ ColumnLayout { // Add Profile Pane passwordErrorLabel.visible = true } else { gcd.createProfile(txtProfileName.text, radioNoPassword.checked, txtPassword1.text) + gcd.reloadProfileList() + parentStack.pane = parentStack.managementPane } } else { gcd.updateNick(onion, txtProfileName.text) @@ -202,12 +207,10 @@ ColumnLayout { // Add Profile Pane passwordErrorLabel.visible = true } else { gcd.changePassword(onion, txtCurrentPassword.text, txtPassword1.text, radioNoPassword.checked) - return } } } - gcd.reloadProfileList() - parentStack.pane = parentStack.managementPane + } } From fee151cf26469b96ddec692a28fc851670d7e833 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 18 Dec 2019 11:29:50 -0800 Subject: [PATCH 28/28] save edit name fix --- qml/panes/ProfileAddEditPane.qml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qml/panes/ProfileAddEditPane.qml b/qml/panes/ProfileAddEditPane.qml index b0c3ef85..8939877d 100644 --- a/qml/panes/ProfileAddEditPane.qml +++ b/qml/panes/ProfileAddEditPane.qml @@ -208,6 +208,9 @@ ColumnLayout { // Add Profile Pane } else { gcd.changePassword(onion, txtCurrentPassword.text, txtPassword1.text, radioNoPassword.checked) } + } else { + gcd.reloadProfileList() + parentStack.pane = parentStack.managementPane } }