dan
/
ui
forked from cwtch.im/ui
1
0
Fork 0

New getVal/retVal message handling, and new attribute storage schema

keyval
Dan Ballard 3 years ago
parent 631810df5b
commit fc3bdb76f6
  1. 5
      go.mod
  2. 17
      go.sum
  3. 5
      go/constants/attributes.go
  4. 6
      go/constants/settings.go
  5. 16
      go/handlers/appHandler.go
  6. 96
      go/handlers/peerHandler.go
  7. 94
      go/ui/gcd.go
  8. 34
      go/ui/imageType.go
  9. 156
      go/ui/manager.go
  10. 4
      qml/widgets/ContactPicture.qml
  11. 6
      qml/widgets/ContactRow.qml

@ -3,11 +3,10 @@ module cwtch.im/ui
go 1.12
require (
cwtch.im/cwtch v0.3.10
cwtch.im/cwtch v0.3.11
git.openprivacy.ca/openprivacy/connectivity v1.1.1
git.openprivacy.ca/openprivacy/log v1.0.0
github.com/gopherjs/gopherjs v0.0.0-20200209183636-89e6cbcd0b6d // indirect
github.com/therecipe/qt v0.0.0-20191101232336-18864661ae4f
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 // indirect; Required - do not delete or `go tidy` away
golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678 // indirect
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20191002095216-73192f6811d0 // indirect; Required - do not delete or `go tidy` away
)

@ -1,7 +1,11 @@
cwtch.im/cwtch v0.3.10 h1:akrIwsc1KnLbT3K6ZIFkhmA7kI62L03EWna7Ul1vszU=
cwtch.im/cwtch v0.3.10/go.mod h1:tmYeI2v0IEeBMbqzhcndXWZ2oyflhK4Afcf27+49rKU=
cwtch.im/cwtch v0.3.11 h1:2+W2w9HDQowKwEGx4oRLywmn0NzQ0Sg9JEyBdR/V1mA=
cwtch.im/cwtch v0.3.11/go.mod h1:PnMJb9CyzdrdbYjmL99pl6Nu34s6+lmeENVnGaY0hzk=
cwtch.im/tapir v0.1.15 h1:XSCWOvjmNkzMT2IceFgTBXWGKtYfr3a8o+La1s10OhE=
cwtch.im/tapir v0.1.15/go.mod h1:HzezugpEx+nZ3LdyDsl0w6n45IJYnOt8uqldkLWmaqs=
cwtch.im/tapir v0.1.17 h1:2jVZUe1a88tMI4aJPvRTO4Id3NN3PsM62cT5lntEChk=
cwtch.im/tapir v0.1.17/go.mod h1:HzezugpEx+nZ3LdyDsl0w6n45IJYnOt8uqldkLWmaqs=
git.openprivacy.ca/openprivacy/connectivity v1.1.0/go.mod h1:4P8mirZZslKbo2zBrXXVjgEdqGwHo/6qoFBwFQW6d6E=
git.openprivacy.ca/openprivacy/connectivity v1.1.1 h1:hKxBOmxP7Jdu3K1BJ93mRtKNiWUoP6YHt/o2snE2Z0w=
git.openprivacy.ca/openprivacy/connectivity v1.1.1/go.mod h1:4P8mirZZslKbo2zBrXXVjgEdqGwHo/6qoFBwFQW6d6E=
@ -12,6 +16,7 @@ git.openprivacy.ca/openprivacy/log v1.0.0/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQN
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/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca h1:Q2r7AxHdJwWfLtBZwvW621M3sPqxPc6ITv2j1FGsYpw=
github.com/cretz/bine v0.1.1-0.20200124154328-f9f678b84cca/go.mod h1:6PF6fWAvYtwjRGkAuDEJeWNOv3a2hUouSP/yRYXmvHw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -36,6 +41,7 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
@ -44,6 +50,7 @@ github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-tty v0.0.3/go.mod h1:ihxohKRERHTVzN+aSVRwACLCeqIoZAWpoICkkvrWyR0=
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
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=
@ -57,6 +64,7 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/struCoder/pidusage v0.1.3/go.mod h1:pWBlW3YuSwRl6h7R5KbvA4N8oOqe9LjaKW5CwT1SPjI=
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=
@ -71,6 +79,8 @@ github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200126204426
github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200126204426-5074eb6d8c41/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=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
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=
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72 h1:+ELyKg6m8UBf0nPFSqD0mi7zUfwPyXo23HNjMnXPz7w=
@ -80,11 +90,15 @@ golang.org/x/crypto v0.0.0-20200206161412-a0c6ece9d31a/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200210191831-6ca56c2f2e2b/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678 h1:wCWoJcFExDgyYx2m2hpHgwz8W3+FPdfldvIgzqDIhyg=
golang.org/x/crypto v0.0.0-20200210222208-86ce3cb69678/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df h1:lDWgvUvNnaTnNBc/dwOty86cFeKoKWbwy2wQj0gIxbU=
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
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-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200320181208-1c781a10960a h1:KaxWXSFrOaE2ptiOotI+zFdzHxBsg9MW6XfCv497IRo=
golang.org/x/net v0.0.0-20200320181208-1c781a10960a/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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -98,12 +112,15 @@ golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200320181252-af34d8274f85 h1:fD99hd4ciR6T3oPhr2EkmuKe9oHixHx9Hj/hND89j3g=
golang.org/x/sys v0.0.0-20200320181252-af34d8274f85/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789 h1:FF0rjo15h51+N6642mf5S3QuplmKo2aCrJUYkHTx85s=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

