initial commit
|
@ -2,3 +2,4 @@
|
||||||
deploy
|
deploy
|
||||||
moc*
|
moc*
|
||||||
rcc*
|
rcc*
|
||||||
|
*.qmlc
|
||||||
|
|
58
gcd.go
|
@ -1,13 +1,12 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
"log"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"cwtch.im/cwtch/model"
|
"cwtch.im/cwtch/model"
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
)
|
"github.com/therecipe/qt/core"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type GrandCentralDispatcher struct {
|
type GrandCentralDispatcher struct {
|
||||||
core.QObject
|
core.QObject
|
||||||
|
@ -15,29 +14,30 @@ type GrandCentralDispatcher struct {
|
||||||
currentOpenConversation string
|
currentOpenConversation string
|
||||||
|
|
||||||
// messages pane stuff
|
// messages pane stuff
|
||||||
_ func(from, message string) `signal:"AppendMessage"`
|
_ func(from, message string) `signal:"AppendMessage"`
|
||||||
_ func() `signal:"ClearMessages"`
|
_ func() `signal:"ClearMessages"`
|
||||||
_ func() `signal:"ResetMessagePane"`
|
_ func() `signal:"ResetMessagePane"`
|
||||||
|
|
||||||
// contact list stuff
|
// contact list stuff
|
||||||
_ func(onion string, num int) `signal:"SetUnread"`
|
_ func(onion string, num int) `signal:"SetUnread"`
|
||||||
_ func(onion string, status int) `signal:"SetConnectionStatus"`
|
_ func(onion string, status int) `signal:"SetConnectionStatus"`
|
||||||
_ func(name, onion, image, badge string, trusted bool) `signal:"AddContact"`
|
_ func(name, onion, image, badge string, trusted bool) `signal:"AddContact"`
|
||||||
_ func(onion string) `signal:"MarkTrusted"`
|
_ func(onion string) `signal:"MarkTrusted"`
|
||||||
|
|
||||||
// profile-area stuff
|
// profile-area stuff
|
||||||
_ func(name, onion, image string) `signal:"UpdateMyProfile"`
|
_ func(name, onion, image string) `signal:"UpdateMyProfile"`
|
||||||
|
_ func(status int, str string) `signal:"TorStatus"`
|
||||||
|
|
||||||
// other stuff i can't ontologize atm
|
// other stuff i can't ontologize atm
|
||||||
_ func(str string) `signal:"InvokePopup"`
|
_ func(str string) `signal:"InvokePopup"`
|
||||||
|
|
||||||
// exfiltrated signals (written in go, below)
|
// exfiltrated signals (written in go, below)
|
||||||
_ func(message string) `signal:"sendMessage,auto"`
|
_ func(message string) `signal:"sendMessage,auto"`
|
||||||
_ func(onion string) `signal:"loadMessagesPane,auto"`
|
_ func(onion string) `signal:"loadMessagesPane,auto"`
|
||||||
_ func(signal string) `signal:"broadcast,auto"` // convenience relay signal
|
_ func(signal string) `signal:"broadcast,auto"` // convenience relay signal
|
||||||
_ func(str string) `signal:"importString,auto"`
|
_ func(str string) `signal:"importString,auto"`
|
||||||
_ func(str string) `signal:"popup,auto"`
|
_ func(str string) `signal:"popup,auto"`
|
||||||
_ func(nick string) `signal:"updateNick,auto"`
|
_ func(nick string) `signal:"updateNick,auto"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *GrandCentralDispatcher) sendMessage(message string) {
|
func (this *GrandCentralDispatcher) sendMessage(message string) {
|
||||||
|
@ -57,11 +57,12 @@ func (this *GrandCentralDispatcher) sendMessage(message string) {
|
||||||
gcd.MarkTrusted(gcd.currentOpenConversation)
|
gcd.MarkTrusted(gcd.currentOpenConversation)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("SENDING MESSAGE %v to %v...\n", message, gcd.currentOpenConversation)
|
select { // fancy trick to do a non-blocking send. this means the user can only send a limited number of messages
|
||||||
connection := peer.PeerWithOnion(gcd.currentOpenConversation)
|
// before the channel buffer fills. TODO: stop the user from sending if the buffer is full
|
||||||
|
case outgoingMessages <- Message{gcd.currentOpenConversation, message, true}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("sending data, connection state: %v\n", connection.GetState())
|
|
||||||
go connection.SendPacket([]byte(message))
|
|
||||||
DeliverMessageToUI(gcd.currentOpenConversation, message, true)
|
DeliverMessageToUI(gcd.currentOpenConversation, message, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,10 +100,15 @@ func (this *GrandCentralDispatcher) importString(str string) {
|
||||||
onion := str
|
onion := str
|
||||||
name := onion
|
name := onion
|
||||||
|
|
||||||
|
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, "~") {
|
if strings.Contains(str, "~") {
|
||||||
parts := strings.Split(str, "~")
|
parts := strings.Split(str, "~")
|
||||||
onion = parts[len(parts) - 1]
|
onion = parts[len(parts)-1]
|
||||||
name = strings.Join(parts[:len(parts) - 1], " ")
|
name = strings.Join(parts[:len(parts)-1], " ")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(onion) != 56 {
|
if len(onion) != 56 {
|
||||||
|
@ -117,7 +123,7 @@ func (this *GrandCentralDispatcher) importString(str string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(name) > 32 {
|
if len(name) > 32 {
|
||||||
name = name[:32]//TODO: better strategy for long names?
|
name = name[:32] //TODO: better strategy for long names?
|
||||||
}
|
}
|
||||||
|
|
||||||
decodedPub, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion[:56]))
|
decodedPub, err := base32.StdEncoding.DecodeString(strings.ToUpper(onion[:56]))
|
||||||
|
@ -136,7 +142,7 @@ func (this *GrandCentralDispatcher) importString(str string) {
|
||||||
_, exists = peer.GetProfile().GetCustomAttribute(onion + "_name")
|
_, exists = peer.GetProfile().GetCustomAttribute(onion + "_name")
|
||||||
if exists {
|
if exists {
|
||||||
gcd.InvokePopup("already have this contact")
|
gcd.InvokePopup("already have this contact")
|
||||||
return//TODO: bring them to the duplicate
|
return //TODO: bring them to the duplicate
|
||||||
}
|
}
|
||||||
|
|
||||||
pp := model.PublicProfile{
|
pp := model.PublicProfile{
|
||||||
|
|
98
main.go
|
@ -1,31 +1,36 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"github.com/therecipe/qt/core"
|
|
||||||
"github.com/therecipe/qt/quick"
|
|
||||||
"github.com/therecipe/qt/quickcontrols2"
|
|
||||||
"github.com/therecipe/qt/widgets"
|
|
||||||
"time"
|
|
||||||
"github.com/sethvargo/go-diceware/diceware"
|
|
||||||
libpeer "cwtch.im/cwtch/peer"
|
libpeer "cwtch.im/cwtch/peer"
|
||||||
"path"
|
"cwtch.im/cwtch/peer/connections"
|
||||||
"os/user"
|
|
||||||
"fmt"
|
|
||||||
"encoding/base32"
|
"encoding/base32"
|
||||||
|
"fmt"
|
||||||
|
"github.com/sethvargo/go-diceware/diceware"
|
||||||
|
"github.com/therecipe/qt/core"
|
||||||
|
"github.com/therecipe/qt/quick"
|
||||||
|
"github.com/therecipe/qt/quickcontrols2"
|
||||||
|
"github.com/therecipe/qt/widgets"
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"cwtch.im/cwtch/peer/connections"
|
"time"
|
||||||
)
|
"strconv"
|
||||||
|
"git.openprivacy.ca/openprivacy/asaur"
|
||||||
|
)
|
||||||
|
|
||||||
var gcd *GrandCentralDispatcher
|
var gcd *GrandCentralDispatcher
|
||||||
|
|
||||||
type ContactManager map[string]*Contact
|
type ContactManager map[string]*Contact
|
||||||
|
|
||||||
var contactMgr ContactManager
|
var contactMgr ContactManager
|
||||||
var peer libpeer.CwtchPeer
|
var peer libpeer.CwtchPeer
|
||||||
|
var outgoingMessages chan Message
|
||||||
|
|
||||||
type Contact struct {
|
type Contact struct {
|
||||||
Messages []Message
|
Messages []Message
|
||||||
Unread int
|
Unread int
|
||||||
Status connections.ConnectionState
|
Status connections.ConnectionState
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Contact) AddMessage(m Message) {
|
func (this *Contact) AddMessage(m Message) {
|
||||||
|
@ -34,7 +39,7 @@ func (this *Contact) AddMessage(m Message) {
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
With, Message string
|
With, Message string
|
||||||
FromMe bool
|
FromMe bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeliverMessageToUI(from, message string, fromMe bool) {
|
func DeliverMessageToUI(from, message string, fromMe bool) {
|
||||||
|
@ -77,8 +82,12 @@ func main() {
|
||||||
view.SetSource(core.NewQUrl3("qrc:/qml/main.qml", 0))
|
view.SetSource(core.NewQUrl3("qrc:/qml/main.qml", 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outgoingMessages = make(chan Message, 1000)
|
||||||
|
go postmanPat()
|
||||||
|
|
||||||
initialize(view)
|
initialize(view)
|
||||||
view.Show()
|
view.Show()
|
||||||
|
go torStatusPoller()
|
||||||
go presencePoller()
|
go presencePoller()
|
||||||
go ricochetListener()
|
go ricochetListener()
|
||||||
go alice()
|
go alice()
|
||||||
|
@ -128,6 +137,37 @@ func presencePoller() { // TODO: make this subscribe-able in ricochet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func torStatusPoller() {
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
//todo: this should use a config manager
|
||||||
|
//todo: also, try dialing the proxy to differentiate tor not running vs control port not configured
|
||||||
|
rawStatus, err := asaur.GetInfo("localhost:9051", "tcp4", "", "status/bootstrap-phase")
|
||||||
|
if err != nil {
|
||||||
|
gcd.TorStatus(0, "can't find tor. is it running? is the controlport configured?")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
status := asaur.ParseBootstrapPhase(rawStatus)
|
||||||
|
progress, _ := strconv.Atoi(status["PROGRESS"])
|
||||||
|
|
||||||
|
if status["TAG"] == "done" {
|
||||||
|
gcd.TorStatus(3, "tor appears to be running just fine!")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if progress == 0 {
|
||||||
|
gcd.TorStatus(1, "tor is trying to start up")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
gcd.TorStatus(2, status["SUMMARY"])
|
||||||
|
//qCwtchApp.SetTorStatusProgress(progress)
|
||||||
|
//qCwtchApp.SetTorStatusSummary(status["SUMMARY"])
|
||||||
|
//if status["TAG"] == "done" {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ricochetListener() {
|
func ricochetListener() {
|
||||||
processData := func(onion string, data []byte) []byte {
|
processData := func(onion string, data []byte) []byte {
|
||||||
/* _, exists := peer.GetProfile().GetCustomAttribute(onion + "_name")
|
/* _, exists := peer.GetProfile().GetCustomAttribute(onion + "_name")
|
||||||
|
@ -159,6 +199,30 @@ func ricochetListener() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func postmanPat() {
|
||||||
|
postOffice := make(map[string]chan Message)
|
||||||
|
|
||||||
|
for {
|
||||||
|
m := <-outgoingMessages
|
||||||
|
|
||||||
|
_, found := postOffice[m.With]
|
||||||
|
if !found {
|
||||||
|
postOffice[m.With] = make(chan Message, 100)
|
||||||
|
go andHisBlackAndWhiteCat(postOffice[m.With])
|
||||||
|
}
|
||||||
|
|
||||||
|
postOffice[m.With] <- m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func andHisBlackAndWhiteCat(incomingMessages chan Message) {
|
||||||
|
for {
|
||||||
|
m := <-incomingMessages
|
||||||
|
connection := peer.PeerWithOnion(m.With)
|
||||||
|
connection.SendPacket([]byte(m.Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func initialize(view *quick.QQuickView) {
|
func initialize(view *quick.QQuickView) {
|
||||||
//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
|
||||||
|
@ -203,11 +267,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 {
|
||||||
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 {
|
||||||
fmt.Printf("error: %v %v %v\n", onion, err, barr)
|
fmt.Printf("error: %v %v %v\n", onion, err, barr)
|
||||||
return "qrc:/qml/images/extra/openprivacy.png"
|
return "qrc:/qml/images/extra/openprivacy.png"
|
||||||
}
|
}
|
||||||
return "qrc:/qml/images/profiles/" + choices[int(barr[33]) % len(choices)] + ".png"
|
return "qrc:/qml/images/profiles/" + choices[int(barr[33])%len(choices)] + ".png"
|
||||||
}
|
}
|
After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
|
@ -18,7 +18,7 @@ Item {
|
||||||
|
|
||||||
|
|
||||||
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
|
Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS
|
||||||
color: "#FFEEEE"
|
color: "#D2C0DD"
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
Layout.minimumWidth: 200
|
Layout.minimumWidth: 200
|
||||||
Layout.maximumWidth: 200
|
Layout.maximumWidth: 200
|
||||||
|
|
BIN
qml/main.qmlc
|
@ -10,22 +10,22 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
property alias nick: cn.text
|
property alias nick: cn.text
|
||||||
property alias image: img.source
|
property alias image: imgProfile.source
|
||||||
property string onion
|
property string onion
|
||||||
property string badge
|
property string badge
|
||||||
property bool isActive
|
property bool isActive
|
||||||
property bool isHover
|
property bool isHover
|
||||||
property bool isTrusted
|
property bool isTrusted
|
||||||
property int status
|
property alias status: imgProfile.status
|
||||||
|
|
||||||
|
|
||||||
Rectangle { // CONTACT ENTRY BACKGROUND COLOR
|
Rectangle { // CONTACT ENTRY BACKGROUND COLOR
|
||||||
id: root
|
id: root
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
height: childrenRect.height
|
height: childrenRect.height + 3
|
||||||
width: parent.width
|
width: parent.width
|
||||||
color: isHover ? "#EEEEFF" : (isActive ? "#EEEEFF" : "#FFEEEE")
|
color: isHover ? "#D2D2F3" : (isActive ? "#D2D2F3" : "#D2C0DD")
|
||||||
|
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
|
@ -34,39 +34,8 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
|
||||||
Item { // PROFILE IMAGE
|
ContactPicture {
|
||||||
id: imgProfile
|
id: imgProfile
|
||||||
implicitWidth: 48
|
|
||||||
implicitHeight: 48
|
|
||||||
anchors.left: parent.left
|
|
||||||
|
|
||||||
|
|
||||||
Image {
|
|
||||||
id: img
|
|
||||||
anchors.fill: parent
|
|
||||||
fillMode: Image.PreserveAspectFit
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle { // PRESENCE INDICATOR
|
|
||||||
color: "#FFFFFF"
|
|
||||||
width: 8
|
|
||||||
height: 8
|
|
||||||
radius: 2
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.margins: 4
|
|
||||||
|
|
||||||
|
|
||||||
Rectangle { //-2:WtfCodeError,-1:Untrusted,0:Disconnected,1:Connecting,2:Connected,3:Authenticated,4:Failed,5:Killed
|
|
||||||
color: status==3?"green":(status==4?"red":(status==-1?"blue":"orange"))
|
|
||||||
width: 5
|
|
||||||
height: 5
|
|
||||||
radius: 2
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.margins: 1.5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Label { // CONTACT NAME
|
Label { // CONTACT NAME
|
||||||
|
@ -77,6 +46,7 @@ RowLayout { // LOTS OF NESTING TO DEAL WITH QT WEIRDNESS, SORRY
|
||||||
anchors.left: imgProfile.right
|
anchors.left: imgProfile.right
|
||||||
font.pixelSize: 16
|
font.pixelSize: 16
|
||||||
font.italic: !isTrusted
|
font.italic: !isTrusted
|
||||||
|
textFormat: Text.PlainText
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle { // UNREAD MESSAGES?
|
Rectangle { // UNREAD MESSAGES?
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
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 CustomQmlTypes 1.0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: imgProfile
|
||||||
|
implicitWidth: 48
|
||||||
|
implicitHeight: 48
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.margins: 5
|
||||||
|
|
||||||
|
property alias source: img.source
|
||||||
|
property int status
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 48
|
||||||
|
height: 48
|
||||||
|
color: "#FFFFFF"
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
|
|
||||||
|
Image { // PROFILE IMAGE
|
||||||
|
id: img
|
||||||
|
anchors.fill: parent
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image { // CIRCLE MASK
|
||||||
|
id: mask
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
visible: false
|
||||||
|
source: "qrc:/qml/images/extra/clipcircle.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
OpacityMask {
|
||||||
|
anchors.fill: img
|
||||||
|
source: img
|
||||||
|
maskSource: mask
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle { // PRESENCE INDICATOR
|
||||||
|
color: "#FFFFFF"
|
||||||
|
width: 8
|
||||||
|
height: 8
|
||||||
|
radius: 2
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: 4
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle { //-2:WtfCodeError,-1:Untrusted,0:Disconnected,1:Connecting,2:Connected,3:Authenticated,4:Failed,5:Killed
|
||||||
|
color: status == 3 ? "green" : status == -1 ? "blue" : status == 1 ? "orange" : status == 2 ? "orange" : "red"
|
||||||
|
width: 5
|
||||||
|
height: 5
|
||||||
|
radius: 2
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: 1.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,12 +23,74 @@ ColumnLayout {
|
||||||
implicitHeight: 96
|
implicitHeight: 96
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
|
||||||
|
Rectangle { // WHITE CIRCLE BORDER
|
||||||
|
width: 96
|
||||||
|
height: 96
|
||||||
|
color: "#FFFFFF"
|
||||||
|
radius: width / 2
|
||||||
|
|
||||||
Image {
|
Image { // ACTUAL IMAGE
|
||||||
id: imgProfileImg
|
id: imgProfileImg
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
source: "qrc:/qml/images/extra/robot.png"
|
visible: false
|
||||||
|
}
|
||||||
|
|
||||||
|
Image { // INNER CIRCLE MASK
|
||||||
|
id: mask
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
visible: false
|
||||||
|
source: "qrc:/qml/images/extra/clipcircle.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
OpacityMask { // WE PUT IT ALL TOGETHER ANNND
|
||||||
|
anchors.fill: imgProfileImg
|
||||||
|
source: imgProfileImg
|
||||||
|
maskSource: mask
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle { // TOR STATUS INDICATOR
|
||||||
|
color: "#FFFFFF"
|
||||||
|
width: 12
|
||||||
|
height: 12
|
||||||
|
radius: 3
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: 8
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle { //0: no tor, 1: progress 0%, 2: progress 1-99%, 3: DONE
|
||||||
|
id: rectTorStatus
|
||||||
|
color: code == 3 ? "green": code == 2 ? "orange" : code == 1 ? "yellow" : "red"
|
||||||
|
width: 8
|
||||||
|
height: 8
|
||||||
|
radius: 2
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: 2
|
||||||
|
|
||||||
|
property int code
|
||||||
|
property string message
|
||||||
|
property bool hovered
|
||||||
|
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onEntered: rectTorStatus.hovered = true
|
||||||
|
|
||||||
|
onExited: rectTorStatus.hovered = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.delay: 400
|
||||||
|
ToolTip.timeout: 5000
|
||||||
|
ToolTip.text: message
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +100,6 @@ ColumnLayout {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
|
||||||
onUpdated: {
|
onUpdated: {
|
||||||
console.log("sending updatenick ["+nick+"]")
|
|
||||||
gcd.updateNick(lblNick.text)
|
gcd.updateNick(lblNick.text)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,5 +197,10 @@ ColumnLayout {
|
||||||
onion = _onion
|
onion = _onion
|
||||||
image = _image
|
image = _image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTorStatus: function(code, str) {
|
||||||
|
rectTorStatus.code = code
|
||||||
|
rectTorStatus.message = str
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|