From 7a7be2777b93b000c6100e0d8912a6e0a1d5e7c6 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Tue, 19 Nov 2019 13:36:28 -0800 Subject: [PATCH 1/3] 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 2/3] 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 541f4f7da44155ce9b6c58e3def3b47112076edf Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Fri, 29 Nov 2019 17:35:57 -0800 Subject: [PATCH 3/3] 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