From b8ffa85facf9633d5ed4e3df44faea2edacfe255 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 26 May 2020 11:21:27 -0700 Subject: [PATCH 1/3] Add global settings pane; migrate peer settings to new opaque settings widgets; minor fixes; add global settings storage; rework global settings settings --- qml/panes/SettingsPane.qml | 35 ++++++++++++++++++++++++++++++++++- qml/widgets/Statusbar.qml | 1 - 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/qml/panes/SettingsPane.qml b/qml/panes/SettingsPane.qml index a847faa2..914d967f 100644 --- a/qml/panes/SettingsPane.qml +++ b/qml/panes/SettingsPane.qml @@ -72,6 +72,7 @@ Opaque.SettingsList { // settingsPane value: gcd.themeScale live: false stepSize: 0.1 + onValueChanged: { gcd.themeScale = zoomSlider.value } @@ -88,7 +89,6 @@ Opaque.SettingsList { // settingsPane } } - Opaque.Setting { //: Theme label: qsTr("setting-theme") @@ -195,6 +195,39 @@ text: qsTr("block-unknown-label") } +======= +} + + +/* Opaque.ScalingLabel { +width: parent.width +wrapMode: TextEdit.Wrap +//: Interface zoom (mostly affects text and button sizes) +text: qsTr("zoom-label") + ":" +} + + + + +CheckBox { +id: blockUnknownToggle +checked: true +onClicked: { +if (blockUnknownToggle.checked) { +gcd.blockUnknownPeers() +} else { +gcd.allowUnknownPeers() +} +} +style: CheckBoxStyle { +label: Opaque.ScalingLabel { +text: qsTr("block-unknown-label") +} +} +} + + +>>>>>>> 007b851f... Add global settings pane; migrate peer settings to new opaque settings widgets; minor fixes; add global settings storage; rework global settings settings */ diff --git a/qml/widgets/Statusbar.qml b/qml/widgets/Statusbar.qml index 52e67b6c..84de4657 100644 --- a/qml/widgets/Statusbar.qml +++ b/qml/widgets/Statusbar.qml @@ -190,5 +190,4 @@ Rectangle { changeStatus() } } - } -- 2.25.1 From 315c66009e3037c43ee3ad730a3971ed7e0559ed Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 23 Jun 2020 16:42:51 -0700 Subject: [PATCH 2/3] Migrate to new peer authorization levels, add new peer approval workflow, add blocked peers to contact list in seperate section --- assets/core/account_blocked.svg | 19 ++++ go.mod | 7 +- go.sum | 6 ++ go/constants/attributes.go | 1 + go/handlers/peerHandler.go | 14 +-- go/ui/gcd.go | 66 ++++++------ go/ui/manager.go | 6 +- go/ui/settings.go | 30 +----- i18n/translation_de.ts | 35 ++++--- i18n/translation_en.qm | Bin 6958 -> 7019 bytes i18n/translation_en.ts | 35 ++++--- i18n/translation_fr.ts | 35 ++++--- i18n/translation_pt.ts | 35 ++++--- qml/const/Const.qml | 14 +++ qml/const/qmldir | 1 + qml/main.qml | 2 - qml/opaque | 2 +- qml/panes/PeerSettingsPane.qml | 25 +++-- qml/panes/SettingsPane.qml | 37 ------- qml/widgets/ContactList.qml | 177 +++++++++++++++++++++++++++----- qml/widgets/ContactRow.qml | 42 +++++++- qml/widgets/MyProfile.qml | 3 +- qml/widgets/ProfileList.qml | 2 +- 23 files changed, 385 insertions(+), 209 deletions(-) create mode 100644 assets/core/account_blocked.svg create mode 100644 qml/const/Const.qml create mode 100644 qml/const/qmldir diff --git a/assets/core/account_blocked.svg b/assets/core/account_blocked.svg new file mode 100644 index 00000000..412efcff --- /dev/null +++ b/assets/core/account_blocked.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/go.mod b/go.mod index 96f34001..c53c6bc6 100644 --- a/go.mod +++ b/go.mod @@ -3,12 +3,13 @@ module cwtch.im/ui go 1.12 require ( - cwtch.im/cwtch v0.3.11 + cwtch.im/cwtch v0.3.15 git.openprivacy.ca/openprivacy/connectivity v1.1.4 - git.openprivacy.ca/openprivacy/log v1.0.0 + git.openprivacy.ca/openprivacy/log v1.0.1 github.com/gopherjs/gopherjs v0.0.0-20200209183636-89e6cbcd0b6d // indirect github.com/therecipe/qt v0.0.0-20200126204426-5074eb6d8c41 github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect - golang.org/x/crypto v0.0.0-20200320181102-891825fb96df + golang.org/x/crypto v0.0.0-20200420104511-884d27f42877 // indirect ) + diff --git a/go.sum b/go.sum index 976949b9..e6e5746f 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,10 @@ 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/cwtch v0.3.14 h1:XL8UbCUyIosdFTD5nSlpvhbQQGFLjvFmd81/SmfBSP8= +cwtch.im/cwtch v0.3.14/go.mod h1:wDmgxWBWak/xvZ5GurdYNOJ8b8eha1MwVdiWsCS/pwI= +cwtch.im/cwtch v0.3.15 h1:Z7fFREwXY728q2YmmwgHL357zAobrsWJ2oPkkGwzvo0= +cwtch.im/cwtch v0.3.15/go.mod h1:iI9q4C3njHFBYQkNEbzMdK6QWPS0Vbkc0FigRHZNTvM= 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= @@ -23,6 +27,8 @@ git.openprivacy.ca/openprivacy/libricochet-go v1.0.13 h1:Z86uL9K47onznY1wP1P/wWf git.openprivacy.ca/openprivacy/libricochet-go v1.0.13/go.mod h1:ZUuX1SOrgV4K18IEcp0hQJNPKszRr2oGb3UeK2iYe5U= git.openprivacy.ca/openprivacy/log v1.0.0 h1:Rvqm1weUdR4AOnJ79b1upHCc9vC/QF1rhSD2Um7sr1Y= git.openprivacy.ca/openprivacy/log v1.0.0/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw= +git.openprivacy.ca/openprivacy/log v1.0.1 h1:NWV5oBTatvlSzUE6wtB+UQCulgyMOtm4BXGd34evMys= +git.openprivacy.ca/openprivacy/log v1.0.1/go.mod h1:gGYK8xHtndRLDymFtmjkG26GaMQNgyhioNS82m812Iw= 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= diff --git a/go/constants/attributes.go b/go/constants/attributes.go index 940e3041..5e972ada 100644 --- a/go/constants/attributes.go +++ b/go/constants/attributes.go @@ -5,6 +5,7 @@ const SchemaVersion = "schemaVersion" const Name = "name" const LastRead = "last-read" const Picture = "picture" +const ShowBlocked = "show-blocked" const ProfileTypeV1DefaultPassword = "v1-defaultPassword" const ProfileTypeV1Password = "v1-userPassword" diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index c6c8f02e..c4f940db 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -3,6 +3,7 @@ package handlers import ( "cwtch.im/cwtch/app" "cwtch.im/cwtch/event" + "cwtch.im/cwtch/model" "cwtch.im/cwtch/model/attr" peerC "cwtch.im/cwtch/peer" "cwtch.im/cwtch/protocol/connections" @@ -77,13 +78,12 @@ func PeerHandler(onion string, uiManager ui.Manager, subscribed chan bool) { case event.PeerStateChange: cxnState := connections.ConnectionStateToType[e.Data[event.ConnectionState]] - // if it's not in the.PeerHandler it's new. Only add once Authed - if 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) - if cxnState == connections.AUTHENTICATED { - peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false) + // New connection established + if cxnState == connections.AUTHENTICATED { + // if it's not in the peer it's new + if contact := peer.GetContact(e.Data[event.RemotePeer]); contact == nil { + // Contact does not exist, we will add them + peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], model.AuthUnknown) uiManager.AddContact(e.Data[event.RemotePeer]) } } diff --git a/go/ui/gcd.go b/go/ui/gcd.go index 0910fc7a..f70ae3e0 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -3,6 +3,7 @@ package ui import ( "cwtch.im/cwtch/app" "cwtch.im/cwtch/event" + "cwtch.im/cwtch/model" "cwtch.im/cwtch/model/attr" "cwtch.im/cwtch/protocol/connections" "cwtch.im/ui/go/constants" @@ -52,14 +53,13 @@ type GrandCentralDispatcher struct { _ func(failed bool) `signal:"ChangePasswordResponse"` // contact list stuff - _ func(handle, displayName, image 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"` - _ func(handle string) `signal:"RemoveContact"` - _ func(handle, key, value string) `signal:"UpdateContactAttribute"` + _ func(handle, displayName, image string, badge, status int, authorization string, 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) `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"` @@ -72,14 +72,14 @@ type GrandCentralDispatcher struct { _ func(loading bool) `signal:"SetLoadingState"` // profile-area stuff - _ func(name, onion, image, tag string) `signal:"UpdateMyProfile"` - _ func(status int) `signal:"TorStatus"` + _ func(name, onion, image, tag, showBlocked string) `signal:"UpdateMyProfile"` + _ func(status int) `signal:"TorStatus"` // settings helpers _ func(str string) `signal:"InvokePopup"` _ func(locale string, zoom float32, theme string) `signal:"SupplySettings"` _ func(groupID, name, server, invitation string, accepted bool, addrbooknames, addrbookaddrs []string) `signal:"SupplyGroupSettings"` - _ func(onion, nick string, blocked bool) `signal:"SupplyPeerSettings"` + _ func(onion, nick string, authorization string) `signal:"SupplyPeerSettings"` // signals emitted from the ui (written in go, below) // ui @@ -92,10 +92,10 @@ type GrandCentralDispatcher struct { _ func() `signal:"reloadProfileList,auto"` _ func(onion string) `signal:"deleteProfile,auto"` _ func(onion, currentPassword, newPassword string, defaultPass bool) `signal:"changePassword,auto""` + _ func(key, val string) `signal:"storeSetting,auto"` // operating a profile _ func(message string, mid string) `signal:"sendMessage,auto"` - _ func(onion string) `signal:"blockPeer,auto"` - _ func(onion string) `signal:"unblockPeer,auto"` + _ func(onion string, auth string) `signal:"setPeerAuthorization,auto"` _ func(onion string) `signal:"loadMessagesPane,auto"` _ func(signal string) `signal:"broadcast,auto"` // convenience relay signal _ func(str string) `signal:"importString,auto"` @@ -339,7 +339,7 @@ func (this *GrandCentralDispatcher) requestPeerSettings() { contact := the.Peer.GetContact(this.SelectedConversation()) if contact == nil { log.Errorf("error: requested settings for unknown contact %v?", this.SelectedConversation()) - this.SupplyPeerSettings(this.SelectedConversation(), this.SelectedConversation(), false) + this.SupplyPeerSettings(this.SelectedConversation(), this.SelectedConversation(), string(contact.Authorization)) return } @@ -348,7 +348,7 @@ func (this *GrandCentralDispatcher) requestPeerSettings() { // Todo: Move to profile settings //blockunkownpeers, _ := the.Peer.GetAttribute(attr.GetPeerScope(constants.BlockUnknownPeersSetting)) - this.SupplyPeerSettings(contact.Onion, name, contact.Blocked) + this.SupplyPeerSettings(contact.Onion, name, string(contact.Authorization)) } func (this *GrandCentralDispatcher) savePeerSettings(onion, nick string) { @@ -397,7 +397,7 @@ func (this *GrandCentralDispatcher) createContact(onion string) { if contact := the.Peer.GetContact(onion); contact != nil { return } - the.Peer.AddContact(onion, onion, false) + the.Peer.AddContact(onion, onion, model.AuthApproved) the.Peer.PeerWithOnion(onion) } @@ -460,7 +460,7 @@ func (this *GrandCentralDispatcher) importString(str string) { this.InvokePopup("already have this contact") return //TODO: bring them to the duplicate } else { - the.Peer.AddContact(name, onion, false) + the.Peer.AddContact(name, onion, model.AuthApproved) the.Peer.PeerWithOnion(onion) } @@ -496,21 +496,18 @@ func (this *GrandCentralDispatcher) createGroup(server, groupName string) { the.Peer.JoinServer(server) } -func (this *GrandCentralDispatcher) blockPeer(onion string) { - err := the.Peer.BlockPeer(onion) +func (this *GrandCentralDispatcher) setPeerAuthorization(onion string, authorization string) { + log.Debugf("Setting peer auth level to %v for %v\n", authorization, onion) + err := the.Peer.SetContactAuthorization(onion, model.Authorization(authorization)) if err != nil { - this.InvokePopup("Error Blocking Peer: " + err.Error()) + log.Errorf("Could not set peer authorization %v to %v\n", onion, authorization) + return } - this.UpdateContactBlocked(onion, true) -} - -func (this *GrandCentralDispatcher) unblockPeer(onion string) { - err := the.Peer.UnblockPeer(onion) - if err != nil { - this.InvokePopup("Error Unblocking Peer: " + err.Error()) + this.RemoveContact(onion) + this.GetUiManager(this.selectedProfile()).AddContact(onion) + if model.Authorization(authorization) == model.AuthApproved { + the.Peer.PeerWithOnion(onion) } - the.Peer.PeerWithOnion(onion) - this.UpdateContactBlocked(onion, false) } func (this *GrandCentralDispatcher) inviteToGroup(onion, groupID string) { @@ -622,7 +619,12 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) { the.Peer.SetAttribute(attr.GetPublicScope(constants.Picture), ImageToString(pic)) } tag, _ := the.Peer.GetAttribute(app.AttributeTag) - this.UpdateMyProfile(the.Peer.GetName(), the.Peer.GetOnion(), getPicturePath(pic), tag) + showBlocked, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.ShowBlocked)) + if !exists { + showBlocked = "false" + the.Peer.SetAttribute(attr.GetSettingsScope(constants.ShowBlocked), showBlocked) + } + this.UpdateMyProfile(the.Peer.GetName(), the.Peer.GetOnion(), getPicturePath(pic), tag, showBlocked) contacts := the.Peer.GetContacts() for i := range contacts { @@ -658,6 +660,10 @@ func (this *GrandCentralDispatcher) changePassword(onion, currentPassword, newPa the.CwtchApp.ChangePeerPassword(onion, currentPassword, newPassword) } +func (this *GrandCentralDispatcher) storeSetting(key, val string) { + the.Peer.SetAttribute(attr.GetSettingsScope(key), val) +} + func (this *GrandCentralDispatcher) reloadProfileList() { this.ResetProfileList() diff --git a/go/ui/manager.go b/go/ui/manager.go index c22d92ba..87d7fd75 100644 --- a/go/ui/manager.go +++ b/go/ui/manager.go @@ -123,7 +123,7 @@ func getProfilePic(id string) string { return getPicturePath(pic) } } - return getPicturePath(NewImage("fontawesome/regular/user.svg", TypeImageDistro)) + return RandomProfileImage(id) } } @@ -241,7 +241,7 @@ func (this *manager) AddContact(handle string) { unread := countUnread(group.Timeline.GetMessages(), lastRead) picture := getProfilePic(handle) - this.gcd.AddContact(handle, getNick(handle), picture, unread, int(connections.ConnectionStateToType[group.State]), false, false, getLastMessageTime(&group.Timeline)) + this.gcd.AddContact(handle, getNick(handle), picture, unread, int(connections.ConnectionStateToType[group.State]), string(model.AuthApproved), false, getLastMessageTime(&group.Timeline)) } return } else if !isPeer(handle) { @@ -256,7 +256,7 @@ func (this *manager) AddContact(handle string) { unread := countUnread(contact.Timeline.GetMessages(), lastRead) picture := getProfilePic(handle) - this.gcd.AddContact(handle, getNick(handle), 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]), string(contact.Authorization), false, getLastMessageTime(&contact.Timeline)) } }) } diff --git a/go/ui/settings.go b/go/ui/settings.go index 1b4e4208..18ffdcfd 100644 --- a/go/ui/settings.go +++ b/go/ui/settings.go @@ -1,15 +1,11 @@ package ui import ( - "crypto/rand" "cwtch.im/cwtch/storage/v1" "cwtch.im/ui/go/the" "encoding/json" "git.openprivacy.ca/openprivacy/log" "github.com/therecipe/qt/core" - "golang.org/x/crypto/pbkdf2" - "golang.org/x/crypto/sha3" - "io" "io/ioutil" "os" "path" @@ -31,34 +27,12 @@ var DefaultGlobalSettings = GlobalSettings{ Theme: "light", } -// createKeySalt derives a key from a password: returns key, salt, err -func createKeySalt(password string) ([32]byte, [128]byte, error) { - var salt [128]byte - if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil { - log.Errorf("Cannot read from random: %v\n", err) - return [32]byte{}, salt, err - } - dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512) - - var dkr [32]byte - copy(dkr[:], dk) - return dkr, salt, nil -} - -func createKey(password string, salt []byte) [32]byte { - dk := pbkdf2.Key([]byte(password), salt, 4096, 32, sha3.New512) - - var dkr [32]byte - copy(dkr[:], dk) - return dkr -} - func InitGlobalSettingsFile(directory string, password string) error { var key [32]byte salt, err := ioutil.ReadFile(path.Join(directory, saltFile)) if err != nil { var newSalt [128]byte - key, newSalt, err = createKeySalt(password) + key, newSalt, err = v1.CreateKeySalt(password) if err != nil { return err } @@ -68,7 +42,7 @@ func InitGlobalSettingsFile(directory string, password string) error { return err } } else { - key = createKey(password, salt) + key = v1.CreateKey(password, salt) } the.GlobalSettingsFile = v1.NewFileStore(directory, GlobalSettingsFilename, key) diff --git a/i18n/translation_de.ts b/i18n/translation_de.ts index b843b25d..5e0ea234 100644 --- a/i18n/translation_de.ts +++ b/i18n/translation_de.ts @@ -55,11 +55,16 @@ ContactList - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." Adresse hier hinzufügen, um einen Kontakt aufzunehmen + + + blocked + + GroupSettingsPane @@ -227,38 +232,38 @@ PeerSettingsPane - + address-label Adresse - + copy-btn Kopieren - + copied-to-clipboard-notification notification: copied to clipboard in die Zwischenablage kopiert - + display-name-label Angezeigter Name - + save-btn speichern - + delete-btn löschen - + block-btn @@ -371,7 +376,7 @@ ProfileList - + add-new-profile-btn @@ -447,34 +452,34 @@ - + large-text-label Groß - + setting-theme Theme - + theme-light - + theme-dark - + version %1 Version %1 - + builddate %2 Built on: %2 diff --git a/i18n/translation_en.qm b/i18n/translation_en.qm index 49063bb7ca7c3562f06fbd5367ca76ffd8fc6f6f..295db41bb087d625d60951b6ace442ed548469fa 100644 GIT binary patch delta 678 zcmX9*ZAepL7(Mr1cR$|yG4GmFvM;25Y2lAH`e7Pcfo2dPCN+ZPN198k4T7>QMEDIiBe#I8o5DG97vR`0-22r6C_}=WcmlZ6W(KdOExaTO z%djOo+es}qEZuv$0mfP0oE)W=wPH(=0r0~qWWD0zAiY_M)L2J#by4cbXd)?7Qh%AB zMnQVv>;hIik>>84A?XL?m8UO%12zL}4Li zaWIAIQN0?bWpDX>gL8`PS}Hd;bl_04+$ zaap}P&r_(pI&C8d<~7a!VU7k|NTJ}=9LI9Vz=GD(P6PPADP&Vx|9lo;y{-)BH(1h z^MDedpXZAxDJ91k@sE+i9^>^{LVc%In<}R2k~QVHw1M_9*>{Rha`^^|;YBm5(TG~q k$p2|XI?^F;b9G~FjYM~~q`ArOsrHvQ`22mx(*_;?0P6d(pa1{> delta 621 zcmXAmZAepb6vm%>uiL%*-PRdKBBB~(X)&m<7lR(!ryqaLdCv2k^PjCqRGjyP_rU+U z6;Pg`zcNH#l$_}WKpm9I?#BVCRytYl01EF)@zUpjF)j^Em$9!}N-6b#wpvQhWB~h* zyw=OQH!0U`+6fc|?nQ@=`Eyu`<7HCmlL$_RDZK3{^7<62br0%6tCZoSJ=utiG;0zko< zJoR<1h$jO0?vP)}PVxsCa7Bt%kTb5Kq4x|P6iUYC;FvI)djPB#6_>cX_$1;@HGn%K z217C4bXMG}rm<_Ocq&*o8pZr@7GZdPiQl2WG|uUb(G6_u)Z25dfHSS%$jKZ$sb{@R zZtpdGFXw1*HcthH|Il&&_Eu|QzVR3u9(Kn#>0gP231UV`v_n*ncI6e^CE}M zhA0SE#nv|ra>c5w-NE1gY1P{tAZb>^ZC(Jd&QwvTeX*W& mWO(|6*5nD&wB5#sjjWEj|8MN^?dO*dBqQ>IluQ;3`Tqg$T&6|< diff --git a/i18n/translation_en.ts b/i18n/translation_en.ts index 91af2302..d075bdc8 100644 --- a/i18n/translation_en.ts +++ b/i18n/translation_en.ts @@ -55,11 +55,16 @@ ContactList - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." ... paste an address here to add a contact... + + + blocked + Blocked + EmojiDrawer @@ -322,33 +327,33 @@ Right-click to reset. PeerSettingsPane - + address-label Address - + copy-btn Copy - + copied-to-clipboard-notification notification: copied to clipboard Copied to Clipboard - + display-name-label Display Name - + save-btn Save - + block-btn Block Peer @@ -357,7 +362,7 @@ Right-click to reset. Unblock Peer - + delete-btn Delete @@ -478,7 +483,7 @@ Right-click to reset. ProfileList - + add-new-profile-btn Add new profile @@ -563,34 +568,34 @@ Right-click to reset. Zoom level - + large-text-label Large - + setting-theme Theme Theme - + theme-light Light - + theme-dark Dark - + version %1 Version %1 Version %1 - + builddate %2 Built on: %2 Built on: %2 diff --git a/i18n/translation_fr.ts b/i18n/translation_fr.ts index 0193e6f0..f80bbf5b 100644 --- a/i18n/translation_fr.ts +++ b/i18n/translation_fr.ts @@ -55,11 +55,16 @@ ContactList - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." ... coller une adresse ici pour ajouter un contact... + + + blocked + + GroupSettingsPane @@ -227,38 +232,38 @@ PeerSettingsPane - + address-label Adresse - + copy-btn Copier - + copied-to-clipboard-notification notification: copied to clipboard Copié dans le presse-papier - + display-name-label Pseudo - + save-btn Sauvegarder - + delete-btn Effacer - + block-btn @@ -371,7 +376,7 @@ ProfileList - + add-new-profile-btn @@ -447,34 +452,34 @@ - + large-text-label Large - + setting-theme Theme - + theme-light - + theme-dark - + version %1 Version %1 - + builddate %2 Built on: %2 diff --git a/i18n/translation_pt.ts b/i18n/translation_pt.ts index 4bcda35a..0f7938bc 100644 --- a/i18n/translation_pt.ts +++ b/i18n/translation_pt.ts @@ -55,11 +55,16 @@ ContactList - + paste-address-to-add-contact ex: "... paste an address here to add a contact ..." … cole um endereço aqui para adicionar um contato… + + + blocked + + GroupSettingsPane @@ -227,38 +232,38 @@ PeerSettingsPane - + address-label Endereço - + copy-btn Copiar - + copied-to-clipboard-notification notification: copied to clipboard Copiado - + display-name-label Nome de Exibição - + save-btn Salvar - + delete-btn Deletar - + block-btn @@ -371,7 +376,7 @@ ProfileList - + add-new-profile-btn @@ -447,34 +452,34 @@ - + large-text-label Grande - + setting-theme Theme - + theme-light - + theme-dark - + version %1 Version %1 - + builddate %2 Built on: %2 diff --git a/qml/const/Const.qml b/qml/const/Const.qml new file mode 100644 index 00000000..3f4b8986 --- /dev/null +++ b/qml/const/Const.qml @@ -0,0 +1,14 @@ +pragma Singleton + +import QtQuick 2.0 + +Item { + + // defined in cwtch.im/cwtch/model/profile.go ln24 + readonly property string auth_unknown: "unknown" + readonly property string auth_blocked: "blocked" + readonly property string auth_approved: "approved" + + // defined in cwtch.im/ui/go/constants/attributes.go + readonly property string show_blocked: "show-blocked" +} diff --git a/qml/const/qmldir b/qml/const/qmldir new file mode 100644 index 00000000..4830b8b4 --- /dev/null +++ b/qml/const/qmldir @@ -0,0 +1 @@ +singleton Const 1.0 Const.qml \ No newline at end of file diff --git a/qml/main.qml b/qml/main.qml index bcd04f89..6b033d8d 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -348,8 +348,6 @@ ApplicationWindow { if (Qt.application.state == 4) { // Active gcd.onActivate() - console.log(Fonts.applicationFontRegular.name) - console.log(Fonts.applicationFontBold.name) } } } diff --git a/qml/opaque b/qml/opaque index 0ec6a2df..05059341 160000 --- a/qml/opaque +++ b/qml/opaque @@ -1 +1 @@ -Subproject commit 0ec6a2df571e1a9c462d902cfc89f2402a9bef97 +Subproject commit 0505934172c42fc589f8c0e095e1e8f0efeda245 diff --git a/qml/panes/PeerSettingsPane.qml b/qml/panes/PeerSettingsPane.qml index b6b9904c..dffded18 100644 --- a/qml/panes/PeerSettingsPane.qml +++ b/qml/panes/PeerSettingsPane.qml @@ -10,12 +10,13 @@ import QtQuick.Controls.Styles 1.4 import "../opaque" as Opaque import "../opaque/styles" import "../opaque/theme" +import "../const" Opaque.SettingsList { // settingsPane id: root anchors.fill: parent - property bool blocked + property string authorization settings: Column { anchors.fill: parent @@ -59,8 +60,6 @@ Opaque.SettingsList { // settingsPane } - - Opaque.Setting { label: qsTr("block-btn") @@ -68,15 +67,19 @@ Opaque.SettingsList { // settingsPane field: Opaque.ToggleSwitch { anchors.right: parent.right - isToggled: root.blocked // ? qsTr("unblock-btn") : qsTr("block-btn") + isToggled: root.authorization == Const.auth_blocked onToggled: function() { - if (root.blocked) { - gcd.unblockPeer(txtOnion.text) + console.log("peer block toddle for " + txtOnion.text + " currently: " + root.authorization) + if (root.authorization == Const.auth_blocked) { + root.authorization = Const.auth_unknown + console.log("setPeerAuthorization to " + Const.auth_unknown + " for " + txtOnion.text) + gcd.setPeerAuthorization(txtOnion.text, Const.auth_unknown) } else { - gcd.blockPeer(txtOnion.text) + root.authorization = Const.auth_blocked + console.log("setPeerAuthorization to " + Const.auth_blocked + " for " + txtOnion.text) + gcd.setPeerAuthorization(txtOnion.text, Const.auth_blocked) } - root.blocked = !root.blocked - isToggled = root.blocked + isToggled = root.authorization == Const.auth_blocked } } } @@ -103,10 +106,10 @@ Opaque.SettingsList { // settingsPane Connections { target: gcd - onSupplyPeerSettings: function(onion, nick, blocked) { + onSupplyPeerSettings: function(onion, nick, authorization) { txtOnion.text = onion txtDisplayName.text = nick - root.blocked = blocked + root.authorization = authorization } } diff --git a/qml/panes/SettingsPane.qml b/qml/panes/SettingsPane.qml index 914d967f..35342804 100644 --- a/qml/panes/SettingsPane.qml +++ b/qml/panes/SettingsPane.qml @@ -193,43 +193,6 @@ text: qsTr("block-unknown-label") } } } - - -======= -} - - -/* Opaque.ScalingLabel { -width: parent.width -wrapMode: TextEdit.Wrap -//: Interface zoom (mostly affects text and button sizes) -text: qsTr("zoom-label") + ":" -} - - - - -CheckBox { -id: blockUnknownToggle -checked: true -onClicked: { -if (blockUnknownToggle.checked) { -gcd.blockUnknownPeers() -} else { -gcd.allowUnknownPeers() -} -} -style: CheckBoxStyle { -label: Opaque.ScalingLabel { -text: qsTr("block-unknown-label") -} -} -} - - ->>>>>>> 007b851f... Add global settings pane; migrate peer settings to new opaque settings widgets; minor fixes; add global settings storage; rework global settings settings - - */ diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index f2cf8fdf..c9aa3797 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -6,11 +6,13 @@ import QtQuick.Layouts 1.3 import "../opaque" as Opaque import "../opaque/theme" +import "../const" ColumnLayout { id: root property alias dualPane: myprof.dualPane + property real logscale: 4 * Math.log10(gcd.themeScale + 1) spacing: 10 @@ -26,6 +28,16 @@ ColumnLayout { id: myprof } + function removeContact(model, handle) { + for(var i = 0; i < model.count; i++){ + if(model.get(i)["_handle"] == handle) { + model.remove(i) + return true + } + } + return false + } + Opaque.IconTextField { id: searchAddText @@ -61,10 +73,9 @@ ColumnLayout { Layout.fillHeight: true Layout.minimumWidth: parent.width Layout.maximumWidth: parent.width - contentWidth: colContacts.width + contentWidth: parent.width contentHeight: colContacts.height boundsBehavior: Flickable.StopAtBounds - maximumFlickVelocity: 400 ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded @@ -80,6 +91,8 @@ ColumnLayout { } } + + ColumnLayout { id: colContacts width: root.width @@ -88,17 +101,29 @@ ColumnLayout { Connections { // ADD/REMOVE CONTACT ENTRIES target: gcd - onAddContact: function(handle, displayName, image, badge, status, blocked, loading, lastMsgTs) { + onAddContact: function(handle, displayName, image, badge, status, authorization, loading, lastMsgTs) { + var model = contactsModel + if (authorization == Const.auth_blocked) { + model = blockedContactsModel + } - for (var i = 0; i < contactsModel.count; i++) { - if (contactsModel.get(i)["_handle"] == handle) { + for (var i = 0; i < model.count; i++) { + if (model.get(i)["_handle"] == handle) { return } } - var index = contactsModel.count - for (var i = 0; i < contactsModel.count; i++) { - if (contactsModel.get(i)["_lastMsgTs"] < lastMsgTs) { + // Sort order: [unknown,authed][most recent message] + var index = model.count + for (var i = 0; i < model.count; i++) { + var contact = model.get(i) + + if (contact["_authorization"] == authorization) { + if (contact["_lastMsgTs"] < lastMsgTs) { + index = i + break + } + } else if (authorization == Const.auth_unknown) { index = i break } @@ -106,26 +131,23 @@ ColumnLayout { var newContact = { "_handle": handle, - "_displayName": displayName + (blocked ? " (blocked)" : "" ), + "_displayName": displayName, "_image": image, "_badge": badge, "_status": status, - "_blocked": blocked, + "_authorization": authorization, "_loading": loading, "_loading": loading, "_lastMsgTs": lastMsgTs } - contactsModel.insert(index, newContact) + model.insert(index, newContact) } onRemoveContact: function(handle) { - for(var i = 0; i < contactsModel.count; i++){ - if(contactsModel.get(i)["_handle"] == handle) { - console.log("deleting contact " + contactsModel.get(i)["_handle"]) - contactsModel.remove(i) - return - } + var removed = root.removeContact(contactsModel, handle) + if (!removed) { + root.removeContact(blockedContactsModel, handle) } } @@ -142,6 +164,7 @@ ColumnLayout { onResetProfile: function() { contactsModel.clear() + blockedContactsModel.clear() } } @@ -150,6 +173,7 @@ ColumnLayout { } Repeater { + id: contactRepeater model: contactsModel // ... AND DISPLAYED HERE delegate: ContactRow { handle: _handle @@ -157,17 +181,124 @@ ColumnLayout { image: _image badge: _badge status: _status - blocked: _blocked + authorization: _authorization loading: _loading + rowColor: (_authorization == Const.auth_unknown) ? Theme.backgroundHilightElementColor : Theme.backgroundMainColor + Layout.fillWidth: true } } + + Item { + id: blockItem + height: blockedToggle.height + Layout.fillWidth: true + visible: blockedContactsModel.count > 0 + + MouseArea { + anchors.fill: blockItem + + hoverEnabled: true + + onClicked: { + blockedToggle.showing = !blockedToggle.showing + blockedContacts.visible = blockedToggle.showing + if (blockedToggle.showing) { + gcd.storeSetting(Const.show_blocked, "true") + } else { + gcd.storeSetting(Const.show_blocked, "false") + } + } + + onEntered: { + blockedBG.color = Theme.backgroundPaneColor + } + + onExited: { + blockedBG.color = Theme.backgroundMainColor + } + } + + Rectangle { + id: blockedBG + property bool isHover: false + + anchors.fill: blockItem + color: Theme.backgroundMainColor + + Connections { + target: Theme + + onThemeChanged: { + blockedBG.color = Theme.backgroundMainColor + } + } + } + + Row { + id: blockedToggle + property bool showing: true + + leftPadding: 32 * logscale + topPadding: 16 * logscale + bottomPadding: 8 * logscale + spacing: 5 * logscale + + Opaque.ScalingLabel { + id: blockLbl + + text: qsTr("blocked") + size: Theme.chatMetaTextSize + color: Theme.portraitBlockedTextColor + } + + Opaque.ScalingLabel { + id: blockBtn + + text: blockedToggle.showing ? "▲" : "▼" + size: Theme.chatMetaTextSize + color: Theme.portraitBlockedTextColor + } + } + + Connections { + target: gcd + + onUpdateMyProfile: function(_nick, _onion, _image, _tag, _showBlocked) { + blockedToggle.showing = (_showBlocked == "true") + blockedContacts.visible = (_showBlocked == "true") + } + } + } + + + ListModel { + id: blockedContactsModel + } + + ColumnLayout { + id: blockedContacts + Layout.fillWidth: true + spacing: 0 + + Repeater { + id: blockedContactsRepeater + model: blockedContactsModel // ... AND DISPLAYED HERE + delegate: ContactRow { + handle: _handle + displayName: _displayName + image: _image + badge: _badge + status: _status + authorization: _authorization + loading: _loading + rowColor: Theme.backgroundHilightElementColor + rowHilightColor: Theme.backgroundMainColor + Layout.fillWidth: true + } + } + } } } - - } - - - diff --git a/qml/widgets/ContactRow.qml b/qml/widgets/ContactRow.qml index 061fe130..4be5090b 100644 --- a/qml/widgets/ContactRow.qml +++ b/qml/widgets/ContactRow.qml @@ -10,11 +10,16 @@ import QtQuick.Controls.Styles 1.4 import "../opaque" as Opaque import "../opaque/styles" import "../opaque/theme" +import "../const" Opaque.PortraitRow { property int status: 0 property int badge property bool loading + property string authorization + + // TODO: should be in ContactRow + property bool blocked badgeColor: Theme.portraitContactBadgeColor badgeVisible: badge > 0 @@ -48,6 +53,35 @@ Opaque.PortraitRow { } } + Column { + visible: authorization == Const.auth_unknown + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 1 * gcd.themeScale + anchors.rightMargin: 25 * gcd.themeScale + spacing: 16 * gcd.themeScale + + Opaque.Icon { + source: gcd.assetPath + "core/favorite-24px.svg" + iconColor: Theme.toolbarIconColor + backgroundColor: rowColor + height: 18 * gcd.themeScale + width: 18 * gcd.themeScale + + onClicked: { gcd.setPeerAuthorization(handle, Const.auth_approved)} + } + + Opaque.Icon { + source: gcd.assetPath + "core/delete-24px.svg" + iconColor: Theme.toolbarIconColor + backgroundColor: rowColor + height: 18 * gcd.themeScale + width: 18 * gcd.themeScale + + onClicked: { console.log("approve"); gcd.setPeerAuthorization(handle, Const.auth_blocked)} + } + } + onClicked: function(handle) { gcd.broadcast("ResetMessagePane") isActive = true @@ -65,7 +99,13 @@ Opaque.PortraitRow { function setColors(status) { //-2:WtfCodeError,-1:Error,0:Disconnected,1:Connecting,2:Connected,3:Authenticated,4:Synced,5:Failed,6:Killed - if (status == 4 || status == 3) { + + if (authorization == Const.auth_blocked) { + portraitBorderColor = Theme.portraitBlockedBorderColor + portraitColor = Theme.portraitBlockedBackgroundColor + nameColor = Theme.portraitBlockedTextColor + onionColor = Theme.portraitBlockedTextColor + } else if (status == 4 || status == 3) { portraitBorderColor = Theme.portraitOnlineBorderColor portraitColor = Theme.portraitOnlineBackgroundColor nameColor = Theme.portraitOnlineTextColor diff --git a/qml/widgets/MyProfile.qml b/qml/widgets/MyProfile.qml index 70c6269b..45f6a348 100644 --- a/qml/widgets/MyProfile.qml +++ b/qml/widgets/MyProfile.qml @@ -139,12 +139,11 @@ Item { Connections { target: gcd - onUpdateMyProfile: function(_nick, _onion, _image, _tag) { + onUpdateMyProfile: function(_nick, _onion, _image, _tag, _showBlocked) { nick = _nick onion = _onion image = _image tag = _tag - //realignProfile() } onResetProfile: { realignProfile() } diff --git a/qml/widgets/ProfileList.qml b/qml/widgets/ProfileList.qml index 1a65846c..2286770a 100644 --- a/qml/widgets/ProfileList.qml +++ b/qml/widgets/ProfileList.qml @@ -98,8 +98,8 @@ ColumnLayout { handle: _handle displayName: _displayName image: _image - blocked: false tag: _tag + Layout.fillWidth: true } } -- 2.25.1 From 94e30f496297b6bb37103a496bb56150e9b5663a Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Mon, 20 Jul 2020 16:36:11 -0700 Subject: [PATCH 3/3] blocked contacts have normal row colors --- qml/widgets/ContactList.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index c9aa3797..157f9b56 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -293,8 +293,6 @@ ColumnLayout { status: _status authorization: _authorization loading: _loading - rowColor: Theme.backgroundHilightElementColor - rowHilightColor: Theme.backgroundMainColor Layout.fillWidth: true } } -- 2.25.1