@ -1,9 +1,10 @@
package constants
const Nick = "nick"
const SchemaVersion = "schemaVersion"
const Name = "name"
const LastRead = "last-read"
const Picture = "picture"
const DefaultPassword = "default-password"
const ProfileTypeV1DefaultPassword = "v1-defaultPassword"
const ProfileTypeV1Password = "v1-userPassword"

@ -1,5 +1,5 @@
package constants
const BlockUnknownPeersSetting = "settings.blockunknownpeers"
const LocaleSetting = "settings.locale"
const ZoomSetting = "settings.zoom"
const BlockUnknownPeersSetting = ".blockunknownpeers"
const LocaleSetting = ".locale"
const ZoomSetting = ".zoom"

@ -91,10 +91,10 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingAccounts
}
case event.NewPeer:
onion := e.Data[event.Identity]
peer := the.CwtchApp.GetPeer(onion)
p := the.CwtchApp.GetPeer(onion)
if tag, exists := peer.GetAttribute(app.AttributeTag); !exists || tag == "" {
peer.SetAttribute(app.AttributeTag, constants.ProfileTypeV1DefaultPassword)
if tag, exists := p.GetAttribute(app.AttributeTag); !exists || tag == "" {
p.SetAttribute(app.AttributeTag, constants.ProfileTypeV1DefaultPassword)
}
log.Infof("NewPeer for %v\n", onion)
@ -104,16 +104,16 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingAccounts
the.CwtchApp.AddPeerPlugin(onion, plugins.NETWORKCHECK)
incSubscribed := make(chan bool)
go PeerHandler(onion, gcd.GetUiManager(peer.GetOnion()), incSubscribed)
go PeerHandler(onion, gcd.GetUiManager(p.GetOnion()), incSubscribed)
<-incSubscribed
if e.Data[event.Status] != "running" {
peer.Listen()
peer.StartPeersConnections()
peer.StartGroupConnections()
p.Listen()
p.StartPeersConnections()
p.StartGroupConnections()
}
blockUnkownPeers, exists := peer.GetAttribute(constants.BlockUnknownPeersSetting)
blockUnkownPeers, exists := p.GetAttribute(constants.BlockUnknownPeersSetting)
if exists && blockUnkownPeers == "true" {
the.EventBus.Publish(event.NewEvent(event.BlockUnknownPeers, map[event.Field]string{}))
}

@ -3,16 +3,21 @@ package handlers
import (
"cwtch.im/cwtch/app"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/model/attr"
peerC "cwtch.im/cwtch/peer"
"cwtch.im/cwtch/protocol/connections"
"cwtch.im/ui/go/constants"
"cwtch.im/ui/go/the"
"cwtch.im/ui/go/ui"
"git.openprivacy.ca/openprivacy/log"
"strconv"
"time"
)
func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
peer := the.CwtchApp.GetPeer(onion)
upgradeSchema(peer)
eventBus := the.CwtchApp.GetEventBus(onion)
q := event.NewQueue()
eventBus.Subscribe(event.NewMessageFromPeer, q)
@ -27,6 +32,7 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
eventBus.Subscribe(event.NetworkStatus, q)
eventBus.Subscribe(event.ChangePasswordSuccess, q)
eventBus.Subscribe(event.ChangePasswordError, q)
eventBus.Subscribe(event.NewRetValMessageFromPeer, q)
subscribed <- true
@ -49,9 +55,6 @@ func PeerHandler(onion string, 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 peer.GetContact(e.Data[event.RemotePeer]) == nil {
peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
}
case event.PeerAcknowledgement:
uiManager.Acknowledge(e.Data[event.EventID])
@ -76,7 +79,7 @@ func PeerHandler(onion string, 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 contact := peer.GetContact(e.Data[event.RemotePeer]); contact != nil {
if contact := peer.GetContact(e.Data[event.RemotePeer]); contact == nil {
// 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)
@ -90,8 +93,27 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
if contact := peer.GetContact(e.Data[event.RemotePeer]); contact != nil {
contact.State = e.Data[event.ConnectionState]
uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
if cxnState == connections.AUTHENTICATED {
peer.SendGetValToPeer(e.Data[event.RemotePeer], attr.PublicScope, constants.Name)
peer.SendGetValToPeer(e.Data[event.RemotePeer], attr.PublicScope, constants.Picture)
}
}
case event.NewRetValMessageFromPeer:
onion := e.Data[event.RemotePeer]
scope := e.Data[event.Scope]
path := e.Data[event.Path]
val := e.Data[event.Data]
exists, _ := strconv.ParseBool(e.Data[event.Exists])
if exists && scope == attr.PublicScope {
switch path {
case constants.Name:
uiManager.UpdateContactDisplayName(onion, val)
case constants.Picture:
uiManager.UpdateContactPicture(onion, val)
}
}
case event.ServerStateChange:
serverOnion := e.Data[event.GroupServer]
state := connections.ConnectionStateToType[e.Data[event.ConnectionState]]
@ -119,6 +141,72 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) {
case event.ChangePasswordError:
uiManager.ChangePasswordResponse(true)
}
}
}
func upgradeSchema(p peerC.CwtchPeer) {
schemaVerVal, exists := p.GetAttribute(constants.SchemaVersion)
if !exists {
schemaVerVal = "0"
}
schemaVer, err := strconv.Atoi(schemaVerVal)
if err != nil {
schemaVer = 0
}
if schemaVer < 1 {
upgradeSchema1(p)
}
}
func upgradeSchema1(p peerC.CwtchPeer) {
log.Infof("UpgradeSchema 1\n")
p.SetAttribute(attr.GetPublicScope(constants.Name), p.GetName())
if picture, exists := p.GetAttribute("picture"); exists {
p.SetAttribute(attr.GetPublicScope(constants.Picture), ui.ImageToString(ui.NewImage(picture, ui.TypeImageDistro)))
}
if locale, exists := p.GetAttribute("settings.locale"); exists {
p.SetAttribute(attr.GetSettingsScope(constants.LocaleSetting), locale)
}
if zoom, exists := p.GetAttribute("settings.zoom"); exists {
p.SetAttribute(attr.GetSettingsScope(constants.ZoomSetting), zoom)
}
if blockunknown, exists := p.GetAttribute("settings.blockunknownpeers"); exists {
p.SetAttribute(attr.GetSettingsScope(constants.BlockUnknownPeersSetting), blockunknown)
}
for _, contactID := range p.GetContacts() {
if nick, exists := p.GetContactAttribute(contactID, "nick"); exists {
p.SetContactAttribute(contactID, attr.GetLocalScope(constants.Name), nick)
}
if picture, exists := p.GetContactAttribute(contactID, "picture"); exists {
p.SetContactAttribute(contactID, attr.GetLocalScope(constants.Picture), ui.ImageToString(ui.NewImage(picture, ui.TypeImageDistro)))
}
if lastRead, exists := p.GetContactAttribute(contactID, "last-read"); exists {
p.SetContactAttribute(contactID, attr.GetLocalScope(constants.LastRead), lastRead)
}
}
for _, gID := range p.GetGroups() {
if nick, exists := p.GetGroupAttribute(gID, "nick"); exists {
p.SetGroupAttribute(gID, attr.GetLocalScope(constants.Name), nick)
}
if picture, exists := p.GetGroupAttribute(gID, "picture"); exists {
p.SetGroupAttribute(gID, attr.GetLocalScope(constants.Picture), ui.ImageToString(ui.NewImage(picture, ui.TypeImageDistro)))
}
if lastRead, exists := p.GetGroupAttribute(gID, "last-read"); exists {
p.SetGroupAttribute(gID, attr.GetLocalScope(constants.LastRead), lastRead)
}
}
p.SetAttribute(constants.SchemaVersion, "1")
}

@ -3,6 +3,7 @@ package ui
import (
"cwtch.im/cwtch/app"
"cwtch.im/cwtch/event"
"cwtch.im/cwtch/model/attr"
"cwtch.im/cwtch/protocol/connections"
"cwtch.im/ui/go/constants"
"github.com/therecipe/qt/qml"
@ -49,6 +50,7 @@ type GrandCentralDispatcher struct {
// 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"`
_ func(handle, image string) `signal:"UpdateContactPicture"`
_ func(handle string, status int, loading bool) `signal:"UpdateContactStatus"`
_ func(handle string, blocked bool) `signal:"UpdateContactBlocked"`
_ func(handle string) `signal:"IncContactUnreadCount"`
@ -106,7 +108,6 @@ type GrandCentralDispatcher struct {
_ func() `signal:"requestPeerSettings,auto"`
_ func(onion, nick string) `signal:"savePeerSettings,auto"`
_ func(onion, groupID string) `signal:"inviteToGroup,auto"`
_ func(onion, key, nick string) `signal:"setAttribute,auto"`
_ func(onion string) `signal:"deleteContact,auto"`
_ func() `signal:"allowUnknownPeers,auto"`
_ func() `signal:"blockUnknownPeers,auto"`
@ -249,7 +250,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
this.UpdateContactStatus(group.GroupID, int(state), loading)
tl := group.GetTimeline()
nick, _ := group.GetAttribute(constants.Nick)
nick := getNick(handle)
updateLastReadTime(group.GroupID)
if nick == "" {
this.SetToolbarTitle(handle)
@ -264,7 +265,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
handle = tl[i].PeerID
}
name := getOrDefault(tl[i].PeerID, constants.Nick, tl[i].PeerID)
name := getNick(tl[i].PeerID)
image := getProfilePic(tl[i].PeerID)
this.PrependMessage(
@ -289,10 +290,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
var nick string
if contact != nil {
nick, _ = contact.GetAttribute(constants.Nick)
if nick == "" {
nick = handle
}
nick = getNick(contact.Onion)
}
updateLastReadTime(contact.Onion)
this.SetToolbarTitle(nick)
@ -306,7 +304,7 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
from = "me"
}
displayname := getOrDefault(messages[i].PeerID, constants.Nick, messages[i].PeerID)
displayname := getNick(messages[i].PeerID)
image := getProfilePic(messages[i].PeerID)
this.AppendMessage(
@ -325,9 +323,20 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) {
}
func (this *GrandCentralDispatcher) requestSettings() {
zoom, _ := the.Peer.GetAttribute(constants.ZoomSetting)
locale, _ := the.Peer.GetAttribute(constants.LocaleSetting)
blockunkownpeers, _ := the.Peer.GetAttribute(constants.BlockUnknownPeersSetting)
zoom, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.ZoomSetting))
if !exists {
zoom = "1.0"
}
locale, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.LocaleSetting))
if !exists {
// TODO: pull env locale
locale = ""
}
blockunkownpeers, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.BlockUnknownPeersSetting))
if !exists {
blockunkownpeers = "false"
}
this.SupplySettings(zoom, locale, blockunkownpeers == "true")
}
@ -338,7 +347,7 @@ func (this *GrandCentralDispatcher) saveSettings(zoom, locale string) {
return
}
the.Peer.SetAttribute(constants.ZoomSetting, zoom)
the.Peer.SetAttribute(attr.GetSettingsScope(constants.ZoomSetting), zoom)
}
func (this *GrandCentralDispatcher) requestPeerSettings() {
@ -349,19 +358,13 @@ func (this *GrandCentralDispatcher) requestPeerSettings() {
return
}
name, exists := contact.GetAttribute(constants.Nick)
if !exists {
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
}
name := getNick(contact.Onion)
this.SupplyPeerSettings(contact.Onion, name, contact.Blocked)
}
func (this *GrandCentralDispatcher) savePeerSettings(onion, nick string) {
the.Peer.SetContactAttribute(onion, constants.Nick, nick)
the.Peer.SetContactAttribute(onion, attr.GetLocalScope(constants.Name), nick)
this.UpdateContactDisplayName(onion, nick)
}
@ -373,25 +376,21 @@ func (this *GrandCentralDispatcher) requestGroupSettings(groupID string) {
return
}
nick, _ := group.GetAttribute(constants.Nick)
nick := getNick(groupID)
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(constants.Nick)
if hasname {
contactnames[i] = name
} else {
contactnames[i] = contact
}
contactnames[i] = getNick(contact)
}
this.SupplyGroupSettings(group.GroupID, nick, group.GroupServer, invite, group.Accepted, contactnames, contactaddrs)
}
func (this *GrandCentralDispatcher) saveGroupSettings(groupID, nick string) {
the.Peer.SetGroupAttribute(groupID, constants.Nick, nick)
the.Peer.SetGroupAttribute(groupID, attr.GetLocalScope(constants.Name), nick)
this.UpdateContactDisplayName(groupID, nick)
}
@ -485,9 +484,10 @@ func (this *GrandCentralDispatcher) popup(str string) {
}
func (this *GrandCentralDispatcher) updateNick(onion, nick string) {
peer := the.CwtchApp.GetPeer(onion)
if peer != nil {
peer.SetName(nick)
p := the.CwtchApp.GetPeer(onion)
if p != nil {
p.SetName(nick)
p.SetAttribute(attr.GetPublicScope(constants.Name), nick)
the.CwtchApp.GetEventBus(onion).Publish(event.NewEvent(event.SetProfileName, map[event.Field]string{
event.ProfileName: nick,
}))
@ -503,7 +503,7 @@ func (this *GrandCentralDispatcher) createGroup(server, groupName string) {
this.GetUiManager(this.selectedProfile()).AddContact(groupID)
the.Peer.SetGroupAttribute(groupID, constants.Nick, groupName)
the.Peer.SetGroupAttribute(groupID, attr.GetLocalScope(constants.Name), groupName)
the.Peer.JoinServer(server)
}
@ -548,28 +548,23 @@ func (this *GrandCentralDispatcher) acceptGroup(groupID string) {
}
}
func (this *GrandCentralDispatcher) setAttribute(onion, key, value string) {
the.Peer.SetContactAttribute(onion, key, value)
this.GetUiManager(this.selectedProfile()).UpdateContactAttribute(onion, key, value)
}
func (this *GrandCentralDispatcher) blockUnknownPeers() {
the.Peer.SetAttribute(constants.BlockUnknownPeersSetting, "true")
the.Peer.SetAttribute(attr.GetSettingsScope(constants.BlockUnknownPeersSetting), "true")
the.EventBus.Publish(event.NewEvent(event.BlockUnknownPeers, map[event.Field]string{}))
}
func (this *GrandCentralDispatcher) allowUnknownPeers() {
the.Peer.SetAttribute(constants.BlockUnknownPeersSetting, "false")
the.Peer.SetAttribute(attr.GetSettingsScope(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.Peer.SetAttribute(constants.LocaleSetting, locale)
the.Peer.SetAttribute(attr.GetSettingsScope(constants.LocaleSetting), locale)
zoom, _ := the.Peer.GetAttribute(constants.ZoomSetting)
blockunkownpeers, _ := the.Peer.GetAttribute(constants.BlockUnknownPeersSetting)
zoom, _ := the.Peer.GetAttribute(attr.GetSettingsScope(constants.ZoomSetting))
blockunkownpeers, _ := the.Peer.GetAttribute(attr.GetPeerScope(constants.BlockUnknownPeersSetting))
this.SupplySettings(zoom, locale, blockunkownpeers == "true")
}
@ -597,12 +592,17 @@ 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)
picVal, exists := the.Peer.GetAttribute(attr.GetPublicScope(constants.Picture))
if !exists {
pic = RandomProfileImage(the.Peer.GetOnion())
the.Peer.SetAttribute(constants.Picture, pic)
picVal = ImageToString(NewImage(RandomProfileImage(onion), TypeImageDistro))
the.Peer.SetAttribute(attr.GetPublicScope(constants.Picture), picVal)
}
pic, err := StringToImage(picVal)
if err != nil {
pic = NewImage(RandomProfileImage(onion), TypeImageDistro)
the.Peer.SetAttribute(attr.GetPublicScope(constants.Picture), ImageToString(pic))
}
this.UpdateMyProfile(the.Peer.GetName(), the.Peer.GetOnion(), profilePicRelativize(pic))
this.UpdateMyProfile(the.Peer.GetName(), the.Peer.GetOnion(), getPicturePath(pic))
contacts := the.Peer.GetContacts()
for i := range contacts {
@ -617,7 +617,7 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) {
// load ui preferences
this.RequestSettings()
locale, exists := the.Peer.GetAttribute(constants.LocaleSetting)
locale, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.LocaleSetting))
if exists {
this.SetLocale_helper(locale)
}

@ -0,0 +1,34 @@
package ui
import "encoding/json"
// Image types we support
const (
// TypeImageDistro is a reletive path to any of the distributed images in cwtch/ui in the assets folder
TypeImageDistro = "distro"
// TypeImageComposition will be an face image composed of a recipe of parts like faceType, eyeType, etc
TypeImageComposition = "composition"
)
type image struct {
Val string
T string
}
func NewImage(val, t string) *image {
return &image{val, t}
}
func StringToImage(str string) (*image, error) {
var img image
err := json.Unmarshal([]byte(str), &img)
if err != nil {
return nil, err
}
return &img, nil
}
func ImageToString(img *image) string {
bytes, _ := json.Marshal(img)
return string(bytes)
}

@ -3,6 +3,7 @@ package ui
import (
"cwtch.im/cwtch/app"
"cwtch.im/cwtch/model"
"cwtch.im/cwtch/model/attr"
"cwtch.im/cwtch/protocol/connections"
"cwtch.im/ui/go/constants"
"cwtch.im/ui/go/the"
@ -20,7 +21,7 @@ func isPeer(id string) bool {
return len(id) == 56
}
func getOrDefault(id, key, defaultVal string) string {
func getOrDefault(id, key string, defaultVal string) string {
var val string
var ok bool
if isGroup(id) {
@ -35,7 +36,7 @@ func getOrDefault(id, key, defaultVal string) string {
}
}
func getWithSetDefault(id string, key, defaultVal string) string {
func getWithSetDefault(id string, key string, defaultVal string) string {
var val string
var ok bool
if isGroup(id) {
@ -54,12 +55,34 @@ func getWithSetDefault(id string, key, defaultVal string) string {
return val
}
func getNick(id string) string {
if isGroup(id) {
nick, exists := the.Peer.GetGroupAttribute(id, attr.GetLocalScope(constants.Name))
if !exists || nick == "" {
nick, exists = the.Peer.GetGroupAttribute(id, attr.GetPeerScope(constants.Name))
if !exists {
nick = id
}
}
return nick
} else {
nick, exists := the.Peer.GetContactAttribute(id, attr.GetLocalScope(constants.Name))
if !exists {
nick, exists = the.Peer.GetContactAttribute(id, attr.GetPeerScope(constants.Name))
if !exists {
nick = id
}
}
return nick
}
}
// initLastReadTime checks and gets the Attributable's LastRead time or sets it to now
func initLastReadTime(id string) time.Time {
nowStr, _ := time.Now().MarshalText()
lastReadStr := getWithSetDefault(id, constants.LastRead, string(nowStr))
lastReadAttr := getWithSetDefault(id, attr.GetLocalScope(constants.LastRead), string(nowStr))
var lastRead time.Time
lastRead.UnmarshalText([]byte(lastReadStr))
lastRead.UnmarshalText([]byte(lastReadAttr))
return lastRead
}
@ -70,37 +93,57 @@ func profilePicRelativize(filename string) string {
return parts[len(parts)-1]
}
func initProfilePicture(id string) string {
if isGroup(id) {
return profilePicRelativize(getWithSetDefault(id, constants.Picture, RandomGroupImage(id)))
} else {
return profilePicRelativize(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
// getProfilePic returns a string path to an image to display for hte given peer/group id
func getProfilePic(id string) string {
log.Debugf("getProfilePic for %v\n", id)
if isGroup(id) {
if pic, exists := the.Peer.GetGroupAttribute(id, constants.Picture); !exists {
return RandomGroupImage(id)
} else {
return profilePicRelativize(pic)
if picVal, exists := the.Peer.GetGroupAttribute(id, attr.GetLocalScope(constants.Picture)); exists {
pic, err := StringToImage(picVal)
if err == nil {
return getPicturePath(pic)
}
}
if picVal, exists := the.Peer.GetGroupAttribute(id, attr.GetPeerScope(constants.Picture)); exists {
pic, err := StringToImage(picVal)
if err == nil {
return getPicturePath(pic)
}
}
return getPicturePath(NewImage(RandomGroupImage(id), TypeImageDistro))
} else {
if pic, exists := the.Peer.GetContactAttribute(id, constants.Picture); !exists {
return profilePicRelativize(RandomProfileImage(id))
} else {
return pic
if picVal, exists := the.Peer.GetContactAttribute(id, attr.GetLocalScope(constants.Picture)); exists {
pic, err := StringToImage(picVal)
if err == nil {
return getPicturePath(pic)
}
}
if picVal, exists := the.Peer.GetContactAttribute(id, attr.GetPeerScope(constants.Picture)); exists {
pic, err := StringToImage(picVal)
if err == nil {
return getPicturePath(pic)
}
}
return getPicturePath(NewImage("fontawesome/regular/user.svg", TypeImageDistro))
}
}
func getPicturePath(pic *image) string {
switch pic.T {
case TypeImageDistro:
return profilePicRelativize(pic.Val)
default:
log.Errorf("Unhandled profile picture type of %v\n", pic.T)
return ""
}
}
func updateLastReadTime(id string) {
lastRead, _ := time.Now().MarshalText()
if isGroup(id) {
the.Peer.SetGroupAttribute(id, constants.LastRead, string(lastRead))
the.Peer.SetGroupAttribute(id, attr.GetLocalScope(constants.LastRead), string(lastRead))
} else {
the.Peer.SetContactAttribute(id, constants.LastRead, string(lastRead))
the.Peer.SetContactAttribute(id, attr.GetLocalScope(constants.LastRead), string(lastRead))
}
}
@ -118,23 +161,27 @@ func countUnread(messages []model.Message, lastRead time.Time) int {
// AddProfile adds a new profile to the UI
func AddProfile(gcd *GrandCentralDispatcher, handle string) {
peer := the.CwtchApp.GetPeer(handle)
if peer != nil {
nick := peer.GetName()
if nick == "" {
p := the.CwtchApp.GetPeer(handle)
if p != nil {
nick, exists := p.GetAttribute(attr.GetPublicScope(constants.Name))
if !exists {
nick = handle
peer.SetAttribute(constants.Nick, nick)
}
pic, ok := peer.GetAttribute(constants.Picture)
picVal, ok := p.GetAttribute(attr.GetPublicScope(constants.Picture))
if !ok {
pic = RandomProfileImage(handle)
peer.SetAttribute(constants.Picture, pic)
picVal = ImageToString(NewImage(RandomProfileImage(handle), TypeImageDistro))
}
pic, err := StringToImage(picVal)
if err != nil {
pic = NewImage(RandomProfileImage(handle), TypeImageDistro)
}
picPath := getPicturePath(pic)
tag, _ := peer.GetAttribute(app.AttributeTag)
log.Infof("AddProfile %v %v %v %v\n", handle, nick, profilePicRelativize(pic), tag)
gcd.AddProfile(handle, nick, profilePicRelativize(pic), tag)
tag, _ := p.GetAttribute(app.AttributeTag)
log.Infof("AddProfile %v %v %v %v\n", handle, nick, picPath, tag)
gcd.AddProfile(handle, nick, picPath, tag)
}
}
@ -157,6 +204,7 @@ type Manager interface {
ReloadProfiles()
UpdateContactDisplayName(handle string, name string)
UpdateContactPicture(handle string, picVal string)
UpdateContactStatus(handle string, status int, loading bool)
UpdateContactAttribute(handle, key, value string)
@ -184,40 +232,32 @@ func getLastMessageTime(tl *model.Timeline) int {
}
// AddContact adds a new contact to the ui for this manager's profile
func (this *manager) AddContact(Handle string) {
func (this *manager) AddContact(handle string) {
this.gcd.DoIfProfile(this.profile, func() {
if isGroup(Handle) {
group := the.Peer.GetGroup(Handle)
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
}
picture := getProfilePic(handle)
this.gcd.AddContact(Handle, nick, picture, group.GroupServer, unread, int(connections.ConnectionStateToType[group.State]), false, false, getLastMessageTime(&group.Timeline))
this.gcd.AddContact(handle, getNick(handle), 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)
} else if !isPeer(handle) {
log.Errorf("sorry, unable to handle AddContact(%v)", handle)
debug.PrintStack()
return
}
contact := the.Peer.GetContact(Handle)
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
}
picture := getProfilePic(handle)
this.gcd.AddContact(Handle, nick, picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false, getLastMessageTime(&contact.Timeline))
this.gcd.AddContact(handle, getNick(handle), picture, "", unread, int(connections.ConnectionStateToType[contact.State]), contact.Blocked, false, getLastMessageTime(&contact.Timeline))
}
})
}
@ -238,7 +278,7 @@ 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) {
this.gcd.DoIfProfile(this.profile, func() {
nick := getOrDefault(handle, constants.Nick, handle)
nick := getNick(handle)
image := getProfilePic(handle)
// If we have this group loaded already
@ -270,6 +310,16 @@ func (this *manager) UpdateContactDisplayName(handle string, name string) {
})
}
// UpdateContactPicture updates a contact's picture in the contact list and conversations
func (this *manager) UpdateContactPicture(handle string, picVal string) {
this.gcd.DoIfProfile(this.profile, func() {
pic, err := StringToImage(picVal)
if err == nil {
this.gcd.UpdateContactPicture(handle, getPicturePath(pic))
}
})
}
// 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() {

@ -25,13 +25,13 @@ Item {
id: mainImage
width: baseWidth
height: baseWidth
color: highlight ? windowItem.cwtch_dark_color: "#FFFFFF"
color: "#350052" //: "#FFFFFF" //windowItem.cwtch_dark_color: "#FFFFFF"
radius: width / 2
Rectangle {
width: highlight ? baseWidth - 4 : baseWidth
height: highlight ? baseWidth - 4 : baseWidth
color: button ? windowItem.cwtch_dark_color: "#FFFFFF"
color: "#350052" //: "#FFFFFF" // windowItem.cwtch_dark_color: "#FFFFFF"
radius: width / 2
anchors.centerIn:parent

@ -244,6 +244,12 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
}
}
onUpdateContactPicture: function(_handle, _image) {
if (handle == _handle) {
image = _image
}
}
onIncContactUnreadCount: function(handle) {
if (handle == _handle && gcd.selectedConversation != handle) {
badge++

Loading…
Cancel
Save