forked from cwtch.im/ui
initial commit
This commit is contained in:
parent
65c50a50b0
commit
1011d4a4f9
|
@ -32,7 +32,11 @@ func (this *ChatChannelListener) OpenInbound() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ChatChannelListener) ChatMessage(messageID uint32, when time.Time, message string) bool {
|
func (this *ChatChannelListener) ChatMessage(messageID uint32, when time.Time, message string) bool {
|
||||||
DeliverMessageToUI(this.rai.RemoteHostname, message, uint(messageID), false, when)
|
DeliverMessageToUI(this.rai.RemoteHostname, this.rai.RemoteHostname, "", message, uint(messageID), false, when)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
peer.Save()
|
||||||
|
}()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
gcd.go
24
gcd.go
|
@ -18,7 +18,7 @@ type GrandCentralDispatcher struct {
|
||||||
currentOpenConversation string
|
currentOpenConversation string
|
||||||
|
|
||||||
// messages pane stuff
|
// messages pane stuff
|
||||||
_ func(from, message string, mID uint, ts string) `signal:"AppendMessage"`
|
_ func(from, message, displayname string, mID uint, ts, source string) `signal:"AppendMessage"`
|
||||||
_ func() `signal:"ClearMessages"`
|
_ func() `signal:"ClearMessages"`
|
||||||
_ func() `signal:"ResetMessagePane"`
|
_ func() `signal:"ResetMessagePane"`
|
||||||
_ func(uint) `signal:"Acknowledged"`
|
_ func(uint) `signal:"Acknowledged"`
|
||||||
|
@ -78,7 +78,7 @@ func (this *GrandCentralDispatcher) sendMessage(message string, mID uint) {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
DeliverMessageToUI(gcd.currentOpenConversation, message, mID, true, time.Now())
|
DeliverMessageToUI(gcd.currentOpenConversation, gcd.currentOpenConversation, "", message, mID, true, time.Now())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *GrandCentralDispatcher) loadMessagesPane(onion string) {
|
func (this *GrandCentralDispatcher) loadMessagesPane(onion string) {
|
||||||
|
@ -86,15 +86,27 @@ func (this *GrandCentralDispatcher) loadMessagesPane(onion string) {
|
||||||
gcd.currentOpenConversation = onion
|
gcd.currentOpenConversation = onion
|
||||||
gcd.SetUnread(onion, 0)
|
gcd.SetUnread(onion, 0)
|
||||||
|
|
||||||
if len(onion) == 32 { // eg 48e7dcfc353e6d77da2c31d63654fd19
|
if len(onion) == 32 { // LOAD GROUP
|
||||||
log.Printf("LOADING GROUP %s", onion)
|
log.Printf("LOADING GROUP %s", onion)
|
||||||
tl := peer.GetGroup(onion).GetTimeline()
|
tl := peer.GetGroup(onion).GetTimeline()
|
||||||
log.Printf("messages: %d", len(tl))
|
log.Printf("messages: %d", len(tl))
|
||||||
for i := range tl {
|
for i := range tl {
|
||||||
gcd.AppendMessage(tl[i].PeerID, tl[i].Message, 0, tl[i].Timestamp.Format(TIME_FORMAT))
|
var handle string
|
||||||
|
if tl[i].PeerID == peer.GetProfile().Onion {
|
||||||
|
handle = "me"
|
||||||
|
} else {
|
||||||
|
handle = tl[i].PeerID
|
||||||
|
}
|
||||||
|
var name string
|
||||||
|
var exists bool
|
||||||
|
name, exists = peer.GetProfile().GetCustomAttribute(tl[i].PeerID + "_name")
|
||||||
|
if !exists || name == "" {
|
||||||
|
name = tl[i].PeerID[:16] + "..."
|
||||||
|
}
|
||||||
|
gcd.AppendMessage(handle, tl[i].Message, name, 0, tl[i].Timestamp.Format(TIME_FORMAT), randomProfileImage(tl[i].PeerID))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
} // ELSE LOAD CONTACT
|
||||||
|
|
||||||
_, exists := contactMgr[onion]
|
_, exists := contactMgr[onion]
|
||||||
if exists { // (if not, they haven't been accepted as a contact yet)
|
if exists { // (if not, they haven't been accepted as a contact yet)
|
||||||
|
@ -106,7 +118,7 @@ func (this *GrandCentralDispatcher) loadMessagesPane(onion string) {
|
||||||
if messages[i].FromMe {
|
if messages[i].FromMe {
|
||||||
from = "me"
|
from = "me"
|
||||||
}
|
}
|
||||||
gcd.AppendMessage(from, messages[i].Message, messages[i].MessageID, messages[i].Timestamp.Format(TIME_FORMAT))
|
gcd.AppendMessage(from, messages[i].Message, "", messages[i].MessageID, messages[i].Timestamp.Format(TIME_FORMAT), randomProfileImage(onion))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
57
main.go
57
main.go
|
@ -52,21 +52,21 @@ type Message struct {
|
||||||
Timestamp time.Time
|
Timestamp time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeliverMessageToUI(from, message string, mID uint, fromMe bool, ts time.Time) {
|
func DeliverMessageToUI(handle, from, displayname, message string, mID uint, fromMe bool, ts time.Time) {
|
||||||
_, found := contactMgr[from]
|
_, found := contactMgr[handle]
|
||||||
if !found {
|
if !found {
|
||||||
contactMgr[from] = &Contact{[]Message{}, 0, 0}
|
contactMgr[handle] = &Contact{[]Message{}, 0, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
contactMgr[from].AddMessage(Message{from, message, fromMe, mID, ts})
|
contactMgr[handle].AddMessage(Message{from, message, fromMe, mID, ts})
|
||||||
if gcd.currentOpenConversation == from {
|
if gcd.currentOpenConversation == handle {
|
||||||
if fromMe {
|
if fromMe {
|
||||||
from = "me"
|
from = "me"
|
||||||
}
|
}
|
||||||
gcd.AppendMessage(from, message, mID, ts.Format(TIME_FORMAT))
|
gcd.AppendMessage(from, message, displayname, mID, ts.Format(TIME_FORMAT), randomProfileImage(from))
|
||||||
} else {
|
} else {
|
||||||
contactMgr[from].Unread++
|
contactMgr[handle].Unread++
|
||||||
gcd.SetUnread(from, contactMgr[from].Unread)
|
gcd.SetUnread(handle, contactMgr[handle].Unread)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,9 @@ func main() {
|
||||||
quickcontrols2.QQuickStyle_SetStyle("Universe")
|
quickcontrols2.QQuickStyle_SetStyle("Universe")
|
||||||
view := quick.NewQQuickView(nil)
|
view := quick.NewQQuickView(nil)
|
||||||
view.SetResizeMode(quick.QQuickView__SizeRootObjectToView)
|
view.SetResizeMode(quick.QQuickView__SizeRootObjectToView)
|
||||||
view.SetTitle("bounce")
|
view.SetMinimumHeight(280)
|
||||||
|
view.SetMinimumWidth(300)
|
||||||
|
view.SetTitle("cwtch")
|
||||||
gcd = NewGrandCentralDispatcher(nil)
|
gcd = NewGrandCentralDispatcher(nil)
|
||||||
view.RootContext().SetContextProperty("gcd", gcd)
|
view.RootContext().SetContextProperty("gcd", gcd)
|
||||||
|
|
||||||
|
@ -113,11 +115,12 @@ func groupPoller() {
|
||||||
groups := peer.GetGroups()
|
groups := peer.GetGroups()
|
||||||
for i := range groups {
|
for i := range groups {
|
||||||
group := peer.GetGroup(groups[i])
|
group := peer.GetGroup(groups[i])
|
||||||
log.Printf("setting group %s to status %d", groups[i], int(servers[group.GroupServer]))
|
//log.Printf("setting group %s to status %d", groups[i], int(servers[group.GroupServer]))
|
||||||
gcd.SetConnectionStatus(groups[i], int(servers[group.GroupServer]))
|
gcd.SetConnectionStatus(groups[i], int(servers[group.GroupServer]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func presencePoller() { // TODO: make this subscribe-able in ricochet
|
func presencePoller() { // TODO: make this subscribe-able in ricochet
|
||||||
time.Sleep(time.Second * 4)
|
time.Sleep(time.Second * 4)
|
||||||
for {
|
for {
|
||||||
|
@ -181,8 +184,22 @@ func torStatusPoller() {
|
||||||
func cwtchListener(groupID string, channel chan model.Message) {
|
func cwtchListener(groupID string, channel chan model.Message) {
|
||||||
for {
|
for {
|
||||||
m := <-channel
|
m := <-channel
|
||||||
log.Printf("GROUPMSG %s", m.Message)
|
log.Printf("GROUPMSG %s %s", m.Message, m.PeerID)
|
||||||
DeliverMessageToUI(groupID, m.Message, 0, m.PeerID == peer.GetProfile().Onion, m.Timestamp)
|
name := m.PeerID
|
||||||
|
if name == peer.GetProfile().Onion {
|
||||||
|
name = "me"
|
||||||
|
} else {
|
||||||
|
var exists bool // lol this is a golang antifeature
|
||||||
|
name, exists = peer.GetProfile().GetCustomAttribute(m.PeerID + "_name")
|
||||||
|
if !exists {
|
||||||
|
name = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
name = m.PeerID[:16] + "..."
|
||||||
|
}
|
||||||
|
DeliverMessageToUI(groupID, m.PeerID, name, m.Message, 0, m.PeerID == peer.GetProfile().Onion, m.Timestamp)
|
||||||
|
peer.Save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,19 +242,20 @@ func andHisBlackAndWhiteCat(incomingMessages chan Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialize(view *quick.QQuickView) {
|
func initialize(view *quick.QQuickView) {
|
||||||
|
log.Printf(os.Args[0])
|
||||||
//TODO: this section is ported over and has a lot of printf errors, need to show them in the ui
|
//TODO: this section is ported over and has a lot of printf errors, need to show them in the ui
|
||||||
var dirname, filename string
|
var dirname, filename string
|
||||||
if os.Getenv("SENDAFRIEND_FOLDER") != "" {
|
if os.Getenv("CWTCH_FOLDER") != "" {
|
||||||
dirname = os.Getenv("SENDAFRIEND_FOLDER")
|
dirname = os.Getenv("CWTCH_FOLDER")
|
||||||
filename = path.Join(dirname, "identity.private")
|
filename = path.Join(dirname, "keep-this-file-private")
|
||||||
} else {
|
} else {
|
||||||
usr, err := user.Current()
|
usr, err := user.Current()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("\nerror: could not load current user: %v\n", err)
|
fmt.Printf("\nerror: could not load current user: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
dirname = path.Join(usr.HomeDir, ".sendafriend")
|
dirname = path.Join(usr.HomeDir, ".cwtch")
|
||||||
filename = path.Join(dirname, "identity.private")
|
filename = path.Join(dirname, "keep-this-file-private")
|
||||||
}
|
}
|
||||||
|
|
||||||
os.MkdirAll(dirname, 0700)
|
os.MkdirAll(dirname, 0700)
|
||||||
|
@ -293,6 +311,11 @@ func initialize(view *quick.QQuickView) {
|
||||||
|
|
||||||
// temporary until we do real picture selection
|
// temporary until we do real picture selection
|
||||||
func randomProfileImage(onion string) string {
|
func randomProfileImage(onion string) string {
|
||||||
|
//TODO: this is a hack, fix ever passing this in
|
||||||
|
if onion == "me" {
|
||||||
|
onion = peer.GetProfile().Onion
|
||||||
|
}
|
||||||
|
|
||||||
choices := []string{"001-centaur", "002-kraken", "003-dinosaur", "004-tree-1", "005-hand", "006-echidna", "007-robot", "008-mushroom", "009-harpy", "010-phoenix", "011-dragon-1", "012-devil", "013-troll", "014-alien", "015-minotaur", "016-madre-monte", "017-satyr", "018-karakasakozou", "019-pirate", "020-werewolf", "021-scarecrow", "022-valkyrie", "023-curupira", "024-loch-ness-monster", "025-tree", "026-cerberus", "027-gryphon", "028-mermaid", "029-vampire", "030-goblin", "031-yeti", "032-leprechaun", "033-medusa", "034-chimera", "035-elf", "036-hydra", "037-cyclops", "038-pegasus", "039-narwhal", "040-woodcutter", "041-zombie", "042-dragon", "043-frankenstein", "044-witch", "045-fairy", "046-genie", "047-pinocchio", "048-ghost", "049-wizard", "050-unicorn"}
|
choices := []string{"001-centaur", "002-kraken", "003-dinosaur", "004-tree-1", "005-hand", "006-echidna", "007-robot", "008-mushroom", "009-harpy", "010-phoenix", "011-dragon-1", "012-devil", "013-troll", "014-alien", "015-minotaur", "016-madre-monte", "017-satyr", "018-karakasakozou", "019-pirate", "020-werewolf", "021-scarecrow", "022-valkyrie", "023-curupira", "024-loch-ness-monster", "025-tree", "026-cerberus", "027-gryphon", "028-mermaid", "029-vampire", "030-goblin", "031-yeti", "032-leprechaun", "033-medusa", "034-chimera", "035-elf", "036-hydra", "037-cyclops", "038-pegasus", "039-narwhal", "040-woodcutter", "041-zombie", "042-dragon", "043-frankenstein", "044-witch", "045-fairy", "046-genie", "047-pinocchio", "048-ghost", "049-wizard", "050-unicorn"}
|
||||||
barr, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
|
barr, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion))
|
||||||
if err != nil || len(barr) != 35 {
|
if err != nil || len(barr) != 35 {
|
||||||
|
|
132
qml/main.qml
132
qml/main.qml
|
@ -8,17 +8,18 @@ import "fonts/Twemoji.js" as T
|
||||||
import "widgets"
|
import "widgets"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
id: windowItem
|
||||||
width: 525
|
width: 525
|
||||||
height: 500
|
height: 500
|
||||||
id: root
|
|
||||||
|
|
||||||
|
readonly property real ratio: height / width
|
||||||
|
|
||||||
FontAwesome {
|
FontAwesome { // PRETTY BUTTON ICONS
|
||||||
id: awesome
|
id: awesome
|
||||||
resource: "qrc:/qml/fonts/fontawesome.ttf"
|
resource: "qrc:/qml/fonts/fontawesome.ttf"
|
||||||
}
|
}
|
||||||
|
|
||||||
function parse(text, size) {
|
function parse(text, size) { // REPLACE EMOJI WITH <IMG> TAGS
|
||||||
T.twemoji.base = "qrc:/qml/fonts/twemoji/"
|
T.twemoji.base = "qrc:/qml/fonts/twemoji/"
|
||||||
T.twemoji.ext = ".png"
|
T.twemoji.ext = ".png"
|
||||||
T.twemoji.size = "72x72"
|
T.twemoji.size = "72x72"
|
||||||
|
@ -26,7 +27,7 @@ Item {
|
||||||
return T.twemoji.parse(text)
|
return T.twemoji.parse(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreEmoji(text) {
|
function restoreEmoji(text) { // REPLACE <IMG> TAGS WITH EMOJI
|
||||||
var re = /<img src="qrc:\/qml\/fonts\/twemoji\/72x72\/([^"]*?)\.png" width="10" height="10" \/>/g
|
var re = /<img src="qrc:\/qml\/fonts\/twemoji\/72x72\/([^"]*?)\.png" width="10" height="10" \/>/g
|
||||||
var arr
|
var arr
|
||||||
var newtext = text
|
var newtext = text
|
||||||
|
@ -42,7 +43,57 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
/* Rectangle { // THE TOOLBAR
|
||||||
|
id: toolbar
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
width: ratio >= 0.92 ? parent.width : 70
|
||||||
|
height: ratio >= 0.92 ? 70 : parent.height
|
||||||
|
color: "#4B3557"
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
width: parent.width
|
||||||
|
height: parent.height
|
||||||
|
columns: ratio >= 0.92 ? children.length : 1
|
||||||
|
|
||||||
|
|
||||||
|
ContactPicture {
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
source: "qrc:/qml/images/profiles/001-centaur.png"
|
||||||
|
status: -2
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactPicture {
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
source: "qrc:/qml/images/profiles/002-kraken.png"
|
||||||
|
status: -2
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactPicture {
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
source: "qrc:/qml/images/profiles/003-dinosaur.png"
|
||||||
|
status: -2
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactPicture {
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
source: "qrc:/qml/images/profiles/004-tree-1.png"
|
||||||
|
status: -2
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactPicture {
|
||||||
|
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
|
||||||
|
source: "qrc:/qml/images/profiles/005-hand.png"
|
||||||
|
status: -2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR
|
||||||
|
/* anchors.left: ratio >= 0.92 ? parent.left : toolbar.right
|
||||||
|
anchors.top: ratio >= 0.92 ? toolbar.bottom : parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom */
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
|
@ -50,8 +101,9 @@ Item {
|
||||||
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
|
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
|
||||||
color: "#D2C0DD"
|
color: "#D2C0DD"
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.minimumWidth: 200
|
Layout.minimumWidth: Layout.maximumWidth
|
||||||
Layout.maximumWidth: 200
|
Layout.maximumWidth: theStack.pane == theStack.emptyPane ? parent.width : 200
|
||||||
|
visible: (ratio <= 1.08 && windowItem.width >= 500) || theStack.pane == theStack.emptyPane
|
||||||
|
|
||||||
|
|
||||||
ContactList{
|
ContactList{
|
||||||
|
@ -59,21 +111,79 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle { // THE RIGHT PANE WHERE THE MESSAGES GO
|
Rectangle { // THE RIGHT PANE WHERE THE MESSAGES AND STuFF GO
|
||||||
color: "#EEEEFF"
|
color: "#EEEEFF"
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
|
||||||
MessageList{
|
StackLayout {
|
||||||
|
id: theStack
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
currentIndex: 0
|
||||||
|
|
||||||
|
property alias pane: theStack.currentIndex
|
||||||
|
readonly property int emptyPane: 0
|
||||||
|
readonly property int messagePane: 1
|
||||||
|
readonly property int settingsPane: 2
|
||||||
|
readonly property int userProfilePane: 3
|
||||||
|
readonly property int groupProfilePane: 4
|
||||||
|
|
||||||
|
|
||||||
|
Item {} // empty
|
||||||
|
|
||||||
|
MessageList { // messagePane
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout { // settingsPane
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
|
||||||
|
StackToolbar {
|
||||||
|
text: "Cwtch Settings"
|
||||||
|
aux.visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Label { text: "welcome to the global app settings page!" }
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout { // userProfilePane
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
|
||||||
|
StackToolbar {
|
||||||
|
text: "Settings for Sarah"
|
||||||
|
aux.visible: false
|
||||||
|
|
||||||
|
back.onClicked: theStack.pane = theStack.messagePane
|
||||||
|
}
|
||||||
|
|
||||||
|
Label { text: "per-user things like contact name and picture will be edited here" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Label { // groupProfilePane
|
||||||
|
font.pixelSize: 12
|
||||||
|
text: "invite new people or change the group name here"
|
||||||
|
|
||||||
|
|
||||||
|
StackToolbar { text: "Group settings" }
|
||||||
|
}
|
||||||
|
|
||||||
|
Label { // addGroupPane
|
||||||
|
font.pixelSize: 12
|
||||||
|
text: "add a new group"
|
||||||
|
|
||||||
|
|
||||||
|
StackToolbar { text: "Create group" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertyAnimation { id: anmPopup; easing.type: Easing.InQuart; duration: 7000; target: popup; property: "opacity"; to: 0; }
|
PropertyAnimation { id: anmPopup; easing.type: Easing.InQuart; duration: 7000; target: popup; property: "opacity"; to: 0; }
|
||||||
|
|
||||||
Rectangle {
|
Rectangle { // THE ERROR MESSAGE POPUP
|
||||||
id: popup
|
id: popup
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
@ -96,7 +206,7 @@ Item {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Connections {
|
Connections { // POPUPS ARE INVOKED BY GO FUNCS
|
||||||
target: gcd
|
target: gcd
|
||||||
|
|
||||||
onInvokePopup: function(str) {
|
onInvokePopup: function(str) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
||||||
id: rectUnread
|
id: rectUnread
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
height: 16
|
height: 16
|
||||||
width: childrenRect.width + 10
|
width: lblUnread.width + 10
|
||||||
radius: 8
|
radius: 8
|
||||||
color: "#4B3557"
|
color: "#4B3557"
|
||||||
visible: badge != "0"
|
visible: badge != "0"
|
||||||
|
@ -65,6 +65,7 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
||||||
|
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
id: lblUnread
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
color: "#FFFFFF"
|
color: "#FFFFFF"
|
||||||
|
@ -82,6 +83,7 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
||||||
onClicked: {
|
onClicked: {
|
||||||
gcd.broadcast("ResetMessagePane")
|
gcd.broadcast("ResetMessagePane")
|
||||||
isActive = true
|
isActive = true
|
||||||
|
theStack.pane = theStack.messagePane
|
||||||
gcd.loadMessagesPane(onion)
|
gcd.loadMessagesPane(onion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +110,6 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
||||||
}
|
}
|
||||||
|
|
||||||
onSetConnectionStatus: function(foronion, x) {
|
onSetConnectionStatus: function(foronion, x) {
|
||||||
if (foronion.length == 32) console.log(onion+" setting status "+x+" for contact "+foronion)
|
|
||||||
if (onion == foronion) {
|
if (onion == foronion) {
|
||||||
status = x
|
status = x
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -9,7 +9,6 @@ Item {
|
||||||
id: imgProfile
|
id: imgProfile
|
||||||
implicitWidth: 48
|
implicitWidth: 48
|
||||||
implicitHeight: 48
|
implicitHeight: 48
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.margins: 5
|
anchors.margins: 5
|
||||||
|
|
||||||
property alias source: img.source
|
property alias source: img.source
|
||||||
|
|
|
@ -8,12 +8,18 @@ import "controls" as Awesome
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: root
|
id: root
|
||||||
Layout.alignment: from == "me" ? Qt.AlignRight : Qt.AlignLeft
|
//Layout.alignment: from == "me" ? Qt.AlignRight : Qt.AlignLeft
|
||||||
|
anchors.left: from == "me" ? undefined : parent.left
|
||||||
|
anchors.right: from == "me" ? parent.right : undefined
|
||||||
|
height: Math.max(imgProfile.height, rectMessageBubble.height)
|
||||||
|
|
||||||
property alias message: lbl.text
|
property alias message: lbl.text
|
||||||
property string from
|
property string from
|
||||||
|
property string displayname
|
||||||
property int messageID
|
property int messageID
|
||||||
property alias timestamp: ts.text
|
property alias timestamp: ts.text
|
||||||
|
property alias source: imgProfile.source
|
||||||
|
property alias status: imgProfile.status
|
||||||
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
@ -27,15 +33,22 @@ RowLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ContactPicture {
|
||||||
|
id: imgProfile
|
||||||
|
anchors.left: parent.left
|
||||||
|
visible: from != "me"
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle { // THIS IS JUST A PRETTY MESSAGE-HOLDING RECTANGLE
|
Rectangle { // THIS IS JUST A PRETTY MESSAGE-HOLDING RECTANGLE
|
||||||
height: childrenRect.height + 3
|
id: rectMessageBubble
|
||||||
width: childrenRect.width + 6
|
height: lbl.height + ts.height + 4
|
||||||
|
width: colMessageBubble.width + 6
|
||||||
color: from == "me" ? "#B09CBC" : "#4B3557"
|
color: from == "me" ? "#B09CBC" : "#4B3557"
|
||||||
radius: 5
|
radius: 5
|
||||||
|
|
||||||
// the console will complain constantly about me setting these anchors, but qt only allows margins if they've been set to something
|
// the console will complain constantly about me setting these anchors, but qt only allows margins if they've been set to something
|
||||||
// a kludge to fix this would be to have spacers before/after and set the widths according to the side they're on ^ea
|
// a kludge to fix this would be to have spacers before/after and set the widths according to the side they're on ^ea
|
||||||
anchors.left: from == "me" ? undefined : parent.left
|
anchors.left: from == "me" ? undefined : imgProfile.right //parent.left
|
||||||
anchors.right: from == "me" ? parent.right : undefined
|
anchors.right: from == "me" ? parent.right : undefined
|
||||||
anchors.leftMargin: 5
|
anchors.leftMargin: 5
|
||||||
anchors.rightMargin: 9
|
anchors.rightMargin: 9
|
||||||
|
@ -43,6 +56,9 @@ RowLayout {
|
||||||
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
id: colMessageBubble
|
||||||
|
|
||||||
|
|
||||||
Column { // combine these into one element or else childrenRect won't play nicely
|
Column { // combine these into one element or else childrenRect won't play nicely
|
||||||
TextEdit { // this is used as a helper to calculate the message box width
|
TextEdit { // this is used as a helper to calculate the message box width
|
||||||
id: dummy
|
id: dummy
|
||||||
|
@ -63,18 +79,19 @@ RowLayout {
|
||||||
font.pixelSize: 12
|
font.pixelSize: 12
|
||||||
selectByMouse: true
|
selectByMouse: true
|
||||||
readOnly: true
|
readOnly: true
|
||||||
width: dummy.width > root.parent.width - 70 ? root.parent.width - 70 : dummy.width
|
width: Math.min(dummy.width, root.parent.width - (imgProfile.visible ? imgProfile.width : 0) - 40)
|
||||||
wrapMode: TextEdit.Wrap
|
wrapMode: TextEdit.Wrap
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
id: rowBottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
|
||||||
Label {
|
Label { // TIMESTAMP
|
||||||
id: ts
|
id: ts
|
||||||
color: "#FFFFFF"
|
color: "#FFFFFF"
|
||||||
font.pixelSize: 10
|
font.pixelSize: 10
|
||||||
|
@ -82,13 +99,15 @@ RowLayout {
|
||||||
leftPadding: 10
|
leftPadding: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label { text: dummy.width+", "+root.width }
|
||||||
|
|
||||||
|
Label { // MESSAGE ACKNOWLEDGMENT
|
||||||
id: ack
|
id: ack
|
||||||
color: "#FFFFFF"
|
color: "#FFFFFF"
|
||||||
font.pixelSize: 10
|
font.pixelSize: 10
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
font.family: "FontAwesome"
|
font.family: "FontAwesome"
|
||||||
text: from == "me" ? awesome.icons.fa_ellipsis_h : ""
|
text: from == "me" ? awesome.icons.fa_ellipsis_h : displayname
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -11,7 +11,13 @@ ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
|
||||||
Flickable {
|
StackToolbar {
|
||||||
|
text: "Sarah Jamie Lewis"
|
||||||
|
|
||||||
|
aux.onClicked: theStack.pane = theStack.userProfilePane
|
||||||
|
}
|
||||||
|
|
||||||
|
Flickable { // THE MESSAGE LIST ITSELF
|
||||||
id: sv
|
id: sv
|
||||||
clip: true
|
clip: true
|
||||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||||
|
@ -32,12 +38,14 @@ ColumnLayout {
|
||||||
messagesModel.clear()
|
messagesModel.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
onAppendMessage: function(from, message, mid, ts) {
|
onAppendMessage: function(from, message, displayname, mid, ts, source) {
|
||||||
messagesModel.append({
|
messagesModel.append({
|
||||||
"f": from,
|
"f": from,
|
||||||
"m": parse(message, 12),
|
"m": parse(message, 12),
|
||||||
|
"d": displayname,
|
||||||
"i": mid,
|
"i": mid,
|
||||||
"t": ts
|
"t": ts,
|
||||||
|
"src": source
|
||||||
})
|
})
|
||||||
|
|
||||||
if (sv.contentY + sv.height >= sv.contentHeight - colMessages.height && sv.contentHeight > sv.height) {
|
if (sv.contentY + sv.height >= sv.contentHeight - colMessages.height && sv.contentHeight > sv.height) {
|
||||||
|
@ -67,8 +75,10 @@ ColumnLayout {
|
||||||
delegate: Message {
|
delegate: Message {
|
||||||
from: f
|
from: f
|
||||||
message: m
|
message: m
|
||||||
|
displayname: d
|
||||||
messageID: i
|
messageID: i
|
||||||
timestamp: t
|
timestamp: t
|
||||||
|
source: src
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,9 +108,7 @@ ColumnLayout {
|
||||||
maximumFlickVelocity: 300
|
maximumFlickVelocity: 300
|
||||||
|
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar{
|
ScrollBar.vertical: ScrollBar{}
|
||||||
policy: ScrollBar.AlwaysOn
|
|
||||||
}
|
|
||||||
|
|
||||||
TextEdit {
|
TextEdit {
|
||||||
id: txtMessage
|
id: txtMessage
|
||||||
|
@ -116,7 +124,7 @@ ColumnLayout {
|
||||||
|
|
||||||
Keys.onReturnPressed: { // CTRL+ENTER = LINEBREAK
|
Keys.onReturnPressed: { // CTRL+ENTER = LINEBREAK
|
||||||
if (event.modifiers & Qt.ControlModifier) {
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
txtMessage.insert(txtMessage.cursorPosition, "\n")
|
txtMessage.insert(txtMessage.cursorPosition, "<br>")
|
||||||
} else if (event.modifiers == Qt.NoModifier) {
|
} else if (event.modifiers == Qt.NoModifier) {
|
||||||
btnSend.clicked()
|
btnSend.clicked()
|
||||||
}
|
}
|
||||||
|
@ -126,7 +134,7 @@ ColumnLayout {
|
||||||
// while also stripping any other tag, including other images.
|
// while also stripping any other tag, including other images.
|
||||||
// TODO: this probably breaks if people actually do want to paste html
|
// TODO: this probably breaks if people actually do want to paste html
|
||||||
onTextChanged: {
|
onTextChanged: {
|
||||||
console.log("onTextChanged()")
|
//console.log("onTextChanged()")
|
||||||
|
|
||||||
// we're taking advantage of TextEdit.getText()'s parsing capability, which means occasionally
|
// we're taking advantage of TextEdit.getText()'s parsing capability, which means occasionally
|
||||||
// passing text into it to be filtered. this prevents recursive calls putting us into an
|
// passing text into it to be filtered. this prevents recursive calls putting us into an
|
||||||
|
@ -137,7 +145,7 @@ ColumnLayout {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("1: " + txtMessage.getText(0, txtMessage.text.length))
|
//console.log("1: " + txtMessage.getText(0, txtMessage.text.length))
|
||||||
|
|
||||||
// convert <img> tags back to their emoji form
|
// convert <img> tags back to their emoji form
|
||||||
var nt = restoreEmoji(txtMessage.text)
|
var nt = restoreEmoji(txtMessage.text)
|
||||||
|
@ -146,15 +154,15 @@ ColumnLayout {
|
||||||
txtMessage.text = nt
|
txtMessage.text = nt
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("2: " + txtMessage.getText(0, txtMessage.text.length))
|
//console.log("2: " + txtMessage.getText(0, txtMessage.text.length))
|
||||||
|
|
||||||
// strip all HTML tags
|
// strip all HTML tags
|
||||||
var theText = txtMessage.getText(0, txtMessage.text.length)
|
var theText = txtMessage.getText(0, txtMessage.text.length)
|
||||||
console.log("3: " + theText)
|
//console.log("3: " + theText)
|
||||||
|
|
||||||
// convert emoji back to <img> tags
|
// convert emoji back to <img> tags
|
||||||
nt = parse(theText, 10)
|
nt = parse(theText, 10)
|
||||||
console.log("4: " + nt)
|
//console.log("4: " + nt)
|
||||||
|
|
||||||
// if there were changes...
|
// if there were changes...
|
||||||
if (nt != txtMessage.getText(0, txtMessage.text.length)) {
|
if (nt != txtMessage.getText(0, txtMessage.text.length)) {
|
||||||
|
|
Binary file not shown.
|
@ -2,6 +2,7 @@ import QtGraphicalEffects 1.0
|
||||||
import QtQuick 2.7
|
import QtQuick 2.7
|
||||||
import QtQuick.Controls 2.4
|
import QtQuick.Controls 2.4
|
||||||
import QtQuick.Controls.Material 2.0
|
import QtQuick.Controls.Material 2.0
|
||||||
|
import QtQuick.Controls.Styles 1.4
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
@ -10,8 +11,8 @@ ColumnLayout {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
property alias image: imgProfileImg.source
|
property alias image: imgProfileImg.source
|
||||||
property alias nick: lblNick.text
|
property string nick
|
||||||
property alias onion: lblOnion.text
|
property string onion
|
||||||
|
|
||||||
|
|
||||||
Item{ height: 6 }
|
Item{ height: 6 }
|
||||||
|
@ -93,23 +94,56 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InplaceEditText { // USER NICKNAME
|
//InplaceEditText { // USER NICKNAME
|
||||||
id: lblNick
|
// id: lblNick
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
// anchors.horizontalCenter: parent.horizontalCenter
|
||||||
width: parent.width
|
// width: parent.width
|
||||||
|
//
|
||||||
|
// onUpdated: {
|
||||||
|
// gcd.updateNick(lblNick.text)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
onUpdated: {
|
Text {
|
||||||
gcd.updateNick(lblNick.text)
|
id: txtNick
|
||||||
|
fontSizeMode: Text.HorizontalFit
|
||||||
|
minimumPixelSize: 10
|
||||||
|
font.pixelSize: 20
|
||||||
|
width: 195
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: cbNick.editText
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
onClicked: {
|
||||||
|
parent.visible = false
|
||||||
|
cbNick.visible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox { // USER NICKNAME
|
||||||
|
id: cbNick
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
popup.font.pixelSize: 12
|
||||||
|
width: 200
|
||||||
|
font.pixelSize: 20
|
||||||
|
model: ["erinn", "erinn (open privacy)", "supergirl", "add new profile..."]
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
onCurrentTextChanged: {
|
||||||
|
visible = false
|
||||||
|
txtNick.visible = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this doesnt quite fit everything :{
|
|
||||||
Label { // ONION ADDRESS
|
Label { // ONION ADDRESS
|
||||||
id: lblOnion
|
id: lblOnion
|
||||||
font.pixelSize: 6
|
font.pixelSize: 6
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
padding: 3
|
padding: 3
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
text: "This is your address. You can give it out to people!\n" + onion
|
||||||
}
|
}
|
||||||
|
|
||||||
RowLayout { // TOOLS FOR EDITING PROFILE
|
RowLayout { // TOOLS FOR EDITING PROFILE
|
||||||
|
@ -121,7 +155,7 @@ ColumnLayout {
|
||||||
visible: false
|
visible: false
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton {
|
IconButton { // COPY ONION ADDRESS BUTTON
|
||||||
icon: awesome.icons.fa_clipboard
|
icon: awesome.icons.fa_clipboard
|
||||||
label: "copy"
|
label: "copy"
|
||||||
|
|
||||||
|
@ -133,20 +167,19 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton {
|
IconButton { // SETTINGS BUTTON
|
||||||
icon: awesome.icons.fa_cog
|
icon: awesome.icons.fa_cog
|
||||||
//label: "settings"
|
//label: "settings"
|
||||||
|
|
||||||
onClicked: gcd.popup("not yet implemented, sorry :(")
|
onClicked: theStack.pane = theStack.settingsPane
|
||||||
}
|
}
|
||||||
|
|
||||||
IconButton {
|
IconButton { // SIGN OUT BUTTON
|
||||||
icon: awesome.icons.fa_sign_out
|
icon: awesome.icons.fa_sign_out
|
||||||
//label: "sign out"
|
//label: "sign out"
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
gcd.popup("not yet implemented, sorry :(")
|
gcd.popup("not yet implemented, sorry :(")
|
||||||
console.log(parse("💜", 20))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,7 +232,7 @@ ColumnLayout {
|
||||||
|
|
||||||
onUpdateMyProfile: function(_nick, _onion, _image) {
|
onUpdateMyProfile: function(_nick, _onion, _image) {
|
||||||
nick = _nick
|
nick = _nick
|
||||||
onion = "This is your address. You should give it out to people!\n" + _onion
|
onion = _onion
|
||||||
image = _image
|
image = _image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,49 @@
|
||||||
|
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 "controls" as Awesome
|
||||||
|
import "../fonts/Twemoji.js" as T
|
||||||
|
|
||||||
|
Rectangle { // OVERHEAD BAR ON STACK PANE
|
||||||
|
id: toolbar
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
height: 28
|
||||||
|
color: "#EDEDED"
|
||||||
|
|
||||||
|
property alias text: lbl.text
|
||||||
|
property alias aux: btnAux
|
||||||
|
property alias back: btnBack
|
||||||
|
|
||||||
|
|
||||||
|
IconButton {// BACK BUTTON
|
||||||
|
id: btnBack
|
||||||
|
icon: awesome.icons.fa_arrow_circle_o_left
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: 6
|
||||||
|
visible: ratio >= 1.08 || windowItem.width < 500
|
||||||
|
|
||||||
|
onClicked: theStack.pane = theStack.emptyPane
|
||||||
|
}
|
||||||
|
|
||||||
|
Label { // TEXT
|
||||||
|
id: lbl
|
||||||
|
font.pixelSize: 16
|
||||||
|
text: "Sarah Jamie Lewis"
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton { // COG BUTTON
|
||||||
|
id: btnAux
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: 6
|
||||||
|
icon: awesome.icons.fa_cog
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue