profile manager: screen, unlock, select, back to #238

Manually merged
sarah merged 1 commits from :profileMan into master 2019-11-19 19:26:51 +00:00
18 changed files with 505 additions and 122 deletions

View File

@ -3,3 +3,4 @@ package constants
const Nick = "nick"
const LastRead = "last-read"
const Picture = "picture"
const DefaultPassword = "default-password"

View File

@ -12,7 +12,7 @@ import (
"time"
)
func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) {
func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool, reloadingFirst bool) {
q := event.NewQueue()
the.AppBus.Subscribe(event.NewPeer, q)
the.AppBus.Subscribe(event.PeerError, q)
@ -21,9 +21,12 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) {
the.AppBus.Subscribe(event.NetworkStatus, q)
the.AppBus.Subscribe(event.ReloadDone, q)
subscribed <- true
loadingV1Accounts := !reloadingFirst
networkOffline := false
timeSinceLastSuccess := time.Unix(0,0)
timeSinceLastSuccess := time.Unix(0, 0)
gcd.Loaded()
for {
e := q.Next()
@ -74,65 +77,54 @@ func App(gcd *ui.GrandCentralDispatcher, subscribed chan bool) {
case event.AppError:
if e.Data[event.Error] == event.AppErrLoaded0 {
// TODO: only an error if other profiles are not loaded
log.Infoln("couldn't load your config file. attempting to create one now")
if gcd.Version() == "development" {
the.CwtchApp.CreatePeer("tester", the.AppPassword)
if loadingV1Accounts {
loadingV1Accounts = false
// TODO: only an error if other profiles are not loaded
if len(the.CwtchApp.ListPeers()) == 0 {
log.Infoln("couldn't load your config file. attempting to create one now")
if gcd.Version() == "development" {
the.CwtchApp.CreatePeer("tester", the.AppPassword)
} else {
the.CwtchApp.CreatePeer("alice", the.AppPassword)
}
}
} else {
the.CwtchApp.CreatePeer("alice", the.AppPassword)
gcd.ErrorLoaded0()
}
}
case event.ReloadDone:
if the.Peer == nil {
loadingV1Accounts = true
the.CwtchApp.LoadProfiles(the.AppPassword)
}
case event.NewPeer:
if the.Peer != nil {
continue
}
onion := e.Data[event.Identity]
peer := the.CwtchApp.GetPeer(onion)
if loadingV1Accounts {
the.CwtchApp.GetPeer(onion).SetAttribute(constants.DefaultPassword, "true")
loadingV1Accounts = false
}
log.Infof("NewPeer for %v\n", onion)
gcd.UIManager.AddProfile(onion)
the.CwtchApp.AddPeerPlugin(onion, plugins.CONNECTIONRETRY)
the.CwtchApp.AddPeerPlugin(onion, plugins.NETWORKCHECK)
the.Peer = the.CwtchApp.GetPeer(onion)
the.EventBus = the.CwtchApp.GetEventBus(onion)
incSubscribed := make(chan bool)
go PeerHandler(&gcd.UIManager, incSubscribed)
go PeerHandler(onion, &gcd.UIManager, incSubscribed)
<-incSubscribed
pic, exists := the.Peer.GetAttribute(constants.Picture)
if !exists {
pic = ui.RandomProfileImage(the.Peer.GetProfile().Onion)
the.Peer.SetAttribute(constants.Picture, pic)
}
gcd.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, pic)
contacts := the.Peer.GetContacts()
for i := range contacts {
gcd.UIManager.AddContact(contacts[i])
}
groups := the.Peer.GetGroups()
for i := range groups {
// Only join servers for active and explicitly accepted groups.
gcd.UIManager.AddContact(groups[i])
}
if e.Data[event.Status] != "running" {
the.CwtchApp.LaunchPeers()
peer.Listen()
peer.StartPeersConnections()
peer.StartGroupConnections()
}
// load ui preferences
gcd.RequestSettings()
locale, exists := the.Peer.GetProfile().GetAttribute(constants.LocaleSetting)
if exists {
gcd.SetLocale_helper(locale)
}
blockUnkownPeers, exists := the.Peer.GetProfile().GetAttribute(constants.BlockUnknownPeersSetting)
blockUnkownPeers, exists := peer.GetProfile().GetAttribute(constants.BlockUnknownPeersSetting)
if exists && blockUnkownPeers == "true" {
the.EventBus.Publish(event.NewEvent(event.BlockUnknownPeers, map[event.Field]string{}))
}

View File

@ -9,18 +9,20 @@ import (
"time"
)
func PeerHandler(uiManager *ui.Manager, subscribed chan bool) {
func PeerHandler(onion string, uiManager *ui.Manager, subscribed chan bool) {
peer := the.CwtchApp.GetPeer(onion)
eventBus := the.CwtchApp.GetEventBus(onion)
q := event.NewQueue()
the.EventBus.Subscribe(event.NewMessageFromPeer, q)
the.EventBus.Subscribe(event.PeerAcknowledgement, q)
the.EventBus.Subscribe(event.NewMessageFromGroup, q)
the.EventBus.Subscribe(event.NewGroupInvite, q)
the.EventBus.Subscribe(event.SendMessageToGroupError, q)
the.EventBus.Subscribe(event.SendMessageToPeerError, q)
the.EventBus.Subscribe(event.ServerStateChange, q)
the.EventBus.Subscribe(event.PeerStateChange, q)
the.EventBus.Subscribe(event.PeerCreated, q)
the.EventBus.Subscribe(event.NetworkStatus, q)
eventBus.Subscribe(event.NewMessageFromPeer, q)
eventBus.Subscribe(event.PeerAcknowledgement, q)
eventBus.Subscribe(event.NewMessageFromGroup, q)
eventBus.Subscribe(event.NewGroupInvite, q)
eventBus.Subscribe(event.SendMessageToGroupError, q)
eventBus.Subscribe(event.SendMessageToPeerError, q)
eventBus.Subscribe(event.ServerStateChange, q)
eventBus.Subscribe(event.PeerStateChange, q)
eventBus.Subscribe(event.PeerCreated, q)
eventBus.Subscribe(event.NetworkStatus, q)
subscribed <- true
@ -43,8 +45,8 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) {
case event.NewMessageFromPeer: //event.TimestampReceived, event.RemotePeer, event.Data
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampReceived])
uiManager.AddMessage(e.Data[event.RemotePeer], e.Data[event.RemotePeer], e.Data[event.Data], false, e.EventID, ts, true)
if the.Peer.GetContact(e.Data[event.RemotePeer]) == nil {
the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
if peer.GetContact(e.Data[event.RemotePeer]) == nil {
peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
}
case event.PeerAcknowledgement:
@ -52,10 +54,10 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) {
case event.NewMessageFromGroup: //event.TimestampReceived, event.TimestampSent, event.Data, event.GroupID, event.RemotePeer
ts, _ := time.Parse(time.RFC3339Nano, e.Data[event.TimestampSent])
uiManager.AddMessage(e.Data[event.GroupID], e.Data[event.RemotePeer], e.Data[event.Data], e.Data[event.RemotePeer] == the.Peer.GetProfile().Onion, e.Data[event.Signature], ts, true)
uiManager.AddMessage(e.Data[event.GroupID], e.Data[event.RemotePeer], e.Data[event.Data], e.Data[event.RemotePeer] == peer.GetProfile().Onion, e.Data[event.Signature], ts, true)
case event.NewGroupInvite:
gid, err := the.Peer.GetProfile().ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer])
group := the.Peer.GetGroup(gid)
gid, err := peer.GetProfile().ProcessInvite(e.Data[event.GroupInvite], e.Data[event.RemotePeer])
group := peer.GetGroup(gid)
if err == nil && group != nil {
uiManager.AddContact(gid)
}
@ -70,18 +72,18 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) {
cxnState := connections.ConnectionStateToType[e.Data[event.ConnectionState]]
// if it's not in the.PeerHandler it's new. Only add once Authed
if _, exists := the.Peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; !exists {
if _, exists := peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; !exists {
// Contact does not exist, we will add them but we won't know who they are until they are authenticated
// So if we get any other state from an unknown contact we do nothing
// (the next exists check will fail)
if cxnState == connections.AUTHENTICATED {
the.Peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
peer.AddContact(e.Data[event.RemotePeer], e.Data[event.RemotePeer], false)
uiManager.AddContact(e.Data[event.RemotePeer])
}
}
// if it's in the.PeerHandler its either existing and needs an update or not in the UI and needs to be added
if contact, exists := the.Peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; exists {
if contact, exists := peer.GetProfile().Contacts[e.Data[event.RemotePeer]]; exists {
contact.State = e.Data[event.ConnectionState]
uiManager.UpdateContactStatus(contact.Onion, int(cxnState), false)
@ -89,9 +91,9 @@ func PeerHandler(uiManager *ui.Manager, subscribed chan bool) {
case event.ServerStateChange:
serverOnion := e.Data[event.GroupServer]
state := connections.ConnectionStateToType[e.Data[event.ConnectionState]]
groups := the.Peer.GetGroups()
groups := peer.GetGroups()
for _, groupID := range groups {
group := the.Peer.GetGroup(groupID)
group := peer.GetGroup(groupID)
if group != nil && group.GroupServer == serverOnion {
group.State = e.Data[event.ConnectionState]
loading := false

View File

@ -27,6 +27,12 @@ type GrandCentralDispatcher struct {
_ string `property:"version"`
_ string `property:"buildDate"`
// profile management stuff
_ func() `signal:"Loaded"`
_ func(handle, displayname, image string) `signal:"AddProfile"`
_ func() `signal:"ErrorLoaded0"`
_ func() `signal:"ResetProfile"`
// contact list stuff
_ func(handle, displayName, image, server string, badge, status int, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"`
_ func(handle, displayName string) `signal:"UpdateContactDisplayName"`
@ -82,6 +88,8 @@ type GrandCentralDispatcher struct {
_ func() `signal:"allowUnknownPeers,auto"`
_ func() `signal:"blockUnknownPeers,auto"`
_ func() `signal:"onActivate,auto"`
_ func(password string) `signal:"unlockProfiles,auto"`
_ func(handle string) `signal:"loadProfile,auto"`
}
func (this *GrandCentralDispatcher) sendMessage(message string, mID string) {
@ -295,6 +303,8 @@ func (this *GrandCentralDispatcher) broadcast(signal string) {
log.Debugf("unhandled broadcast signal: %v", signal)
case "ResetMessagePane":
this.ResetMessagePane()
case "ResetProfile":
this.ResetProfile()
}
}
@ -477,3 +487,37 @@ func (this *GrandCentralDispatcher) SetLocale_helper(locale string) {
core.QCoreApplication_InstallTranslator(this.Translator)
this.QMLEngine.Retranslate()
}
func (this *GrandCentralDispatcher) unlockProfiles(password string) {
the.CwtchApp.LoadProfiles(password)
}
func (this *GrandCentralDispatcher) loadProfile(onion string) {
the.Peer = the.CwtchApp.GetPeer(onion)
the.EventBus = the.CwtchApp.GetEventBus(onion)
pic, exists := the.Peer.GetAttribute(constants.Picture)
if !exists {
pic = RandomProfileImage(the.Peer.GetProfile().Onion)
the.Peer.SetAttribute(constants.Picture, pic)
}
this.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, pic)
contacts := the.Peer.GetContacts()
for i := range contacts {
this.UIManager.AddContact(contacts[i])
}
groups := the.Peer.GetGroups()
for i := range groups {
// Only join servers for active and explicitly accepted groups.
this.UIManager.AddContact(groups[i])
}
// load ui preferences
this.RequestSettings()
locale, exists := the.Peer.GetProfile().GetAttribute(constants.LocaleSetting)
if exists {
this.SetLocale_helper(locale)
}
}

View File

@ -98,7 +98,7 @@ func updateLastReadTime(id string) {
func countUnread(messages []model.Message, lastRead time.Time) int {
count := 0
for i := len(messages) - 1; i >= 0; i-- {
if messages[i].Timestamp.After(lastRead) {
if messages[i].Timestamp.After(lastRead) || messages[i].Timestamp.Equal(lastRead) {
count++
} else {
break
@ -115,6 +115,25 @@ func NewManager(gcd *GrandCentralDispatcher) Manager {
return Manager{gcd}
}
func (this *Manager) AddProfile(handle string) {
peer := the.CwtchApp.GetPeer(handle)
if peer != nil {
nick := peer.GetProfile().Name
if nick == "" {
nick = handle
peer.SetAttribute(constants.Nick, nick)
}
pic, ok := peer.GetAttribute(constants.Picture)
if !ok {
pic = RandomProfileImage(handle)
peer.SetAttribute(constants.Picture, pic)
}
log.Infof("AddProfile %v %v %v\n", handle, nick, pic)
this.gcd.AddProfile(handle, nick, pic)
}
}
func (this *Manager) Acknowledge(mID string) {
this.gcd.Acknowledged(mID)
}
@ -170,13 +189,12 @@ func (this *Manager) AddSendMessageError(peer string, signature string, err stri
}
func (this *Manager) AddMessage(handle string, from string, message string, fromMe bool, messageID string, timestamp time.Time, Acknowledged bool) {
updateLastReadTime(handle)
nick := getOrDefault(handle, constants.Nick, handle)
image := getProfilePic(handle)
// If we have this group loaded already
if this.gcd.CurrentOpenConversation() == handle {
updateLastReadTime(handle)
// If the message is not from the user then add it, otherwise, just acknowledge.
if !fromMe {
this.gcd.AppendMessage(handle, from, nick, message, image, messageID, fromMe, timestamp.Format(constants.TIME_FORMAT), false, false)
@ -187,9 +205,8 @@ func (this *Manager) AddMessage(handle string, from string, message string, from
this.gcd.Acknowledged(messageID)
}
}
} else {
this.gcd.IncContactUnreadCount(handle)
}
this.gcd.IncContactUnreadCount(handle)
}
func (this *Manager) UpdateContactDisplayName(handle string, name string) {

View File

@ -145,18 +145,18 @@
<translation>Klicken, um DM zu senden</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>could-not-send-msg-error</source>
<extracomment>Could not send this message</extracomment>
<translation>Nachricht konnte nicht gesendet werden</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>acknowledged-label</source>
<translation>bestätigt</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>pending-label</source>
<translation>Bestätigung ausstehend</translation>
</message>
@ -164,25 +164,25 @@
<context>
<name>MyProfile</name>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="169"/>
<location filename="../qml/widgets/MyProfile.qml" line="183"/>
<source>copy-btn</source>
<extracomment>Button for copying profile onion address to clipboard</extracomment>
<translation>Kopieren</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="173"/>
<location filename="../qml/widgets/MyProfile.qml" line="187"/>
<source>copied-clipboard-notification</source>
<extracomment>Copied to clipboard</extracomment>
<translation>in die Zwischenablage kopiert</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="203"/>
<location filename="../qml/widgets/MyProfile.qml" line="217"/>
<source>new-group-btn</source>
<extracomment>create new group button</extracomment>
<translation>Neue Gruppe anlegen</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="213"/>
<location filename="../qml/widgets/MyProfile.qml" line="227"/>
<source>paste-address-to-add-contact</source>
<extracomment>ex: &quot;... paste an address here to add a contact ...&quot;</extracomment>
<translation>Adresse hier hinzufügen, um einen Kontakt aufzunehmen</translation>
@ -273,6 +273,27 @@
<translation>löschen</translation>
</message>
</context>
<context>
<name>ProfileManagerPane</name>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="26"/>
<source>enter-profile-password</source>
<extracomment>Please enter password:</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="41"/>
<source>error-0-profiles-loaded-for-password</source>
<extracomment>0 profiles loaded with that password</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="51"/>
<source>unlock</source>
<extracomment>Unlock</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsPane</name>
<message>

Binary file not shown.

View File

@ -103,7 +103,7 @@
<message>
<location filename="../qml/widgets/InplaceEditText.qml" line="85"/>
<source>Update</source>
<translation type="unfinished"></translation>
<translation>Update</translation>
</message>
</context>
<context>
@ -145,18 +145,18 @@
<translation>Click to DM</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>could-not-send-msg-error</source>
<extracomment>Could not send this message</extracomment>
<translation>Could not send this message</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>acknowledged-label</source>
<translation>Acknowledged</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>pending-label</source>
<translation>Pending</translation>
</message>
@ -164,25 +164,25 @@
<context>
<name>MyProfile</name>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="169"/>
<location filename="../qml/widgets/MyProfile.qml" line="183"/>
<source>copy-btn</source>
<extracomment>Button for copying profile onion address to clipboard</extracomment>
<translation>Copy</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="173"/>
<location filename="../qml/widgets/MyProfile.qml" line="187"/>
<source>copied-clipboard-notification</source>
<extracomment>Copied to clipboard</extracomment>
<translation>Copied to clipboard</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="203"/>
<location filename="../qml/widgets/MyProfile.qml" line="217"/>
<source>new-group-btn</source>
<extracomment>create new group button</extracomment>
<translation>Create new group</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="213"/>
<location filename="../qml/widgets/MyProfile.qml" line="227"/>
<source>paste-address-to-add-contact</source>
<extracomment>ex: &quot;... paste an address here to add a contact ...&quot;</extracomment>
<translation>... paste an address here to add a contact...</translation>
@ -273,6 +273,27 @@
<translation>Delete</translation>
</message>
</context>
<context>
<name>ProfileManagerPane</name>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="26"/>
<source>enter-profile-password</source>
<extracomment>Please enter password:</extracomment>
<translation>Please enter password</translation>
</message>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="41"/>
<source>error-0-profiles-loaded-for-password</source>
<extracomment>0 profiles loaded with that password</extracomment>
<translation>0 profiles loaded with that password</translation>
</message>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="51"/>
<source>unlock</source>
<extracomment>Unlock</extracomment>
<translation>Unlock</translation>
</message>
</context>
<context>
<name>SettingsPane</name>
<message>

View File

@ -145,18 +145,18 @@
<translation>Envoyer un message privé</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>could-not-send-msg-error</source>
<extracomment>Could not send this message</extracomment>
<translation>Impossible d&apos;envoyer ce message</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>acknowledged-label</source>
<translation>Confirmé</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>pending-label</source>
<translation>En attente</translation>
</message>
@ -164,25 +164,25 @@
<context>
<name>MyProfile</name>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="169"/>
<location filename="../qml/widgets/MyProfile.qml" line="183"/>
<source>copy-btn</source>
<extracomment>Button for copying profile onion address to clipboard</extracomment>
<translation>Copier</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="173"/>
<location filename="../qml/widgets/MyProfile.qml" line="187"/>
<source>copied-clipboard-notification</source>
<extracomment>Copied to clipboard</extracomment>
<translation>Copié dans le presse-papier</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="203"/>
<location filename="../qml/widgets/MyProfile.qml" line="217"/>
<source>new-group-btn</source>
<extracomment>create new group button</extracomment>
<translation>Créer un nouveau groupe</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="213"/>
<location filename="../qml/widgets/MyProfile.qml" line="227"/>
<source>paste-address-to-add-contact</source>
<extracomment>ex: &quot;... paste an address here to add a contact ...&quot;</extracomment>
<translation>... coller une adresse ici pour ajouter un contact...</translation>
@ -273,6 +273,27 @@
<translation>Effacer</translation>
</message>
</context>
<context>
<name>ProfileManagerPane</name>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="26"/>
<source>enter-profile-password</source>
<extracomment>Please enter password:</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="41"/>
<source>error-0-profiles-loaded-for-password</source>
<extracomment>0 profiles loaded with that password</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="51"/>
<source>unlock</source>
<extracomment>Unlock</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsPane</name>
<message>

View File

@ -145,18 +145,18 @@
<translation>Clique para DM</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>could-not-send-msg-error</source>
<extracomment>Could not send this message</extracomment>
<translation>Não deu para enviar esta mensagem</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>acknowledged-label</source>
<translation>Confirmada</translation>
</message>
<message>
<location filename="../qml/widgets/Message.qml" line="161"/>
<location filename="../qml/widgets/Message.qml" line="162"/>
<source>pending-label</source>
<translation>Pendente</translation>
</message>
@ -164,25 +164,25 @@
<context>
<name>MyProfile</name>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="169"/>
<location filename="../qml/widgets/MyProfile.qml" line="183"/>
<source>copy-btn</source>
<extracomment>Button for copying profile onion address to clipboard</extracomment>
<translation>Copiar</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="173"/>
<location filename="../qml/widgets/MyProfile.qml" line="187"/>
<source>copied-clipboard-notification</source>
<extracomment>Copied to clipboard</extracomment>
<translation>Copiado</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="203"/>
<location filename="../qml/widgets/MyProfile.qml" line="217"/>
<source>new-group-btn</source>
<extracomment>create new group button</extracomment>
<translation>Criar novo grupo</translation>
</message>
<message>
<location filename="../qml/widgets/MyProfile.qml" line="213"/>
<location filename="../qml/widgets/MyProfile.qml" line="227"/>
<source>paste-address-to-add-contact</source>
<extracomment>ex: &quot;... paste an address here to add a contact ...&quot;</extracomment>
<translation> cole um endereço aqui para adicionar um contato</translation>
@ -273,6 +273,27 @@
<translation>Deletar</translation>
</message>
</context>
<context>
<name>ProfileManagerPane</name>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="26"/>
<source>enter-profile-password</source>
<extracomment>Please enter password:</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="41"/>
<source>error-0-profiles-loaded-for-password</source>
<extracomment>0 profiles loaded with that password</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../qml/panes/ProfileManagerPane.qml" line="51"/>
<source>unlock</source>
<extracomment>Unlock</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SettingsPane</name>
<message>

View File

@ -255,7 +255,7 @@ func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, client
if !service {
the.AppBus = the.CwtchApp.GetPrimaryBus()
subscribed := make(chan bool)
go handlers.App(gcd, subscribed)
go handlers.App(gcd, subscribed, clientUI)
<-subscribed
}

View File

@ -13,6 +13,7 @@
<file>qml/panes/PeerSettingsPane.qml</file>
<file>qml/panes/SettingsPane.qml</file>
<file>qml/panes/SplashPane.qml</file>
<file>qml/panes/ProfileManagerPane.qml</file>
<file>qml/styles/CwtchComboBoxStyle.qml</file>
<file>qml/styles/CwtchExpandingButton.qml</file>
<file>qml/styles/CwtchTextAreaStyle.qml</file>

View File

@ -113,9 +113,14 @@ ApplicationWindow {
StackLayout {
id: parentStack
currentIndex: 0
anchors.fill: parent
currentIndex: 0
readonly property int splashPane: 0
readonly property int managementPane: 1
readonly property int profilePane: 2
property alias pane: parentStack.currentIndex
Rectangle { // Splash pane
color: "#FFFFFF"
//Layout.fillHeight: true
@ -131,6 +136,18 @@ ApplicationWindow {
running: true
}
}
Rectangle { // Profile login/management pane
anchors.fill: parent
visible: false
color: "#D2C0DD"
ProfileManagerPane {
id: profilesPane
anchors.fill: parent
}
}
RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR
/* anchors.left: ratio >= 0.92 ? parent.left : toolbar.right
anchors.top: ratio >= 0.92 ? toolbar.bottom : parent.top
@ -247,6 +264,11 @@ ApplicationWindow {
onSetToolbarTitle: function(str) {
theStack.title = str
}
onLoaded: function() {
parentStack.pane = parentStack.managementPane
splashPane.running = false
}
}
Connections {

View File

@ -0,0 +1,84 @@
import QtQuick 2.0
import QtGraphicalEffects 1.0
import QtQuick 2.7
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.11
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import "../widgets"
import "../widgets/controls"
import "../styles"
ColumnLayout {
id: thecol
anchors.fill: parent
//leftPadding: 10
//spacing: 5
ScalingLabel {
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: TextEdit.Wrap
//: Please enter password:
text: qsTr("enter-profile-password")+":"
}
TextField {
id: txtPassword
anchors.horizontalCenter: parent.horizontalCenter
style: CwtchTextFieldStyle{ width: thecol.width * 0.8 }
echoMode: TextInput.Password
}
ScalingLabel {
id: error
anchors.horizontalCenter: parent.horizontalCenter
color: "red"
//: 0 profiles loaded with that password
text: qsTr("error-0-profiles-loaded-for-password")
visible: false
}
SimpleButton {
id: "button"
anchors.horizontalCenter: parent.horizontalCenter
icon: "solid/unlock-alt"
//: Unlock
text: qsTr("unlock")
onClicked: {
gcd.unlockProfiles(txtPassword.text)
txtPassword.text = ""
error.visible = false
}
}
Connections { // ADD/REMOVE CONTACT ENTRIES
target: gcd
onErrorLoaded0: function() {
error.visible = true
}
}
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
color: "#D2C0DD"
width: thecol.width
Layout.fillHeight: true
Layout.fillWidth: true
Layout.minimumWidth: Layout.maximumWidth
//Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : 450
ProfileList {
anchors.fill: parent
}
}
}

View File

@ -99,6 +99,10 @@ ColumnLayout {
contactsModel.move(i, 0, 1)
}
}
}
onResetProfile: function() {
contactsModel.clear()
}
}
@ -117,6 +121,7 @@ ColumnLayout {
status: _status
blocked: _blocked
loading: _loading
type: "contact"
}
}

View File

@ -27,6 +27,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
property alias status: imgProfile.status
property string server
property bool background: true
property string type
Rectangle { // CONTACT ENTRY BACKGROUND COLOR
@ -39,23 +40,42 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
ContactPicture {
id: imgProfile
showStatus: true
showStatus: type == "contact"
}
Label { // CONTACT NAME
id: cn
leftPadding: 10
rightPadding: 10
//wrapMode: Text.WordWrap
ColumnLayout {
anchors.left: imgProfile.right
anchors.right: loading ? loadingProgress.left : rectUnread.left
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 16 * gcd.themeScale
font.strikeout: blocked
textFormat: Text.PlainText
//fontSizeMode: Text.HorizontalFit
elide: Text.ElideRight
color: "#000000"
Label { // CONTACT NAME
id: cn
leftPadding: 10
rightPadding: 10
//wrapMode: Text.WordWrap
font.pixelSize: 16 * gcd.themeScale
font.strikeout: blocked
textFormat: Text.PlainText
//fontSizeMode: Text.HorizontalFit
elide: Text.ElideRight
color: "#000000"
}
Label { // Onion
id: onion
text: handle
leftPadding: 10
rightPadding: 10
font.pixelSize: 10 * gcd.themeScale
font.strikeout: blocked
textFormat: Text.PlainText
//fontSizeMode: Text.HorizontalFit
elide: Text.ElideRight
color: "#000000"
}
}
Rectangle { // UNREAD MESSAGES?
@ -116,15 +136,22 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
hoverEnabled: true
onClicked: {
if (displayName != "me") {
gcd.broadcast("ResetMessagePane")
isActive = true
theStack.pane = theStack.messagePane
gcd.loadMessagesPane(handle)
badge = 0
if (handle.length == 32) {
gcd.requestGroupSettings(handle)
}
if (type == "contact") {
if (displayName != "me") {
gcd.broadcast("ResetMessagePane")
isActive = true
theStack.pane = theStack.messagePane
gcd.loadMessagesPane(handle)
badge = 0
if (handle.length == 32) {
gcd.requestGroupSettings(handle)
}
}
} else if (type == "profile") {
gcd.broadcast("ResetMessagePane")
gcd.broadcast("ResetProfile")
gcd.loadProfile(handle)
parentStack.pane = parentStack.profilePane
}
}
@ -164,7 +191,7 @@ Item { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
}
onIncContactUnreadCount: function(handle) {
if (handle == _handle) {
if (handle == _handle && gcd.currentOpenConversation != handle) {
badge++
}
}

View File

@ -19,6 +19,20 @@ ColumnLayout {
property string onion
SimpleButton {// BACK BUTTON
id: btnBack
icon: "solid/arrow-circle-left"
anchors.left: parent.left
//anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 2
anchors.top: parent.top
anchors.topMargin: 2
onClicked: function() {
parentStack.pane = parentStack.managementPane
theStack.pane = theStack.emptyPane
}
}
Item{ height: 6 }
Item { // PROFILE IMAGE
@ -229,8 +243,6 @@ ColumnLayout {
lblNick.text = _nick
onion = _onion
image = _image
parentStack.currentIndex = 1
splashPane.running = false
}
onTorStatus: function(code, str) {

View File

@ -0,0 +1,92 @@
import QtGraphicalEffects 1.0
import QtQuick 2.7
import QtQuick.Controls 2.4
import QtQuick.Controls.Material 2.0
import QtQuick.Layouts 1.3
ColumnLayout {
id: root
MouseArea {
anchors.fill: parent
onClicked: {
forceActiveFocus()
}
}
Flickable { // Profile List
id: sv
clip: true
Layout.minimumHeight: 100
Layout.fillHeight: true
Layout.minimumWidth: parent.width
Layout.maximumWidth: parent.width
contentWidth: colContacts.width
contentHeight: colContacts.height
boundsBehavior: Flickable.StopAtBounds
maximumFlickVelocity: 400
ScrollBar.vertical: ScrollBar {
policy: ScrollBar.AlwaysOn
}
ColumnLayout {
id: colContacts
width: root.width
spacing: 0
Connections { // ADD/REMOVE CONTACT ENTRIES
target: gcd
onAddProfile: function(handle, displayName, image) {
console.log("ProfileList onAddProfile for: " + handle)
for (var i = 0; i < profilesModel.count; i++) {
if (profilesModel.get(i)["_handle"] == handle) {
return
}
}
profilesModel.append({
"_handle": handle,
"_displayName": displayName,
"_image": image
})
}
/*
onRemoveProfile: function(handle) {
for(var i = 0; i < profilesModel.count; i++){
if(profilesModel.get(i)["_handle"] == handle) {
console.log("deleting contact " + profilesModel.get(i)["_handle"])
profilesModel.remove(i)
return
}
}
}*/
}
ListModel { // Profile OBJECTS ARE STORED HERE ...
id: profilesModel
}
Repeater {
model: profilesModel // ... AND DISPLAYED HERE
delegate: ContactRow {
handle: _handle
displayName: _displayName
image: _image
server: ""
badge: 0
status: 0
blocked: false
loading: false
type: "profile"
}
}
}
}
}