forked from cwtch.im/ui
Merge branch 'profileMan' of dan/ui into master
This commit is contained in:
commit
434907ea0d
|
@ -3,3 +3,4 @@ package constants
|
|||
const Nick = "nick"
|
||||
const LastRead = "last-read"
|
||||
const Picture = "picture"
|
||||
const DefaultPassword = "default-password"
|
||||
|
|
|
@ -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{}))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
44
go/ui/gcd.go
44
go/ui/gcd.go
|
@ -27,6 +27,12 @@ type GrandCentralDispatcher struct {
|
|||
_ string `property:"version"`
|
||||
_ string `property:"buildDate"`
|
||||
|
||||
// profile management stuff
|
||||
_ func() `signal:"Loaded"`
|
||||
_ func(handle, displayname, image string) `signal:"AddProfile"`
|
||||
_ func() `signal:"ErrorLoaded0"`
|
||||
_ func() `signal:"ResetProfile"`
|
||||
|
||||
// contact list stuff
|
||||
_ func(handle, displayName, image, server string, badge, status int, blocked bool, loading bool, lastMsgTime int) `signal:"AddContact"`
|
||||
_ func(handle, displayName string) `signal:"UpdateContactDisplayName"`
|
||||
|
@ -82,6 +88,8 @@ type GrandCentralDispatcher struct {
|
|||
_ func() `signal:"allowUnknownPeers,auto"`
|
||||
_ func() `signal:"blockUnknownPeers,auto"`
|
||||
_ func() `signal:"onActivate,auto"`
|
||||
_ func(password string) `signal:"unlockProfiles,auto"`
|
||||
_ func(handle string) `signal:"loadProfile,auto"`
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) sendMessage(message string, mID string) {
|
||||
|
@ -295,6 +303,8 @@ func (this *GrandCentralDispatcher) broadcast(signal string) {
|
|||
log.Debugf("unhandled broadcast signal: %v", signal)
|
||||
case "ResetMessagePane":
|
||||
this.ResetMessagePane()
|
||||
case "ResetProfile":
|
||||
this.ResetProfile()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -477,3 +487,37 @@ func (this *GrandCentralDispatcher) SetLocale_helper(locale string) {
|
|||
core.QCoreApplication_InstallTranslator(this.Translator)
|
||||
this.QMLEngine.Retranslate()
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) unlockProfiles(password string) {
|
||||
the.CwtchApp.LoadProfiles(password)
|
||||
}
|
||||
|
||||
func (this *GrandCentralDispatcher) loadProfile(onion string) {
|
||||
the.Peer = the.CwtchApp.GetPeer(onion)
|
||||
the.EventBus = the.CwtchApp.GetEventBus(onion)
|
||||
|
||||
pic, exists := the.Peer.GetAttribute(constants.Picture)
|
||||
if !exists {
|
||||
pic = RandomProfileImage(the.Peer.GetProfile().Onion)
|
||||
the.Peer.SetAttribute(constants.Picture, pic)
|
||||
}
|
||||
this.UpdateMyProfile(the.Peer.GetProfile().Name, the.Peer.GetProfile().Onion, pic)
|
||||
|
||||
contacts := the.Peer.GetContacts()
|
||||
for i := range contacts {
|
||||
this.UIManager.AddContact(contacts[i])
|
||||
}
|
||||
|
||||
groups := the.Peer.GetGroups()
|
||||
for i := range groups {
|
||||
// Only join servers for active and explicitly accepted groups.
|
||||
this.UIManager.AddContact(groups[i])
|
||||
}
|
||||
|
||||
// load ui preferences
|
||||
this.RequestSettings()
|
||||
locale, exists := the.Peer.GetProfile().GetAttribute(constants.LocaleSetting)
|
||||
if exists {
|
||||
this.SetLocale_helper(locale)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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: "... paste an address here to add a contact ..."</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.
|
@ -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: "... paste an address here to add a contact ..."</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>
|
||||
|
|
|
@ -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'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: "... paste an address here to add a contact ..."</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>
|
||||
|
|
|
@ -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: "... paste an address here to add a contact ..."</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>
|
||||
|
|
2
main.go
2
main.go
|
@ -255,7 +255,7 @@ func loadNetworkingAndFiles(gcd *ui.GrandCentralDispatcher, service bool, client
|
|||
if !service {
|
||||
the.AppBus = the.CwtchApp.GetPrimaryBus()
|
||||
subscribed := make(chan bool)
|
||||
go handlers.App(gcd, subscribed)
|
||||
go handlers.App(gcd, subscribed, clientUI)
|
||||
<-subscribed
|
||||
}
|
||||
|
||||
|
|
1
qml.qrc
1
qml.qrc
|
@ -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>
|
||||
|
|
24
qml/main.qml
24
qml/main.qml
|
@ -113,9 +113,14 @@ ApplicationWindow {
|
|||
|
||||
StackLayout {
|
||||
id: parentStack
|
||||
currentIndex: 0
|
||||
anchors.fill: parent
|
||||
|
||||
currentIndex: 0
|
||||
readonly property int splashPane: 0
|
||||
readonly property int managementPane: 1
|
||||
readonly property int profilePane: 2
|
||||
property alias pane: parentStack.currentIndex
|
||||
|
||||
Rectangle { // Splash pane
|
||||
color: "#FFFFFF"
|
||||
//Layout.fillHeight: true
|
||||
|
@ -131,6 +136,18 @@ ApplicationWindow {
|
|||
running: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { // Profile login/management pane
|
||||
anchors.fill: parent
|
||||
visible: false
|
||||
color: "#D2C0DD"
|
||||
|
||||
ProfileManagerPane {
|
||||
id: profilesPane
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR
|
||||
/* anchors.left: ratio >= 0.92 ? parent.left : toolbar.right
|
||||
anchors.top: ratio >= 0.92 ? toolbar.bottom : parent.top
|
||||
|
@ -247,6 +264,11 @@ ApplicationWindow {
|
|||
onSetToolbarTitle: function(str) {
|
||||
theStack.title = str
|
||||
}
|
||||
|
||||
onLoaded: function() {
|
||||
parentStack.pane = parentStack.managementPane
|
||||
splashPane.running = false
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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++
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue