erinn
/
ui
forked from cwtch.im/ui
1
0
Fork 0

initial commit

This commit is contained in:
erinn 2018-10-29 11:00:21 -07:00
parent 65c50a50b0
commit 1011d4a4f9
15 changed files with 331 additions and 73 deletions

View File

@ -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
View File

@ -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
View File

@ -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 {

View File

@ -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) {

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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
}
}