erinn
/
ui
forked from cwtch.im/ui
1
0
Fork 0
ui/go/gothings/gcd.go

304 lines
8.5 KiB
Go

package gothings
import (
"cwtch.im/ui/go/constants"
"cwtch.im/ui/go/cwutil"
"cwtch.im/cwtch/model"
"cwtch.im/ui/go/characters"
"cwtch.im/ui/go/gobjects"
"cwtch.im/ui/go/the"
"encoding/base32"
"fmt"
"github.com/therecipe/qt/core"
"log"
"strings"
"time"
)
type GrandCentralDispatcher struct {
core.QObject
OutgoingMessages chan gobjects.Letter
UIState InterfaceState
_ string `property:"currentOpenConversation"`
_ float32 `property:"themeScale"`
// contact list stuff
_ func(handle, displayName, image, server string, badge, status int, trusted bool) `signal:"AddContact"`
_ func(handle, displayName, image, server string, badge, status int, trusted bool) `signal:"UpdateContact"`
// messages pane stuff
_ func(handle, from, displayName, message, image string, mID uint, fromMe bool, ts string) `signal:"AppendMessage"`
_ func() `signal:"ClearMessages"`
_ func() `signal:"ResetMessagePane"`
_ func(mID uint) `signal:"Acknowledged"`
// profile-area stuff
_ func(name, onion, image string) `signal:"UpdateMyProfile"`
_ func(status int, str string) `signal:"TorStatus"`
// other stuff i can't ontologize atm
_ func(str string) `signal:"InvokePopup"`
_ func(name, server, invitation string) `signal:"SupplyGroupSettings"`
// signals emitted from the ui (written in go, below)
_ func(message string, mid uint) `signal:"sendMessage,auto"`
_ func(onion string) `signal:"loadMessagesPane,auto"`
_ func(signal string) `signal:"broadcast,auto"` // convenience relay signal
_ func(str string) `signal:"importString,auto"`
_ func(str string) `signal:"popup,auto"`
_ func(nick string) `signal:"updateNick,auto"`
_ func(server, groupName string) `signal:"createGroup,auto"`
_ func() `signal:"requestGroupSettings,auto"`
}
func (this *GrandCentralDispatcher) sendMessage(message string, mID uint) {
if len(message) > 65530 {
this.InvokePopup("message is too long")
return
}
if this.CurrentOpenConversation() == "" {
this.InvokePopup("ui error")
return
}
if len(this.CurrentOpenConversation()) == 32 { // SEND TO GROUP
if !the.Peer.GetGroup(this.CurrentOpenConversation()).Accepted {
the.Peer.GetGroup(this.CurrentOpenConversation()).Accepted = true
the.CwtchApp.SaveProfile(the.Peer)
c := this.UIState.GetContact(this.CurrentOpenConversation())
c.Trusted = true
this.UIState.UpdateContact(c.Handle)
}
the.Peer.SendMessageToGroup(this.CurrentOpenConversation(), message)
return
}
// TODO: require explicit invite accept/reject instead of implicitly trusting on send
if !this.UIState.GetContact(this.CurrentOpenConversation()).Trusted {
this.UIState.GetContact(this.CurrentOpenConversation()).Trusted = true
this.UIState.UpdateContact(this.CurrentOpenConversation())
}
select { // 1 weird trick to do a non-blocking send. this means the user can only send a limited number of messages
// before the channel buffer fills. TODO: stop the user from sending if the buffer is full
case this.OutgoingMessages <- gobjects.Letter{this.CurrentOpenConversation(), message, mID}:
default:
}
this.UIState.AddMessage(&gobjects.Message{
this.CurrentOpenConversation(),
"me",
"",
message,
"",
true,
int(mID),
time.Now(),
})
}
func (this *GrandCentralDispatcher) loadMessagesPane(handle string) {
this.ClearMessages()
this.SetCurrentOpenConversation(handle)
c := this.UIState.GetContact(handle)
c.Badge = 0
this.UIState.UpdateContact(handle)
if len(handle) == 32 { // LOAD GROUP
log.Printf("LOADING GROUP %s", handle)
tl := the.Peer.GetGroup(handle).GetTimeline()
log.Printf("messages: %d", len(tl))
for i := range tl {
if tl[i].PeerID == the.Peer.GetProfile().Onion {
handle = "me"
} else {
handle = tl[i].PeerID
}
var name string
var exists bool
name, exists = the.Peer.GetProfile().GetCustomAttribute(tl[i].PeerID + "_name")
if !exists || name == "" {
name = tl[i].PeerID[:16] + "..."
}
this.AppendMessage(
handle,
tl[i].PeerID,
name,
tl[i].Message,
cwutil.RandomProfileImage(tl[i].PeerID),
0,
tl[i].PeerID == the.Peer.GetProfile().Onion,
tl[i].Timestamp.Format(constants.TIME_FORMAT),
)
}
return
} // ELSE LOAD CONTACT
messages := this.UIState.GetMessages(handle)
for i := range messages {
from := messages[i].From
if messages[i].FromMe {
from = "me"
}
this.AppendMessage(
messages[i].Handle,
from,
messages[i].DisplayName,
messages[i].Message,
cwutil.RandomProfileImage(handle),
uint(messages[i].MessageID),
messages[i].FromMe,
messages[i].Timestamp.Format(constants.TIME_FORMAT),
)
}
}
func (this *GrandCentralDispatcher) requestGroupSettings() {
log.Printf("requestGroupSettings()")
group := the.Peer.GetGroup(this.CurrentOpenConversation())
nick, _ := group.GetAttribute("nick")
invite, _ := the.Peer.ExportGroup(this.CurrentOpenConversation())
this.SupplyGroupSettings(nick, group.GroupServer, invite)
}
func (this *GrandCentralDispatcher) broadcast(signal string) {
switch signal {
default:
log.Printf("unhandled broadcast signal: %v", signal)
case "ResetMessagePane":
this.ResetMessagePane()
}
}
func (this *GrandCentralDispatcher) importString(str string) {
if len(str) < 5 {
log.Printf("ignoring short string")
return
}
log.Printf("importing: %s\n", str)
onion := str
name := onion
str = strings.TrimSpace(str)
//eg: torv3JFDWkXExBsZLkjvfkkuAxHsiLGZBk0bvoeJID9ItYnU=EsEBCiBhOWJhZDU1OTQ0NWI3YmM2N2YxYTM5YjkzMTNmNTczNRIgpHeNaG+6jy750eDhwLO39UX4f2xs0irK/M3P6mDSYQIaOTJjM2ttb29ibnlnaGoyenc2cHd2N2Q1N3l6bGQ3NTNhdW8zdWdhdWV6enB2ZmFrM2FoYzRiZHlkCiJAdVSSVgsksceIfHe41OJu9ZFHO8Kwv3G6F5OK3Hw4qZ6hn6SiZjtmJlJezoBH0voZlCahOU7jCOg+dsENndZxAA==
if str[0:5] == "torv3" { // GROUP INVITE
groupID, err := the.Peer.ImportGroup(str)
if err != nil {
this.InvokePopup("not a valid group invite")
return
}
group := the.Peer.GetGroup(groupID)
the.Peer.JoinServer(group.GroupServer)
the.CwtchApp.SaveProfile(the.Peer)
this.UIState.AddContact(&gobjects.Contact{
groupID[:12],
groupID,
cwutil.RandomGroupImage(groupID),
group.GroupServer,
0,
0,
true,
})
fmt.Printf("imported groupid=%s server=%s", groupID, group.GroupServer)
return
}
if strings.Contains(str, " ") { // usually people prepend spaces and we don't want it going into the name (use ~ for that)
parts := strings.Split(strings.TrimSpace(str), " ")
str = parts[len(parts)-1]
}
if strings.Contains(str, "~") {
parts := strings.Split(str, "~")
onion = parts[len(parts)-1]
name = strings.Join(parts[:len(parts)-1], " ")
}
if len(onion) != 56 {
this.InvokePopup("invalid format")
return
}
name = strings.TrimSpace(name)
if name == "" {
this.InvokePopup("empty name")
return
}
if len(name) > 32 {
name = name[:32] //TODO: better strategy for long names?
}
_, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion[:56]))
if err != nil {
log.Printf("%v", err)
this.InvokePopup("bad format. missing characters?")
return
}
_, exists := the.Peer.GetProfile().GetCustomAttribute(name + "_onion")
if exists {
this.InvokePopup("can't re-use names :(")
return
}
_, exists = the.Peer.GetProfile().GetCustomAttribute(onion + "_name")
if exists {
this.InvokePopup("already have this contact")
return //TODO: bring them to the duplicate
}
this.UIState.AddContact(&gobjects.Contact{
onion,
name,
cwutil.RandomProfileImage(onion),
"",
0,
0,
true,
})
}
func (this *GrandCentralDispatcher) popup(str string) {
this.InvokePopup(str)
}
func (this *GrandCentralDispatcher) updateNick(nick string) {
the.Peer.GetProfile().Name = nick
the.CwtchApp.SaveProfile(the.Peer)
}
func (this *GrandCentralDispatcher) createGroup(server, groupName string) {
groupID, _, err := the.Peer.StartGroup(server)
if err != nil {
this.popup("group creation failed :(")
return
}
this.UIState.AddContact(&gobjects.Contact{
groupID,
groupName,
cwutil.RandomGroupImage(groupID),
server,
0,
0,
true,
})
group := the.Peer.GetGroup(groupID)
group.SetAttribute("nick", groupName)
the.CwtchApp.SaveProfile(the.Peer)
the.Peer.JoinServer(server)
group.NewMessage = make(chan model.Message)
go characters.CwtchListener(this.UIState.AddMessage, group.GroupID, group.NewMessage)
}