Merge branch 'GSoC2017-evaluation-final'
@ -1189,7 +1189,7 @@ int RsServer::StartupRetroShare()
|
||||
#ifdef __APPLE__
|
||||
plugins_directories.push_back(rsAccounts->PathDataDirectory()) ;
|
||||
#endif
|
||||
#ifndef WINDOWS_SYS
|
||||
#if !defined(WINDOWS_SYS) && defined(PLUGIN_DIR)
|
||||
plugins_directories.push_back(std::string(PLUGIN_DIR)) ;
|
||||
#endif
|
||||
std::string extensions_dir = rsAccounts->PathBaseDirectory() + "/extensions6/" ;
|
||||
|
@ -31,7 +31,7 @@ QtObject
|
||||
property int loginAttemptCount: 0
|
||||
property bool attemptingLogin: false
|
||||
|
||||
property int loginNotificationTime: 0
|
||||
property var loginNotificationTime: 0
|
||||
|
||||
function delay(msecs, func)
|
||||
{
|
||||
@ -58,7 +58,11 @@ QtObject
|
||||
var runState = jsonReponse.data.runstate
|
||||
if(typeof(runState) !== 'string')
|
||||
{
|
||||
console.log("runStateCallback(par)", "Core hanged!", par.response)
|
||||
coreReady = false
|
||||
console.log("runStateCallback(par)",
|
||||
"Core hanged!",
|
||||
"typeof(runState):", typeof(runState),
|
||||
"par.response:", par.response)
|
||||
return
|
||||
}
|
||||
|
||||
@ -70,7 +74,7 @@ QtObject
|
||||
break
|
||||
case "fatal_error":
|
||||
coreReady = false
|
||||
console.log("Core hanged")
|
||||
console.log("Core hanged! runState:", runState)
|
||||
break
|
||||
case "waiting_account_select":
|
||||
coreReady = false
|
||||
@ -145,7 +149,11 @@ QtObject
|
||||
interval: 700
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: rsApi.request("/control/password/", "", attemptPasswordCB)
|
||||
onTriggered:
|
||||
{
|
||||
if(am.coreReady) attemptPasswordCBCB()
|
||||
else rsApi.request("/control/password/", "", attemptPasswordCB)
|
||||
}
|
||||
|
||||
function attemptPasswordCB(par)
|
||||
{
|
||||
|
@ -41,10 +41,17 @@ QtObject
|
||||
"unread conversations")
|
||||
notificationsBridge.notify(
|
||||
qsTr("New message!"),
|
||||
qsTr("Unread messages in %1 %2").arg(convCnt).arg(
|
||||
convCnt > 1 ?
|
||||
qsTr("conversations") : qsTr("conversation")
|
||||
)
|
||||
(convCnt > 1) ?
|
||||
qsTr("Unread messages in %1 conversations").arg(convCnt):
|
||||
qsTr("%1 Unread %2 from %3")
|
||||
.arg(json.data[0].unread_count)
|
||||
.arg(json.data[0].unread_count > 1 ? "messages" : "message")
|
||||
.arg(json.data[0].remote_author_name),
|
||||
qsTr("/contacts%1").arg(
|
||||
convCnt == 1?
|
||||
"?gxsId="+json.data[0].remote_author_id +
|
||||
"&name="+json.data[0].remote_author_name
|
||||
: "")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -21,109 +21,266 @@ import QtQuick.Controls 2.0
|
||||
import org.retroshare.qml_components.LibresapiLocalClient 1.0
|
||||
import "." //Needed for ClipboardWrapper singleton
|
||||
import "URI.js" as UriJs
|
||||
import "components/."
|
||||
|
||||
Item
|
||||
{
|
||||
property ApplicationWindow mW
|
||||
|
||||
property int infoIconHeight: 20
|
||||
|
||||
Column
|
||||
{
|
||||
anchors.fill: parent
|
||||
spacing: 7
|
||||
|
||||
Text
|
||||
{
|
||||
text: qsTr("Import/export node from/to clipboard")
|
||||
text: qsTr("Import node from clipboard")
|
||||
font.bold: true
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Button
|
||||
{
|
||||
text: qsTr("Export own certificate link")
|
||||
onClicked:
|
||||
font.pixelSize: 13
|
||||
|
||||
Rectangle
|
||||
{
|
||||
console.log("onClicked", text)
|
||||
rsApi.request(
|
||||
"/peers/self/certificate/", "",
|
||||
function(par)
|
||||
{
|
||||
var radix = JSON.parse(par.response).data.cert_string
|
||||
var name = mainWindow.user_name
|
||||
var encodedName = UriJs.URI.encode(name)
|
||||
var nodeUrl = (
|
||||
"retroshare://certificate?" +
|
||||
"name=" + encodedName +
|
||||
"&radix=" + UriJs.URI.encode(radix) +
|
||||
"&location=" + encodedName )
|
||||
ClipboardWrapper.postToClipBoard(nodeUrl)
|
||||
linkCopiedPopup.itemName = name
|
||||
linkCopiedPopup.open()
|
||||
platformGW.shareUrl(nodeUrl);
|
||||
})
|
||||
id: backgroundRectangle
|
||||
anchors.fill: parent.fill
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width + 20
|
||||
height: parent.height + 5
|
||||
|
||||
color:"transparent"
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: borderBottom
|
||||
width: parent.width
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
color: "lightgrey"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
Row
|
||||
{
|
||||
text: qsTr("Import trusted node")
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 5
|
||||
|
||||
onClicked:
|
||||
ButtonIcon
|
||||
{
|
||||
var cptext = ClipboardWrapper.getFromClipBoard()
|
||||
height: infoIconHeight
|
||||
anchors.verticalCenter: importButton.verticalCenter
|
||||
width: height
|
||||
imgUrl: "/icons/info.svg"
|
||||
onClicked:
|
||||
{
|
||||
tooltipSpace.show(qsTr("Import a friend certificate from your clipboard. <br>"+
|
||||
"This will add him as trusted node."))
|
||||
|
||||
console.log("typeof(cptext)", typeof(cptext))
|
||||
if(cptext.search("://") > 0)
|
||||
mainWindow.handleIntentUri(cptext)
|
||||
else
|
||||
rsApi.request(
|
||||
"/peers/examine_cert/",
|
||||
JSON.stringify({cert_string: cptext}),
|
||||
function(par)
|
||||
{
|
||||
console.log("/peers/examine_cert/ CB",
|
||||
par.response)
|
||||
var resp = JSON.parse(par.response)
|
||||
if(resp.returncode === "fail")
|
||||
}
|
||||
}
|
||||
|
||||
ButtonText
|
||||
{
|
||||
id: importButton
|
||||
text: qsTr("Import trusted node")
|
||||
fontSize: 14
|
||||
|
||||
onClicked:
|
||||
{
|
||||
var cptext = ClipboardWrapper.getFromClipBoard()
|
||||
|
||||
console.log("typeof(cptext)", typeof(cptext))
|
||||
if(cptext.search("://") > 0)
|
||||
mainWindow.handleIntentUri(cptext)
|
||||
else
|
||||
rsApi.request(
|
||||
"/peers/examine_cert/",
|
||||
JSON.stringify({cert_string: cptext}),
|
||||
function(par)
|
||||
{
|
||||
importErrorPop.text = resp.debug_msg
|
||||
importErrorPop.open()
|
||||
return
|
||||
}
|
||||
console.log("/peers/examine_cert/ CB",
|
||||
par.response)
|
||||
var resp = JSON.parse(par.response)
|
||||
if(resp.returncode === "fail")
|
||||
{
|
||||
importErrorPop.text = resp.debug_msg
|
||||
importErrorPop.open()
|
||||
return
|
||||
}
|
||||
|
||||
var jData = resp.data
|
||||
stackView.push(
|
||||
"qrc:/TrustedNodeDetails.qml",
|
||||
{
|
||||
nodeCert: cptext,
|
||||
pgpName: jData.name,
|
||||
pgpId: jData.pgp_id,
|
||||
locationName: jData.location,
|
||||
sslIdTxt: jData.peer_id
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
var jData = resp.data
|
||||
stackView.push(
|
||||
"qrc:/TrustedNodeDetails.qml",
|
||||
{
|
||||
nodeCert: cptext,
|
||||
pgpName: jData.name,
|
||||
pgpId: jData.pgp_id,
|
||||
locationName: jData.location,
|
||||
sslIdTxt: jData.peer_id
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
Text
|
||||
{
|
||||
text: qsTr("Export own plain certificate")
|
||||
onClicked:
|
||||
{
|
||||
rsApi.request(
|
||||
"/peers/self/certificate/", "",
|
||||
function(par)
|
||||
{
|
||||
var radix = JSON.parse(par.response).data.cert_string
|
||||
var name = mainWindow.user_name
|
||||
ClipboardWrapper.postToClipBoard(radix)
|
||||
text: qsTr("Export node")
|
||||
font.bold: true
|
||||
wrapMode: Text.Wrap
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
linkCopiedPopup.itemName = name
|
||||
linkCopiedPopup.open()
|
||||
})
|
||||
font.pixelSize: 13
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent.fill
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width + 20
|
||||
height: parent.height + 5
|
||||
|
||||
color:"transparent"
|
||||
|
||||
Rectangle
|
||||
{
|
||||
width: parent.width
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
color: "lightgrey"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 5
|
||||
|
||||
ButtonIcon
|
||||
{
|
||||
height: infoIconHeight
|
||||
anchors.verticalCenter: btRsCert.verticalCenter
|
||||
width: height
|
||||
imgUrl: "/icons/info.svg"
|
||||
onClicked:
|
||||
{
|
||||
tooltipSpace.show(qsTr("Share your RetroShare link! <br>"+
|
||||
"Send it to a friend and start talk!"))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ButtonText
|
||||
{
|
||||
id: btRsCert
|
||||
text: qsTr("Export own certificate link")
|
||||
fontSize: 14
|
||||
onClicked:
|
||||
{
|
||||
console.log("onClicked", text)
|
||||
rsApi.request(
|
||||
"/peers/self/certificate/", "",
|
||||
function(par)
|
||||
{
|
||||
var radix = JSON.parse(par.response).data.cert_string
|
||||
var name = mainWindow.user_name
|
||||
var encodedName = UriJs.URI.encode(name)
|
||||
var nodeUrl = (
|
||||
"retroshare://certificate?" +
|
||||
"name=" + encodedName +
|
||||
"&radix=" + UriJs.URI.encode(radix) +
|
||||
"&location=" + encodedName )
|
||||
ClipboardWrapper.postToClipBoard(nodeUrl)
|
||||
linkCopiedPopup.itemName = name
|
||||
linkCopiedPopup.open()
|
||||
platformGW.shareUrl(nodeUrl);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonText
|
||||
{
|
||||
onClicked: plainCertificateRow.visible = !plainCertificateRow.visible
|
||||
iconUrl: "/icons/options.svg"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: "white"
|
||||
borderWidth: 1
|
||||
text: qsTr("Advanced")
|
||||
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
id: plainCertificateRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 5
|
||||
visible: false
|
||||
|
||||
ButtonIcon
|
||||
{
|
||||
height: infoIconHeight
|
||||
anchors.verticalCenter: btPlainCert.verticalCenter
|
||||
width: height
|
||||
imgUrl: "/icons/info.svg"
|
||||
onClicked:
|
||||
{
|
||||
tooltipSpace.show(qsTr("This will copy your RetroShare plain certificate.<br>"+
|
||||
"Add it manually to your friend client."))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ButtonText
|
||||
{
|
||||
id: btPlainCert
|
||||
text: qsTr("Export own plain certificate")
|
||||
fontSize: 14
|
||||
onClicked:
|
||||
{
|
||||
rsApi.request(
|
||||
"/peers/self/certificate/", "",
|
||||
function(par)
|
||||
{
|
||||
var radix = JSON.parse(par.response).data.cert_string
|
||||
var name = mainWindow.user_name
|
||||
ClipboardWrapper.postToClipBoard(radix)
|
||||
|
||||
linkCopiedPopup.itemName = name
|
||||
linkCopiedPopup.open()
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: tooltipSpace
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
ToolTip
|
||||
{
|
||||
id: infoTooltip
|
||||
timeout: 5000
|
||||
Component.onCompleted: show("afafsaf",0)
|
||||
}
|
||||
function show (infoText)
|
||||
{
|
||||
infoTooltip.text = infoText
|
||||
infoTooltip.open()
|
||||
}
|
||||
}
|
||||
|
||||
TimedPopup
|
||||
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* RetroShare Android QML App
|
||||
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.7
|
||||
|
||||
|
||||
Item
|
||||
{
|
||||
id: compRoot
|
||||
|
||||
property string gxs_id
|
||||
|
||||
height: 130
|
||||
width: height
|
||||
|
||||
|
||||
////////////// The following should be considered privates /////////////////////
|
||||
|
||||
property bool has_avatar: false
|
||||
property int avatarAttemptCnt: 0
|
||||
function getDetails()
|
||||
{
|
||||
++compRoot.avatarAttemptCnt
|
||||
rsApi.request(
|
||||
"/identity/get_identity_details",
|
||||
JSON.stringify({ gxs_id: compRoot.gxs_id }),
|
||||
function(par)
|
||||
{
|
||||
var jData = JSON.parse(par.response).data
|
||||
setDetails(jData)
|
||||
if(!compRoot.has_avatar &&
|
||||
compRoot.avatarAttemptCnt < 3) getDetails()
|
||||
})
|
||||
}
|
||||
function setDetails(data)
|
||||
{
|
||||
compRoot.has_avatar = data.avatar.length > 0
|
||||
if(compRoot.has_avatar)
|
||||
{
|
||||
contactAvatar.source =
|
||||
"data:image/png;base64," + data.avatar
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: if(visible && !has_avatar) getDetails()
|
||||
onVisibleChanged: if(visible && !has_avatar) getDetails()
|
||||
|
||||
Image
|
||||
{
|
||||
id: contactAvatar
|
||||
anchors.fill: parent
|
||||
visible: compRoot.has_avatar
|
||||
}
|
||||
|
||||
ColorHash
|
||||
{
|
||||
anchors.fill: parent
|
||||
visible: !compRoot.has_avatar
|
||||
hash: compRoot.gxs_id
|
||||
}
|
||||
}
|
128
retroshare-qml-app/src/ChatBubbleDelegate.qml
Normal file
@ -0,0 +1,128 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Layouts 1.2
|
||||
import QtQuick.Controls 2.0
|
||||
import "." // To load styles
|
||||
import "./components"
|
||||
import "URI.js" as UriJs
|
||||
|
||||
Item
|
||||
{
|
||||
|
||||
id: chatBubbleDelegate
|
||||
height: bubble.height
|
||||
width: mainWindow.width - (styles.aditionalBubbleWidth - 10)
|
||||
|
||||
property var styles: StyleChat.bubble
|
||||
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: rootBubble
|
||||
anchors.fill: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: "transparent"
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: bubble
|
||||
width: Math.min (
|
||||
rootBubble.width * styles.bubbleMaxWidth,
|
||||
Math.max(mesageText.implicitWidth, sendersName.implicitWidth )
|
||||
) + timeText.implicitWidth + styles.aditionalBubbleWidth
|
||||
height: mesageText.height + sendersName.height + styles.aditionalBubbleHeight
|
||||
|
||||
|
||||
anchors.left: (model.incoming)? parent.left : undefined
|
||||
anchors.right: (!model.incoming)? parent.right : undefined
|
||||
|
||||
|
||||
color: (!model.incoming)? styles.colorOutgoing : styles.colorIncoming
|
||||
radius: styles.radius
|
||||
|
||||
|
||||
Text
|
||||
{
|
||||
id: sendersName
|
||||
visible: model.incoming
|
||||
text: (model.incoming)? model.author_name + ":" : ""
|
||||
color: styles.colorSenderName
|
||||
font.bold: true
|
||||
|
||||
anchors.leftMargin: styles.lMarginBubble
|
||||
anchors.rightMargin: styles.rMarginBubble
|
||||
anchors.topMargin: styles.tMarginBubble
|
||||
anchors.top: bubble.top
|
||||
|
||||
anchors.left: (model.incoming)? parent.left : undefined
|
||||
anchors.right:(!model.incoming)? parent.right : undefined
|
||||
|
||||
// Used for give minimum heigh to time when the message is bigger than the bubble in sended messages
|
||||
height: (model.incoming || !model.incoming &&
|
||||
mesageText.implicitWidth >= (rootBubble.width * styles.bubbleMaxWidth) )? implicitHeight : 0
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: timeText
|
||||
text: getMessageTime()
|
||||
color: styles.colorMessageTime
|
||||
|
||||
anchors.left: (!model.incoming)? parent.left : undefined
|
||||
anchors.right:(model.incoming)? parent.right : undefined
|
||||
|
||||
anchors.top: bubble.top
|
||||
anchors.leftMargin: styles.lMarginBubble
|
||||
anchors.rightMargin: styles.rMarginBubble
|
||||
anchors.topMargin: styles.tMarginBubble
|
||||
|
||||
}
|
||||
|
||||
|
||||
Text
|
||||
{
|
||||
id: mesageText
|
||||
text: UriJs.URI.withinString(model.msg, function(url)
|
||||
{
|
||||
return "<a href=\""+ url + "\">" + url + "</a>";
|
||||
})
|
||||
|
||||
width: rootBubble.width * styles.bubbleMaxWidth + timeText.width
|
||||
anchors.left: (model.incoming)? parent.left : undefined
|
||||
anchors.right:(!model.incoming)? parent.right : undefined
|
||||
|
||||
anchors.top: sendersName.bottom
|
||||
anchors.leftMargin: styles.lMarginBubble
|
||||
anchors.rightMargin: styles.rMarginBubble
|
||||
|
||||
|
||||
textFormat: Text.RichText
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
|
||||
// Used for the correct alineation when the message must be on right
|
||||
horizontalAlignment:(!model.incoming &&
|
||||
mesageText.implicitWidth <= (rootBubble.width * styles.bubbleMaxWidth)
|
||||
)? Text.AlignRight : Text.AlignLeft
|
||||
|
||||
wrapMode: Text.Wrap
|
||||
font.pixelSize: styles.messageTextSize
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getMessageTime()
|
||||
{
|
||||
var timeFormat = "hh:mm";
|
||||
var recvDate = new Date(model.recv_time*1000)
|
||||
|
||||
var timeString = Qt.formatDateTime(recvDate, timeFormat)
|
||||
return timeString
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
185
retroshare-qml-app/src/ChatCache.qml
Normal file
@ -0,0 +1,185 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQml 2.3
|
||||
import QtQuick.Controls 2.0
|
||||
import org.retroshare.qml_components.LibresapiLocalClient 1.0
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
QtObject
|
||||
{
|
||||
|
||||
id: chatCache
|
||||
|
||||
property QtObject lastMessageCache: QtObject
|
||||
{
|
||||
id: lastMessageCache
|
||||
property var lastMessageList: ({})
|
||||
|
||||
signal lastMessageChanged(var chatI, var newLastMessage)
|
||||
|
||||
|
||||
function updateLastMessageCache (chatId, chatModel)
|
||||
{
|
||||
console.log("updateLastMessageCache (chatId, chatModel)", chatId)
|
||||
// First creates the chat id object for don't wait to work with the object if is needed to call RS api
|
||||
if (!lastMessageList[chatId]) {
|
||||
lastMessageList[chatId] = {}
|
||||
console.log("Last message cache created!")
|
||||
}
|
||||
if (!chatModel) {
|
||||
rsApi.request( "/chat/messages/"+chatId, "", function (par){
|
||||
updateLastMessage(chatId, par.response)
|
||||
})
|
||||
} else {
|
||||
updateLastMessage (chatId, chatModel)
|
||||
}
|
||||
}
|
||||
|
||||
function updateLastMessage (chatId, chatModel)
|
||||
{
|
||||
console.log("updateLastMessage (chatId, chatModel)")
|
||||
var lastMessage = findChatLastMessage (chatModel)
|
||||
lastMessageList[chatId].lastMessage = lastMessage
|
||||
lastMessageChanged(chatId, lastMessage)
|
||||
}
|
||||
|
||||
function findChatLastMessage (chatModel)
|
||||
{
|
||||
var messagesData = JSON.parse(chatModel).data
|
||||
return messagesData.slice(-1)[0]
|
||||
}
|
||||
|
||||
function findChatFirstMessage (chatModel)
|
||||
{
|
||||
var messagesData = JSON.parse(chatModel).data
|
||||
return messagesData.slice[0]
|
||||
}
|
||||
|
||||
function setRemoteGXS (chatId, remoteGXS)
|
||||
{
|
||||
if (!lastMessageList[chatId]) {
|
||||
lastMessageList[chatId] = {}
|
||||
console.log("Last message cache created!")
|
||||
}
|
||||
if (lastMessageList[chatId] && !lastMessageList[chatId].remoteGXS){
|
||||
lastMessageList[chatId].remoteGXS = remoteGXS
|
||||
return true
|
||||
}
|
||||
else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function getChatIdFromGxs (gxs)
|
||||
{
|
||||
for (var key in lastMessageList) {
|
||||
if ( lastMessageList[key].remoteGXS &&
|
||||
lastMessageList[key].remoteGXS.gxs === gxs ) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
function getGxsFromChatId (chatId)
|
||||
{
|
||||
if (lastMessageList[chatId]) return lastMessageList[chatId].remoteGXS
|
||||
return undefined
|
||||
}
|
||||
|
||||
function getChatLastMessage (chatId)
|
||||
{
|
||||
if (lastMessageList[chatId]) {
|
||||
return lastMessageList[chatId].lastMessage
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
property QtObject contactsCache: QtObject
|
||||
{
|
||||
id: contactsCache
|
||||
property var contactsList
|
||||
property var own
|
||||
property var identityDetails: ({})
|
||||
|
||||
|
||||
function getContactFromGxsId (gxsId)
|
||||
{
|
||||
console.log("getContactFromGxsId (gxsId)", gxsId)
|
||||
for(var i in contactsList)
|
||||
{
|
||||
if (contactsList[i].gxs_id == gxsId) return contactsList[i]
|
||||
}
|
||||
}
|
||||
|
||||
function getIdentityDetails (gxsId)
|
||||
{
|
||||
if (identityDetails[gxsId]) return identityDetails[gxsId]
|
||||
return ""
|
||||
}
|
||||
|
||||
function setIdentityDetails (jData)
|
||||
{
|
||||
identityDetails[jData.gxs_id] = jData
|
||||
}
|
||||
|
||||
function getIdentityAvatar (gxsId)
|
||||
{
|
||||
|
||||
if (identityDetails[gxsId] && identityDetails[gxsId].avatar !== undefined)
|
||||
{
|
||||
return identityDetails[gxsId].avatar
|
||||
}
|
||||
return ""
|
||||
}
|
||||
function delIdentityAvatar (gxsId)
|
||||
{
|
||||
if (identityDetails[gxsId] && identityDetails[gxsId].avatar !== undefined)
|
||||
{
|
||||
identityDetails[gxsId].avatar = ""
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
property QtObject facesCache: QtObject
|
||||
{
|
||||
id: facesCache
|
||||
property var iconCache: ({})
|
||||
property var callbackCache: ({})
|
||||
|
||||
}
|
||||
|
||||
property QtObject chatHelper: QtObject
|
||||
{
|
||||
id: chatHelper
|
||||
property var gxs_id
|
||||
property var name
|
||||
property var cb
|
||||
|
||||
function startDistantChat (own_gxs_id, gxs_id, name, cb)
|
||||
{
|
||||
console.log("startDistantChat()")
|
||||
chatHelper.gxs_id = gxs_id
|
||||
chatHelper.name = name
|
||||
chatHelper.cb = cb
|
||||
var jsonData = { "own_gxs_hex": own_gxs_id,
|
||||
"remote_gxs_hex": gxs_id }
|
||||
rsApi.request("/chat/initiate_distant_chat",
|
||||
JSON.stringify(jsonData),
|
||||
startDistantChatCB)
|
||||
|
||||
}
|
||||
|
||||
function startDistantChatCB (par)
|
||||
{
|
||||
var chatId = JSON.parse(par.response).data.chat_id
|
||||
lastMessageCache.setRemoteGXS(chatId, { gxs: gxs_id, name: name})
|
||||
cb(chatId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -18,15 +18,22 @@
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.2
|
||||
import org.retroshare.qml_components.LibresapiLocalClient 1.0
|
||||
import "." //Needed for TokensManager singleton
|
||||
import "./components"
|
||||
import "./components/emoji"
|
||||
|
||||
Item
|
||||
{
|
||||
id: chatView
|
||||
property string chatId
|
||||
property var gxsInfo: ""
|
||||
property int token: 0
|
||||
|
||||
property string objectName:"chatView"
|
||||
|
||||
|
||||
function refreshData()
|
||||
{
|
||||
console.log("chatView.refreshData()", visible)
|
||||
@ -38,66 +45,343 @@ Item
|
||||
token = JSON.parse(par.response).statetoken
|
||||
TokensManager.registerToken(token, refreshData)
|
||||
|
||||
ChatCache.lastMessageCache.updateLastMessageCache(chatId, chatModel.json)
|
||||
|
||||
if(chatListView.visible)
|
||||
{
|
||||
chatListView.positionViewAtEnd()
|
||||
rsApi.request("/chat/mark_chat_as_read/"+chatId)
|
||||
}
|
||||
} )
|
||||
})
|
||||
}
|
||||
|
||||
Component.onCompleted: refreshData()
|
||||
Component.onCompleted:
|
||||
{
|
||||
refreshData()
|
||||
}
|
||||
onFocusChanged: focus && refreshData()
|
||||
|
||||
function changeState ()
|
||||
{
|
||||
toolBar.state = "CHATVIEW"
|
||||
gxsInfo= ChatCache.lastMessageCache.getGxsFromChatId(chatView.chatId)
|
||||
toolBar.gxsSource = gxsInfo.gxs
|
||||
toolBar.titleText = gxsInfo.name
|
||||
}
|
||||
|
||||
|
||||
JSONListModel
|
||||
{
|
||||
id: chatModel
|
||||
query: "$.data[*]"
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: chatMessageDelegate
|
||||
Item
|
||||
{
|
||||
height: 20
|
||||
Row
|
||||
{
|
||||
Text { text: author_name }
|
||||
Text { text: ": " + msg }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView
|
||||
{
|
||||
property var styles: StyleChat.chat
|
||||
id: chatListView
|
||||
width: parent.width
|
||||
height: 300
|
||||
width: parent.width - styles.bubbleMargin
|
||||
height: parent.height - inferiorPanel.height
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
model: chatModel.model
|
||||
delegate: chatMessageDelegate
|
||||
}
|
||||
delegate: ChatBubbleDelegate {}
|
||||
spacing: styles.bubbleSpacing
|
||||
preferredHighlightBegin: 1
|
||||
|
||||
TextField
|
||||
{
|
||||
id: msgComposer
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
width: chatView.width - sendButton.width
|
||||
}
|
||||
|
||||
Button
|
||||
{
|
||||
id: sendButton
|
||||
text: "Send"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
|
||||
onClicked:
|
||||
onHeightChanged:
|
||||
{
|
||||
var jsonData = {"chat_id":chatView.chatId, "msg":msgComposer.text}
|
||||
rsApi.request( "/chat/send_message", JSON.stringify(jsonData),
|
||||
function(par) { msgComposer.text = ""; } )
|
||||
chatListView.currentIndex = count - 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
EmojiPicker {
|
||||
id: emojiPicker
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: parent.height / 2
|
||||
anchors.bottomMargin: categorySelectorHeight
|
||||
|
||||
property int categorySelectorHeight: 50
|
||||
|
||||
color: "white"
|
||||
buttonWidth: 40
|
||||
textArea: inferiorPanel.textMessageArea //the TextArea in which EmojiPicker is pasting the Emoji into
|
||||
|
||||
state: "EMOJI_HIDDEN"
|
||||
states: [
|
||||
State {
|
||||
name: "EMOJI_HIDDEN"
|
||||
PropertyChanges { target: emojiPicker; anchors.topMargin: parent.height }
|
||||
PropertyChanges { target: emojiPicker; anchors.bottomMargin: -1 }
|
||||
},
|
||||
State {
|
||||
name: "EMOJI_SHOWN"
|
||||
PropertyChanges { target: emojiPicker; anchors.topMargin: parent.height / 2 }
|
||||
PropertyChanges { target: emojiPicker; anchors.bottomMargin: categorySelectorHeight }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
|
||||
property var styles: StyleChat.inferiorPanel
|
||||
property alias textMessageArea: msgComposer.textMessageArea
|
||||
|
||||
id: inferiorPanel
|
||||
height: ( msgComposer.height > styles.height)? msgComposer.height: styles.height
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: backgroundRectangle
|
||||
anchors.fill: parent.fill
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color:inferiorPanel.styles.backgroundColor
|
||||
border.color: inferiorPanel.styles.borderColor
|
||||
}
|
||||
|
||||
ButtonIcon
|
||||
{
|
||||
|
||||
id: attachButton
|
||||
|
||||
property var styles: StyleChat.inferiorPanel.btnIcon
|
||||
|
||||
height: styles.height
|
||||
width: styles.width
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
anchors.margins: styles.margin
|
||||
|
||||
imgUrl: styles.attachIconUrl
|
||||
}
|
||||
|
||||
|
||||
RowLayout
|
||||
{
|
||||
id: msgComposer
|
||||
property var styles: StyleChat.inferiorPanel.msgComposer
|
||||
property alias textMessageArea: flickable.msgField
|
||||
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: attachButton.right
|
||||
|
||||
width: chatView.width -
|
||||
(sendButton.width + sendButton.anchors.margins) -
|
||||
(attachButton.width + attachButton.anchors.margins) -
|
||||
(emojiButton.width + emojiButton.anchors.margins)
|
||||
|
||||
height: (flickable.contentHeight < styles.maxHeight)? flickable.contentHeight : styles.maxHeight
|
||||
|
||||
Flickable
|
||||
{
|
||||
id: flickable
|
||||
property alias msgField: msgField
|
||||
|
||||
anchors.fill: parent
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
|
||||
width: parent.width
|
||||
|
||||
contentWidth: msgField.width
|
||||
contentHeight: msgField.height
|
||||
contentY: contentHeight - height
|
||||
|
||||
ScrollBar.vertical: ScrollBar {}
|
||||
|
||||
clip: true
|
||||
|
||||
TextArea
|
||||
{
|
||||
property var styles: StyleChat.inferiorPanel.msgComposer
|
||||
id: msgField
|
||||
|
||||
height: contentHeight + font.pixelSize
|
||||
|
||||
width: parent.width
|
||||
|
||||
placeholderText: styles.placeHolder
|
||||
background: styles.background
|
||||
|
||||
wrapMode: TextEdit.Wrap
|
||||
|
||||
focus: true
|
||||
|
||||
inputMethodHints: Qt.ImhMultiLine
|
||||
|
||||
font.pixelSize: styles.messageBoxTextSize
|
||||
|
||||
onTextChanged:
|
||||
{
|
||||
var msgLenght = (msgField.preeditText)? msgField.preeditText.length : msgField.length
|
||||
|
||||
if (msgLenght == 0)
|
||||
{
|
||||
sendButton.state = ""
|
||||
}
|
||||
else if (msgLenght > 0 )
|
||||
{
|
||||
sendButton.state = "SENDBTN"
|
||||
}
|
||||
}
|
||||
|
||||
property bool shiftPressed: false
|
||||
Keys.onPressed:
|
||||
{
|
||||
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
|
||||
&& !shiftPressed)
|
||||
{
|
||||
if (sendButton.state == "SENDBTN" )
|
||||
{
|
||||
chatView.sendMessage ()
|
||||
}
|
||||
}
|
||||
else if (event.key === Qt.Key_Shift)
|
||||
{
|
||||
shiftPressed = true
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onReleased:
|
||||
{
|
||||
if (event.key === Qt.Key_Shift)
|
||||
{
|
||||
shiftPressed = false
|
||||
}
|
||||
}
|
||||
function reset ()
|
||||
{
|
||||
msgField.text = ""
|
||||
Qt.inputMethod.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonIcon
|
||||
{
|
||||
|
||||
id: emojiButton
|
||||
|
||||
property var styles: StyleChat.inferiorPanel.btnIcon
|
||||
|
||||
height: styles.height
|
||||
width: styles.width
|
||||
|
||||
anchors.right: sendButton.left
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
anchors.margins: styles.margin
|
||||
|
||||
imgUrl: styles.emojiIconUrl
|
||||
|
||||
onClicked: {
|
||||
if (emojiPicker.state == "EMOJI_HIDDEN") {
|
||||
emojiPicker.state = "EMOJI_SHOWN"
|
||||
} else {
|
||||
emojiPicker.state = "EMOJI_HIDDEN"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ButtonIcon
|
||||
{
|
||||
|
||||
id: sendButton
|
||||
|
||||
property var styles: StyleChat.inferiorPanel.btnIcon
|
||||
property alias icon: sendButton.imgUrl
|
||||
|
||||
height: styles.height
|
||||
width: styles.width
|
||||
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
|
||||
anchors.margins: styles.margin
|
||||
|
||||
imgUrl: styles.microIconUrl
|
||||
|
||||
onClicked:
|
||||
{
|
||||
if (sendButton.state == "SENDBTN" )
|
||||
{
|
||||
chatView.sendMessage ()
|
||||
}
|
||||
}
|
||||
|
||||
onPressed:
|
||||
{
|
||||
if (sendButton.state == "RECORDING" )
|
||||
{
|
||||
sendButton.state = ""
|
||||
}
|
||||
else if (sendButton.state == "" )
|
||||
{
|
||||
sendButton.state = "RECORDING"
|
||||
}
|
||||
}
|
||||
|
||||
onReleased:
|
||||
{
|
||||
if (sendButton.state == "RECORDING" )
|
||||
{
|
||||
sendButton.state = ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
states:
|
||||
[
|
||||
State
|
||||
{
|
||||
name: ""
|
||||
PropertyChanges { target: sendButton; icon: styles.microIconUrl}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "RECORDING"
|
||||
PropertyChanges { target: sendButton; icon: styles.microMuteIconUrl}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "SENDBTN"
|
||||
PropertyChanges { target: sendButton; icon: styles.sendIconUrl}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
function sendMessage ()
|
||||
{
|
||||
if (emojiPicker.state == "EMOJI_SHOWN") emojiPicker.state = "EMOJI_HIDDEN"
|
||||
|
||||
msgField.text = getCompleteMessageText () + " " // Needed to prevent pre edit text problems
|
||||
var msgText = msgField.text
|
||||
|
||||
var jsonData = {"chat_id":chatView.chatId, "msg":msgText}
|
||||
rsApi.request( "/chat/send_message", JSON.stringify(jsonData),
|
||||
function(par)
|
||||
{
|
||||
msgField.reset();
|
||||
})
|
||||
}
|
||||
|
||||
// This function is needed for the compatibility with auto predictive keyboards
|
||||
function getCompleteMessageText (){
|
||||
var completeMsg
|
||||
if (msgField.preeditText) completeMsg = msgField.text + msgField.preeditText
|
||||
else completeMsg = msgField.text
|
||||
return completeMsg
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import "." //Needed for ClipboardWrapper singleton
|
||||
import "./components"
|
||||
import "URI.js" as UriJs
|
||||
|
||||
Item
|
||||
@ -26,6 +27,61 @@ Item
|
||||
id: cntDt
|
||||
property var md
|
||||
property bool is_contact: cntDt.md.is_contact
|
||||
property bool isOwn: cntDt.md.own
|
||||
property string objectName: "contactDetails"
|
||||
|
||||
ButtonText
|
||||
{
|
||||
id: avatarPicker
|
||||
|
||||
text: (isOwn)? qsTr("Change your Avatar") : qsTr("Start Chat!")
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
buttonTextPixelSize: 14
|
||||
iconUrl: (isOwn)? "/icons/attach-image.svg": "/icons/chat-bubble.svg"
|
||||
borderRadius: 0
|
||||
|
||||
|
||||
onClicked:
|
||||
{
|
||||
if (isOwn) fileChooser.open()
|
||||
else startDistantChat ()
|
||||
}
|
||||
function startDistantChat ()
|
||||
{
|
||||
ChatCache.chatHelper.startDistantChat(ChatCache.contactsCache.own.gxs_id,
|
||||
cntDt.md.gxs_id,
|
||||
cntDt.md.name,
|
||||
function (chatId)
|
||||
{
|
||||
stackView.push("qrc:/ChatView.qml", {'chatId': chatId})
|
||||
})
|
||||
}
|
||||
CustomFileChooser
|
||||
{
|
||||
id: fileChooser
|
||||
onResultFileChanged:
|
||||
{
|
||||
console.log("Result file changed! " , resultFile)
|
||||
|
||||
var base64Image = androidImagePicker.imageToBase64(resultFile)
|
||||
|
||||
rsApi.request("/identity/set_avatar", JSON.stringify({"gxs_id": cntDt.md.gxs_id, "avatar": base64Image }),
|
||||
function (par)
|
||||
{
|
||||
var jP = JSON.parse(par.response)
|
||||
if (jP.returncode === "ok")
|
||||
{
|
||||
console.log("Avatar changed! ")
|
||||
topFace.refresh()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AvatarOrColorHash
|
||||
{
|
||||
@ -33,7 +89,7 @@ Item
|
||||
|
||||
gxs_id: cntDt.md.gxs_id
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.top: avatarPicker.bottom
|
||||
anchors.topMargin: 6
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@ -68,10 +124,11 @@ Item
|
||||
Image
|
||||
{
|
||||
source: cntDt.is_contact ?
|
||||
"qrc:/icons/rating.png" :
|
||||
"qrc:/icons/rating-unrated.png"
|
||||
height: parent.height - 4
|
||||
"qrc:/icons/rating.svg" :
|
||||
"qrc:/icons/rating-unrated.svg"
|
||||
height: parent.height -4
|
||||
fillMode: Image.PreserveAspectFit
|
||||
sourceSize.height: height
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
MouseArea
|
||||
@ -112,9 +169,11 @@ Item
|
||||
|
||||
spacing: 6
|
||||
|
||||
Button
|
||||
ButtonText
|
||||
{
|
||||
text: qsTr("Contact full link")
|
||||
borderRadius: 0
|
||||
buttonTextPixelSize: 14
|
||||
onClicked:
|
||||
{
|
||||
rsApi.request(
|
||||
@ -140,10 +199,12 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
ButtonText
|
||||
{
|
||||
text: qsTr("Contact short link")
|
||||
enabled: false
|
||||
borderRadius: 0
|
||||
buttonTextPixelSize: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,20 @@ Item
|
||||
property bool searching: false
|
||||
onSearchingChanged: !searching && contactsSortWorker.sendMessage({})
|
||||
|
||||
Component.onCompleted: refreshAll()
|
||||
property string objectName:"contactsView"
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
refreshAll()
|
||||
}
|
||||
onFocusChanged: focus && refreshAll()
|
||||
|
||||
function changeState ()
|
||||
{
|
||||
toolBar.state = "CONTACTSVIEW"
|
||||
toolBar.searchBtnCb = toggleSearchBox
|
||||
}
|
||||
|
||||
WorkerScript
|
||||
{
|
||||
id: contactsSortWorker
|
||||
@ -40,6 +51,11 @@ Item
|
||||
onMessage: contactsListModel.json = JSON.stringify(messageObject)
|
||||
}
|
||||
|
||||
function toggleSearchBox (){
|
||||
if (searching) searching = false
|
||||
else searching = true
|
||||
}
|
||||
|
||||
function refreshAll()
|
||||
{
|
||||
refreshOwn()
|
||||
@ -51,6 +67,7 @@ Item
|
||||
{
|
||||
console.log("contactsView.refreshContactsCB()", visible)
|
||||
var token = JSON.parse(par.response).statetoken
|
||||
ChatCache.contactsCache.contactsList = JSON.parse(par.response).data
|
||||
TokensManager.registerToken(token, refreshContacts)
|
||||
contactsSortWorker.sendMessage(
|
||||
{'action': 'refreshContacts', 'response': par.response})
|
||||
@ -71,6 +88,7 @@ Item
|
||||
|
||||
if(json.data.length > 0)
|
||||
{
|
||||
ChatCache.contactsCache.own = json.data[0]
|
||||
contactsView.own_gxs_id = json.data[0].gxs_id
|
||||
contactsView.own_nick = json.data[0].name
|
||||
if(mainWindow.user_name.length === 0)
|
||||
@ -101,6 +119,11 @@ Item
|
||||
TokensManager.registerToken(json.statetoken, refreshUnread)
|
||||
contactsSortWorker.sendMessage(
|
||||
{'action': 'refreshUnread', 'response': par.response})
|
||||
json.data.forEach (function (chat)
|
||||
{
|
||||
ChatCache.lastMessageCache.updateLastMessageCache(chat.chat_id)
|
||||
ChatCache.lastMessageCache.setRemoteGXS (chat.chat_id, { gxs: chat.remote_author_id, name: chat.remote_author_name})
|
||||
})
|
||||
}
|
||||
function refreshUnread()
|
||||
{
|
||||
@ -109,13 +132,6 @@ Item
|
||||
rsApi.request("/chat/unread_msgs", "", refreshUnreadCallback)
|
||||
}
|
||||
|
||||
function startChatCallback(par)
|
||||
{
|
||||
var chId = JSON.parse(par.response).data.chat_id
|
||||
stackView.push("qrc:/ChatView.qml", {'chatId': chId})
|
||||
}
|
||||
|
||||
|
||||
/** This must be equivalent to
|
||||
p3GxsTunnelService::makeGxsTunnelId(...) */
|
||||
function getChatId(from_gxs, to_gxs)
|
||||
@ -143,31 +159,59 @@ Item
|
||||
Rectangle
|
||||
{
|
||||
id: searchBox
|
||||
visible: contactsView.searching
|
||||
// visible: contactsView.searching
|
||||
|
||||
height: searchText.height
|
||||
width: searchText.width
|
||||
height: searchText.height + 10
|
||||
width: parent.width * 0.9
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
|
||||
Image
|
||||
{
|
||||
id: searchIcon
|
||||
height: searchText.height - 4
|
||||
width: searchText.height - 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
source: "qrc:/icons/edit-find.png"
|
||||
}
|
||||
anchors.leftMargin: 5
|
||||
anchors.rightMargin: 5
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: "white"
|
||||
|
||||
TextField
|
||||
{
|
||||
id: searchText
|
||||
anchors.left: searchIcon.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
// placeholderText : "Search contacts..."
|
||||
width: parent.width
|
||||
anchors.leftMargin: 5
|
||||
height: 0
|
||||
|
||||
background: Rectangle
|
||||
{
|
||||
border.width: 2
|
||||
radius: 5
|
||||
border.color: searchText.focus ? "cornflowerblue" : "lightgrey"
|
||||
color: searchText.focus ? "white" : "ghostwhite"
|
||||
}
|
||||
|
||||
onTextChanged:
|
||||
contactsSortWorker.sendMessage(
|
||||
{'action': 'searchContact', 'sexp': text})
|
||||
}
|
||||
|
||||
states:
|
||||
[
|
||||
State
|
||||
{
|
||||
when: contactsView.searching;
|
||||
PropertyChanges { target: searchText; height: implicitHeight }
|
||||
PropertyChanges { target: searchText; placeholderText : "Search contacts..." }
|
||||
PropertyChanges { target: searchBox; height: searchText.height + 10 }
|
||||
},
|
||||
State
|
||||
{
|
||||
when: !contactsView.searching;
|
||||
PropertyChanges { target: searchText; height: 0 }
|
||||
PropertyChanges { target: searchBox; height: 0 }
|
||||
}
|
||||
]
|
||||
transitions: Transition
|
||||
{
|
||||
NumberAnimation { property: "height"; duration: 500; easing.type: Easing.InOutQuad}
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
@ -187,4 +231,5 @@ Item
|
||||
|
||||
property bool defaultIdentityCreated: false
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,101 +18,252 @@
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import "." //Needed for ChatCache singleton
|
||||
import "./components"
|
||||
|
||||
Item
|
||||
{
|
||||
id: delegateRoot
|
||||
height: 40
|
||||
height: 57
|
||||
width: parent.width
|
||||
|
||||
MouseArea
|
||||
|
||||
property var chatId: undefined
|
||||
property var lastMessageData: ({})
|
||||
property var locale: Qt.locale()
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: contactItem
|
||||
|
||||
anchors.fill: parent
|
||||
onClicked:
|
||||
color: contactItemArea.containsPress ? "lightgrey" : "transparent"
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
MouseArea
|
||||
{
|
||||
console.log("GxsIntentityDelegate onclicked:", model.name,
|
||||
model.gxs_id)
|
||||
contactsView.searching = false
|
||||
if(model.own) contactsView.own_gxs_id = model.gxs_id
|
||||
else
|
||||
id: contactItemArea
|
||||
anchors.fill: parent
|
||||
onClicked:
|
||||
{
|
||||
var jsonData = { "own_gxs_hex": contactsView.own_gxs_id,
|
||||
"remote_gxs_hex": model.gxs_id }
|
||||
rsApi.request("/chat/initiate_distant_chat",
|
||||
JSON.stringify(jsonData),
|
||||
contactsView.startChatCallback)
|
||||
}
|
||||
}
|
||||
|
||||
onPressAndHold: showDetails()
|
||||
|
||||
ColorHash
|
||||
{
|
||||
id: colorHash
|
||||
|
||||
hash: model.gxs_id
|
||||
height: parent.height - 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 2
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked: delegateRoot.showDetails()
|
||||
}
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: nickText
|
||||
color: model.own ? "blue" : "black"
|
||||
text: model.name
|
||||
anchors.left: colorHash.right
|
||||
anchors.leftMargin: 5
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: parent.height - 10
|
||||
spacing: 4
|
||||
|
||||
Rectangle
|
||||
{
|
||||
visible: model.unread_count > 0
|
||||
|
||||
color: "cornflowerblue"
|
||||
antialiasing: true
|
||||
border.color: "blue"
|
||||
border.width: 1
|
||||
height: parent.height - 4
|
||||
radius: height/2
|
||||
width: height
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text
|
||||
console.log("GxsIntentityDelegate onclicked:", model.name,
|
||||
model.gxs_id)
|
||||
contactsView.searching = false
|
||||
if(model.own) contactsView.own_gxs_id = model.gxs_id
|
||||
else
|
||||
{
|
||||
color: "white"
|
||||
font.bold: true
|
||||
text: model.unread_count
|
||||
anchors.centerIn: parent
|
||||
startDistantChat()
|
||||
}
|
||||
}
|
||||
|
||||
Image
|
||||
onPressAndHold: showDetails()
|
||||
hoverEnabled: true
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: backgroundRectangle
|
||||
anchors.fill: parent.fill
|
||||
anchors.right: parent.right
|
||||
width: parent.width - colorHash.width - 15
|
||||
height: parent.height
|
||||
color:"transparent"
|
||||
|
||||
Rectangle
|
||||
{
|
||||
source: model.is_contact ?
|
||||
"qrc:/icons/rating.png" :
|
||||
"qrc:/icons/rating-unrated.png"
|
||||
height: parent.height - 4
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
id: borderBottom
|
||||
width: parent.width
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
color: "lightgrey"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
anchors.margins: 5
|
||||
|
||||
AvatarOrColorHash
|
||||
{
|
||||
id: colorHash
|
||||
|
||||
gxs_id: model.gxs_id
|
||||
|
||||
height: parent.height - 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 2
|
||||
|
||||
onlyCached: true
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
|
||||
id: chatInfoRow
|
||||
height: parent.height
|
||||
width: parent.width - isContactRow.width - colorHash.width
|
||||
anchors.left: colorHash.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.right: isContactRow.left
|
||||
anchors.rightMargin: 5
|
||||
|
||||
|
||||
Item
|
||||
{
|
||||
width: parent.width
|
||||
height: parent.height /2
|
||||
|
||||
Text
|
||||
{
|
||||
id: nickText
|
||||
color: model.own ? "blue" : "black"
|
||||
text: model.name
|
||||
font.bold: true
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
text: setTime()
|
||||
anchors.right: parent.right
|
||||
color: "darkslategrey"
|
||||
anchors.bottom: parent.bottom
|
||||
}
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: lastMessageText
|
||||
width: parent.width
|
||||
height: (lastMessageData && lastMessageData.msg !== undefined)? parent.height /2 : 0
|
||||
|
||||
Text
|
||||
{
|
||||
id: lastMessageSender
|
||||
font.italic: true
|
||||
color: "royalblue"
|
||||
text: ((lastMessageData && lastMessageData.incoming !== undefined) && !lastMessageData.incoming)? "You: " : ""
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: lastMessageMsg
|
||||
anchors.left: lastMessageSender.right
|
||||
text: (lastMessageData && lastMessageData.msg !== undefined)? lastMessageData.msg : ""
|
||||
rightPadding: 5
|
||||
elide: Text.ElideRight
|
||||
color: "darkslategrey"
|
||||
width: chatInfoRow.width - 30
|
||||
height: parent.height
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
visible: model.unread_count > 0
|
||||
|
||||
color: "cornflowerblue"
|
||||
antialiasing: true
|
||||
height: parent.height - 6
|
||||
radius: height/2
|
||||
width: height
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
|
||||
Text
|
||||
{
|
||||
color: "white"
|
||||
font.bold: true
|
||||
text: model.unread_count
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Row
|
||||
{
|
||||
id: isContactRow
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 10
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: parent.height - 10
|
||||
spacing: 4
|
||||
|
||||
Image
|
||||
{
|
||||
source: model.is_contact ?
|
||||
"qrc:/icons/rating.svg" :
|
||||
"qrc:/icons/rating-unrated.svg"
|
||||
height: parent.height - 4
|
||||
sourceSize.height: height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
id: isContactIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
if (!chatId){
|
||||
chatId = getChatIdFromGXS()
|
||||
}
|
||||
if (chatId) {
|
||||
var last = getChatLastMessage(chatId)
|
||||
if (last) lastMessageData = last
|
||||
}
|
||||
}
|
||||
|
||||
Connections
|
||||
{
|
||||
target: ChatCache.lastMessageCache
|
||||
onLastMessageChanged: {
|
||||
if (!chatId) {
|
||||
chatId = getChatIdFromGXS()
|
||||
}
|
||||
if (chatId && chatId === chatI){
|
||||
console.log("New last message received!")
|
||||
lastMessageData = newLastMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getChatLastMessage (chatId)
|
||||
{
|
||||
return ChatCache.lastMessageCache.getChatLastMessage(chatId)
|
||||
}
|
||||
|
||||
function getChatIdFromGXS ()
|
||||
{
|
||||
var id= ChatCache.lastMessageCache.getChatIdFromGxs(model.gxs_id)
|
||||
return ChatCache.lastMessageCache.getChatIdFromGxs(model.gxs_id)
|
||||
}
|
||||
function setTime()
|
||||
{
|
||||
if (!lastMessageData || lastMessageData.recv_time === undefined) return ""
|
||||
|
||||
var timeFormat = "dd.MM.yyyy";
|
||||
var recvDate = new Date(lastMessageData.recv_time*1000)
|
||||
|
||||
// Check if is today
|
||||
if ( new Date (lastMessageData.recv_time*1000).setHours(0,0,0,0) == new Date ().setHours(0,0,0,0))
|
||||
{
|
||||
timeFormat = "hh:mm"
|
||||
}
|
||||
var timeString = Qt.formatDateTime(recvDate, timeFormat)
|
||||
return timeString
|
||||
}
|
||||
|
||||
function showDetails()
|
||||
@ -123,4 +274,14 @@ Item
|
||||
"qrc:/ContactDetails.qml",
|
||||
{md: contactsListView.model.get(index)})
|
||||
}
|
||||
|
||||
function startDistantChat ()
|
||||
{
|
||||
ChatCache.chatHelper.startDistantChat(contactsView.own_gxs_id, model.gxs_id, model.name,
|
||||
function (chatId)
|
||||
{
|
||||
stackView.push("qrc:/ChatView.qml", {'chatId': chatId})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@ import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import org.retroshare.qml_components.LibresapiLocalClient 1.0
|
||||
|
||||
import "components/."
|
||||
|
||||
Item
|
||||
{
|
||||
id: locationView
|
||||
@ -47,7 +49,7 @@ Item
|
||||
target: loginView
|
||||
visible: true
|
||||
buttonText: qsTr("Save")
|
||||
iconUrl: "qrc:/icons/edit-image-face-detect.png"
|
||||
iconUrl: "qrc:/icons/edit-image-face-detect.svg"
|
||||
suggestionText: qsTr("Create your profile")
|
||||
onSubmit:
|
||||
{
|
||||
@ -62,6 +64,11 @@ Item
|
||||
bottomButton.enabled = false
|
||||
bottomButton.text = "Creating profile..."
|
||||
}
|
||||
onCancel:
|
||||
{
|
||||
locationView.state = "selectLocation"
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
State
|
||||
@ -83,6 +90,10 @@ Item
|
||||
locationView.attemptLogin = true
|
||||
attemptTimer.start()
|
||||
}
|
||||
onCancel:
|
||||
{
|
||||
locationView.state = "selectLocation"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -127,28 +138,69 @@ Item
|
||||
width: parent.width
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: bottomButton.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
model: locationsModel.model
|
||||
delegate: Button
|
||||
spacing: 3
|
||||
delegate: Item
|
||||
{
|
||||
text: model.name
|
||||
onClicked:
|
||||
id: delegate
|
||||
width: parent.width
|
||||
height: 60
|
||||
Rectangle
|
||||
{
|
||||
loginView.login = text
|
||||
locationView.sslid = model.id
|
||||
locationView.state = "login"
|
||||
mainWindow.user_name = model.name
|
||||
id: backgroundRectangle
|
||||
anchors.fill: parent.fill
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width /2
|
||||
height: parent.height
|
||||
|
||||
color:"transparent"
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: borderBottom
|
||||
width: parent.width
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
color: "lightgrey"
|
||||
}
|
||||
}
|
||||
|
||||
ButtonText
|
||||
{
|
||||
id: locationButton
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: model.name
|
||||
borderRadius:0
|
||||
iconUrl: "/icons/edit-image-face-detect.svg"
|
||||
color: "white"
|
||||
pressColor: "lightsteelblue"
|
||||
buttonTextPixelSize: 20
|
||||
|
||||
onClicked:
|
||||
{
|
||||
loginView.login = text
|
||||
locationView.sslid = model.id
|
||||
locationView.state = "login"
|
||||
mainWindow.user_name = model.name
|
||||
}
|
||||
}
|
||||
}
|
||||
visible: false
|
||||
}
|
||||
|
||||
Button
|
||||
ButtonText
|
||||
{
|
||||
id: bottomButton
|
||||
text: "Create new location"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: locationView.state = "createLocation"
|
||||
buttonTextPixelSize: 15
|
||||
iconUrl: "/icons/add.svg"
|
||||
borderRadius: 0
|
||||
}
|
||||
|
||||
RsLoginPassView
|
||||
|
@ -19,12 +19,14 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.0
|
||||
import "components/."
|
||||
|
||||
Item
|
||||
{
|
||||
id: loginView
|
||||
property string buttonText: qsTr("Unlock")
|
||||
property string iconUrl: "qrc:/icons/emblem-locked.png"
|
||||
property string cancelText: qsTr("Cancel")
|
||||
property string iconUrl: "qrc:/icons/emblem-locked.svg"
|
||||
property string login
|
||||
property bool loginPreset: false
|
||||
property bool advancedMode: false
|
||||
@ -32,6 +34,7 @@ Item
|
||||
property string password: advancedMode ? "" : hardcodedPassword
|
||||
property string suggestionText
|
||||
signal submit(string login, string password)
|
||||
signal cancel()
|
||||
|
||||
Component.onCompleted: loginPreset = login.length > 0
|
||||
|
||||
@ -53,6 +56,8 @@ Item
|
||||
{
|
||||
source: loginView.iconUrl
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
height: 128
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
Text
|
||||
@ -116,18 +121,32 @@ Item
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom
|
||||
spacing: 3
|
||||
|
||||
Button
|
||||
ButtonIcon
|
||||
{
|
||||
text: qsTr("Advanced...")
|
||||
visible: !loginView.loginPreset
|
||||
onClicked: loginView.advancedMode = !loginView.advancedMode
|
||||
imgUrl: "/icons/options.svg"
|
||||
height: bottomButton.height - 5
|
||||
width: height
|
||||
anchors.verticalCenter: bottomButton.verticalCenter
|
||||
}
|
||||
|
||||
Button
|
||||
ButtonText
|
||||
{
|
||||
id: bottomButton
|
||||
text: loginView.buttonText
|
||||
onClicked: loginView.submit(nameField.text, passwordField.text)
|
||||
iconUrl: "/icons/network.svg"
|
||||
buttonTextPixelSize: 15
|
||||
}
|
||||
ButtonIcon
|
||||
{
|
||||
id: cancelButton
|
||||
onClicked: loginView.cancel()
|
||||
height: bottomButton.height - 5
|
||||
width: height
|
||||
imgUrl: "/icons/back.png"
|
||||
anchors.verticalCenter: bottomButton.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import "components/."
|
||||
|
||||
Item
|
||||
{
|
||||
@ -25,19 +26,55 @@ Item
|
||||
|
||||
property string pgpName
|
||||
property alias pgpId: pgpIdTxt.text
|
||||
property bool isOnline
|
||||
|
||||
property string nodeCert
|
||||
|
||||
property var locations
|
||||
|
||||
function attemptConnectionCB (par)
|
||||
{
|
||||
console.log("attemptConnectionCB()", par.response)
|
||||
}
|
||||
|
||||
|
||||
Image
|
||||
{
|
||||
id: nodeStatusImage
|
||||
source: isOnline?
|
||||
"icons/state-ok.svg" :
|
||||
"icons/state-offline.svg"
|
||||
|
||||
height: 128
|
||||
sourceSize.height: height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 6
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
|
||||
|
||||
Column
|
||||
{
|
||||
id: pgpColumn
|
||||
|
||||
anchors.top: parent.top
|
||||
anchors.top: nodeStatusImage.bottom
|
||||
width: parent.width
|
||||
|
||||
Text { text: nodeDetailsRoot.pgpName.replace(" (Generated by RetroShare) <>", "") }
|
||||
Text { id: pgpIdTxt }
|
||||
Text
|
||||
{
|
||||
text: nodeDetailsRoot.pgpName.replace(" (Generated by RetroShare) <>", "")
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: 20
|
||||
}
|
||||
Text
|
||||
{
|
||||
id: pgpIdTxt
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: "darkslategrey"
|
||||
}
|
||||
}
|
||||
|
||||
JSONListModel
|
||||
@ -48,42 +85,96 @@ Item
|
||||
|
||||
ListView
|
||||
{
|
||||
width: parent.width
|
||||
width: parent.width * .75
|
||||
anchors.top: pgpColumn.bottom
|
||||
anchors.topMargin: 5
|
||||
anchors.bottom: buttonsRow.top
|
||||
model: jsonModel.model
|
||||
delegate: Column
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
|
||||
clip: true
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
spacing:7
|
||||
|
||||
header:Rectangle
|
||||
{
|
||||
height: 60
|
||||
color: "aliceblue"
|
||||
width: parent.width
|
||||
leftPadding: 4
|
||||
spacing: 4
|
||||
z: 2
|
||||
height: headetText.contentHeight + 10
|
||||
radius: 10
|
||||
|
||||
Row
|
||||
Text
|
||||
{
|
||||
height: 30
|
||||
spacing: 10
|
||||
|
||||
Image
|
||||
{
|
||||
id: statusImage
|
||||
source: model.is_online ?
|
||||
"icons/state-ok.png" :
|
||||
"icons/state-offline.png"
|
||||
|
||||
height: parent.height - 4
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text
|
||||
{
|
||||
id: locNameText
|
||||
text: model.location
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
id: headetText
|
||||
text: "Node locations ("+jsonModel.model.count+")"
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
font.italic: true
|
||||
}
|
||||
|
||||
Text { text: model.peer_id }
|
||||
}
|
||||
|
||||
delegate:
|
||||
MouseArea
|
||||
{
|
||||
width: parent.width
|
||||
height: innerCol.height
|
||||
|
||||
onClicked:
|
||||
{
|
||||
console.log("triggerLocationConnectionAttempt()")
|
||||
rsApi.request("/peers/attempt_connection/",
|
||||
JSON.stringify({peer_id: model.peer_id}) , attemptConnectionCB)
|
||||
|
||||
}
|
||||
|
||||
Column
|
||||
{
|
||||
id: innerCol
|
||||
height: idRow.height + gxsInfo.height
|
||||
width: parent.width
|
||||
leftPadding: 4
|
||||
spacing: 6
|
||||
|
||||
Row
|
||||
{
|
||||
id: idRow
|
||||
height: 30
|
||||
spacing: 4
|
||||
|
||||
Image
|
||||
{
|
||||
id: statusImage
|
||||
source: model.is_online ?
|
||||
"icons/network-connect.svg" :
|
||||
"icons/network-disconnect.svg"
|
||||
|
||||
height: parent.height - 4
|
||||
sourceSize.height: height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text
|
||||
{
|
||||
id: locNameText
|
||||
text: model.location
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
TextAndIcon
|
||||
{
|
||||
id: gxsInfo
|
||||
width: parent.width
|
||||
innerText: model.peer_id
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
iconUrl: "/icons/keyring.svg"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,9 +186,14 @@ Item
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 6
|
||||
|
||||
Button
|
||||
ButtonText
|
||||
{
|
||||
text: qsTr("Revoke")
|
||||
|
||||
borderRadius: 0
|
||||
buttonTextPixelSize: 14
|
||||
iconUrl: "/icons/leave.svg"
|
||||
|
||||
onClicked:
|
||||
rsApi.request(
|
||||
"/peers/"+nodeDetailsRoot.pgpId+"/delete", "",
|
||||
@ -105,11 +201,16 @@ Item
|
||||
{ stackView.push("qrc:/TrustedNodesView.qml") })
|
||||
}
|
||||
|
||||
Button
|
||||
ButtonText
|
||||
{
|
||||
text: qsTr("Entrust")
|
||||
|
||||
visible: nodeDetailsRoot.nodeCert.length > 0
|
||||
|
||||
borderRadius: 0
|
||||
buttonTextPixelSize: 14
|
||||
iconUrl: "/icons/invite.svg"
|
||||
|
||||
onClicked:
|
||||
{
|
||||
var jsonData =
|
||||
|
@ -20,6 +20,7 @@ import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import "jsonpath.js" as JSONPath
|
||||
import "." //Needed for TokensManager singleton
|
||||
import "components/."
|
||||
|
||||
Item
|
||||
{
|
||||
@ -65,34 +66,29 @@ Item
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: bottomButton.top
|
||||
model: jsonModel.model
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 3
|
||||
delegate: Item
|
||||
{
|
||||
height: 30
|
||||
property bool isOnline: jsonModel.isOnline(model.pgp_id)
|
||||
height: 54
|
||||
width: parent.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Image
|
||||
{
|
||||
id: statusImage
|
||||
source: jsonModel.isOnline(model.pgp_id) ?
|
||||
"icons/state-ok.png" :
|
||||
"icons/state-offline.png"
|
||||
|
||||
height: parent.height - 4
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 3
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text
|
||||
ButtonText
|
||||
{
|
||||
id: locationButton
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: model.name
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: statusImage.right
|
||||
anchors.leftMargin: 10
|
||||
}
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
borderRadius:0
|
||||
iconUrl: isOnline?
|
||||
"/icons/state-ok.svg" :
|
||||
"/icons/state-offline.svg"
|
||||
color: "transparent"
|
||||
pressColor: "lightsteelblue"
|
||||
buttonTextPixelSize: 18
|
||||
iconHeight:parent.height - 4
|
||||
|
||||
onClicked:
|
||||
{
|
||||
stackView.push(
|
||||
@ -100,6 +96,7 @@ Item
|
||||
{
|
||||
pgpName: model.name,
|
||||
pgpId: model.pgp_id,
|
||||
isOnline: isOnline,
|
||||
locations: jsonModel.getLocations(
|
||||
model.pgp_id)
|
||||
}
|
||||
@ -109,12 +106,17 @@ Item
|
||||
}
|
||||
}
|
||||
|
||||
Button
|
||||
ButtonText
|
||||
{
|
||||
id: bottomButton
|
||||
text: qsTr("Add Trusted Node")
|
||||
text: qsTr("Add/Share Trusted Node")
|
||||
anchors.bottom: parent.bottom
|
||||
onClicked: stackView.push("qrc:/AddTrustedNode.qml")
|
||||
width: parent.width
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
// width: parent.width
|
||||
|
||||
borderRadius: 0
|
||||
buttonTextPixelSize: 14
|
||||
iconUrl: "/icons/add.svg"
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
android:label="RetroShare"
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation"
|
||||
android:screenOrientation="unspecified"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
@ -206,4 +207,8 @@
|
||||
|
||||
<!-- Added by G10h4ck: Needed permission for autostart at boot -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<!-- Added by Angesoc: used to pick images from gallery or take it from camera -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
|
||||
</manifest>
|
||||
|
@ -19,17 +19,25 @@
|
||||
package org.retroshare.android.qml_app;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
|
||||
import org.retroshare.android.qml_app.jni.NativeCalls;
|
||||
|
||||
public class RetroShareQmlActivity extends QtActivity
|
||||
{
|
||||
|
||||
static final int PICK_PHOTO = 1;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
@ -57,12 +65,16 @@ public class RetroShareQmlActivity extends QtActivity
|
||||
@Override
|
||||
public void onNewIntent(Intent intent)
|
||||
{
|
||||
Log.i("RetroShareQmlActivity", "onNewIntent(Intent intent)");
|
||||
Log.i("RetroShareQmlActivity", "on NewIntent(Intent intent)");
|
||||
|
||||
super.onNewIntent(intent);
|
||||
|
||||
String uri = intent.getDataString();
|
||||
if (uri != null) NativeCalls.notifyIntentUri(uri);
|
||||
if (uri != null)
|
||||
{
|
||||
NativeCalls.notifyIntentUri(uri);
|
||||
Log.i("RetroShareQmlActivity", "onNewIntent(Intent intent) Uri: " + uri);
|
||||
}
|
||||
}
|
||||
|
||||
@UsedByNativeCode @SuppressWarnings("unused")
|
||||
@ -84,4 +96,88 @@ public class RetroShareQmlActivity extends QtActivity
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Uri capturedImageURI;
|
||||
|
||||
public void openImagePicker()
|
||||
{
|
||||
Log.i("RetroShareQmlActivity", "openImagePicker()");
|
||||
|
||||
Intent pickIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
|
||||
pickIntent.setType("image/*");
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(MediaStore.Images.Media.TITLE, "Retroshare Avatar");
|
||||
capturedImageURI = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
takePicture.putExtra(MediaStore.EXTRA_OUTPUT, capturedImageURI);
|
||||
|
||||
Intent chooserIntent = Intent.createChooser(pickIntent, "Select Image");
|
||||
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {takePicture});
|
||||
|
||||
startActivityForResult( chooserIntent, PICK_PHOTO);
|
||||
};
|
||||
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||
{
|
||||
Log.i("RetroShareQmlActivity", "onActivityResult()" + String.valueOf(requestCode));
|
||||
|
||||
if (resultCode == RESULT_OK)
|
||||
{
|
||||
if (requestCode == PICK_PHOTO)
|
||||
{
|
||||
final boolean isCamera;
|
||||
|
||||
if (data == null)
|
||||
{
|
||||
isCamera = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
final String action = data.getAction();
|
||||
if (action == null)
|
||||
{
|
||||
isCamera = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
isCamera = action.equals(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
}
|
||||
}
|
||||
|
||||
Uri selectedImageUri;
|
||||
if (isCamera)
|
||||
{
|
||||
selectedImageUri = capturedImageURI;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedImageUri = data == null ? null : data.getData();
|
||||
}
|
||||
|
||||
String uri = getRealPathFromURI(selectedImageUri);
|
||||
if (uri != null)
|
||||
{
|
||||
Log.i("RetroShareQmlActivity", "Image path from uri found!" + uri);
|
||||
NativeCalls.notifyIntentUri("//file"+uri); // Add the authority for get it on qml code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getRealPathFromURI(Uri uri) {
|
||||
String[] projection = { MediaStore.Images.Media.DATA };
|
||||
@SuppressWarnings("deprecation")
|
||||
Cursor cursor = managedQuery(uri, projection, null, null, null);
|
||||
int column_index = cursor
|
||||
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
cursor.moveToFirst();
|
||||
String result = cursor.getString(column_index);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
66
retroshare-qml-app/src/androidimagepicker.h
Normal file
@ -0,0 +1,66 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
|
||||
#include <QFile>
|
||||
#include <QUrl>
|
||||
#include <QImage>
|
||||
#include <QImageReader>
|
||||
#include <QBuffer>
|
||||
|
||||
|
||||
#ifdef __ANDROID__
|
||||
# include <QtAndroid>
|
||||
# include <QtAndroidExtras/QAndroidJniObject>
|
||||
#endif // __ANDROID__
|
||||
|
||||
struct AndroidImagePicker : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
|
||||
static void openPicker()
|
||||
{
|
||||
qDebug() << "Starting image picker intent";
|
||||
|
||||
#ifdef __ANDROID__
|
||||
QtAndroid::androidActivity().callMethod<void>(
|
||||
"openImagePicker",
|
||||
"()V" );
|
||||
#endif // __ANDROID__
|
||||
|
||||
}
|
||||
|
||||
// Used to convert a given image path into a png base64 string
|
||||
static QString imageToBase64 (QString const& path)
|
||||
{
|
||||
// Get local path from uri
|
||||
QUrl url (path);
|
||||
QString localPath = url.toLocalFile();
|
||||
|
||||
qDebug() << "imageToBase64() local path:" << localPath ;
|
||||
|
||||
// Read the image
|
||||
QImageReader reader;
|
||||
reader.setFileName(localPath);
|
||||
QImage image = reader.read();
|
||||
|
||||
image = image.scaled(96,96,Qt::KeepAspectRatio,Qt::SmoothTransformation);
|
||||
|
||||
// Transform image into PNG format
|
||||
QByteArray ba;
|
||||
QBuffer buffer( &ba );
|
||||
buffer.open( QIODevice::WriteOnly );
|
||||
image.save( &buffer, "png" );
|
||||
|
||||
// Get Based 64 image string
|
||||
QString encoded = QString(ba.toBase64());
|
||||
|
||||
qDebug() << "imageToBase64() encoded" ;
|
||||
|
||||
return encoded;
|
||||
|
||||
}
|
||||
};
|
159
retroshare-qml-app/src/components/AvatarOrColorHash.qml
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* RetroShare Android QML App
|
||||
* Copyright (C) 2017 Gioacchino Mazzurco <gio@eigenlab.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick 2.7
|
||||
import "../" //Needed for Chat Cache
|
||||
|
||||
|
||||
Item
|
||||
{
|
||||
id: compRoot
|
||||
|
||||
property string gxs_id
|
||||
property bool onlyCached: false
|
||||
signal clicked ()
|
||||
|
||||
height: 130
|
||||
width: height
|
||||
|
||||
|
||||
////////////// The following should be considered privates /////////////////////
|
||||
|
||||
property bool has_avatar: false
|
||||
property bool default_image: false
|
||||
property int avatarAttemptCnt: 0
|
||||
property string noGxsImage: "/icons/retroshare.png"
|
||||
|
||||
function getDetails()
|
||||
{
|
||||
console.log("getDetails() ", compRoot.gxs_id )
|
||||
++compRoot.avatarAttemptCnt
|
||||
if (gxs_id)
|
||||
{
|
||||
default_image = false
|
||||
var hasAvatarCached = hasAvatar (ChatCache.contactsCache.getIdentityAvatar(gxs_id))
|
||||
if ( !hasAvatarCached )
|
||||
{
|
||||
rsApi.request(
|
||||
"/identity/get_identity_details",
|
||||
JSON.stringify({ gxs_id: compRoot.gxs_id }),
|
||||
function(par)
|
||||
{
|
||||
var jData = JSON.parse(par.response).data
|
||||
saveDetails(jData)
|
||||
if(!compRoot.has_avatar &&
|
||||
compRoot.avatarAttemptCnt < 3) getDetails()
|
||||
})
|
||||
}
|
||||
else
|
||||
{
|
||||
setImage(ChatCache.contactsCache.getIdentityDetails(gxs_id))
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
has_avatar = true
|
||||
default_image = true
|
||||
contactAvatar.source = noGxsImage
|
||||
}
|
||||
}
|
||||
function saveDetails(data)
|
||||
{
|
||||
ChatCache.contactsCache.setIdentityDetails(data)
|
||||
setImage(data)
|
||||
|
||||
}
|
||||
function setImage (data)
|
||||
{
|
||||
compRoot.has_avatar = hasAvatar (data.avatar)
|
||||
if(compRoot.has_avatar)
|
||||
{
|
||||
contactAvatar.source =
|
||||
"data:image/png;base64," + data.avatar
|
||||
}
|
||||
}
|
||||
|
||||
function hasAvatar (avatar)
|
||||
{
|
||||
return avatar.length > 0
|
||||
}
|
||||
|
||||
function showDetails()
|
||||
{
|
||||
console.log("showDetails() ", gxs_id)
|
||||
|
||||
if (stackView.currentItem.objectName != "contactDetails")
|
||||
{
|
||||
stackView.push(
|
||||
"qrc:/ContactDetails.qml",
|
||||
{md: ChatCache.contactsCache.getContactFromGxsId(gxs_id)})
|
||||
|
||||
}
|
||||
}
|
||||
function refresh()
|
||||
{
|
||||
ChatCache.contactsCache.delIdentityAvatar(gxs_id)
|
||||
compRoot.avatarAttemptCnt = 0
|
||||
getDetails()
|
||||
|
||||
}
|
||||
|
||||
Component.onCompleted: startComponent ()
|
||||
|
||||
onVisibleChanged: startComponent ()
|
||||
|
||||
function startComponent ()
|
||||
{
|
||||
if (onlyCached && hasAvatar (ChatCache.contactsCache.getIdentityAvatar(gxs_id) ) )
|
||||
{
|
||||
console.log("load cached avatar")
|
||||
setImage(ChatCache.contactsCache.getIdentityDetails(gxs_id))
|
||||
|
||||
}
|
||||
else if (!onlyCached)
|
||||
{
|
||||
if(visible && (!has_avatar || default_image ) ) getDetails()
|
||||
}
|
||||
}
|
||||
|
||||
Image
|
||||
{
|
||||
id: contactAvatar
|
||||
anchors.fill: parent
|
||||
visible: compRoot.has_avatar
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
Faces
|
||||
{
|
||||
visible: !compRoot.has_avatar
|
||||
hash: compRoot.gxs_id
|
||||
anchors.fill: parent
|
||||
iconSize: parent.height
|
||||
}
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked:
|
||||
{
|
||||
compRoot.clicked()
|
||||
showDetails()
|
||||
}
|
||||
}
|
||||
}
|
28
retroshare-qml-app/src/components/ButtonIcon.qml
Normal file
@ -0,0 +1,28 @@
|
||||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
|
||||
id: root
|
||||
signal clicked
|
||||
signal pressed
|
||||
signal released
|
||||
|
||||
property var imgUrl: ""
|
||||
property alias fillMode: image.fillMode
|
||||
|
||||
Image {
|
||||
id: image
|
||||
anchors.fill: parent
|
||||
source: imgUrl
|
||||
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
onClicked: { root.clicked() }
|
||||
onPressed: { root.pressed() }
|
||||
onReleased: { root.released() }
|
||||
}
|
||||
}
|
130
retroshare-qml-app/src/components/ButtonText.qml
Normal file
@ -0,0 +1,130 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
Item
|
||||
{
|
||||
id: button
|
||||
property alias text: innerText.text;
|
||||
property alias buttonTextPixelSize: innerText.font.pixelSize
|
||||
property alias innerAnchors: innerElements.anchors;
|
||||
property alias rectangleButton: rectangleButton;
|
||||
|
||||
property var iconUrl
|
||||
property int iconHeight: 20
|
||||
|
||||
property color color: "lightsteelblue"
|
||||
property color hoverColor: color
|
||||
property color pressColor: color
|
||||
property int fontSize
|
||||
property int borderWidth
|
||||
property int borderRadius: 3
|
||||
property int innerMargin: 10
|
||||
|
||||
height: innerText.height + innerMargin + innerMargin
|
||||
width: innerText.width + innerMargin + innerMargin + icon.width + icon.width
|
||||
|
||||
scale: state === "Pressed" ? 0.96 : 1.0
|
||||
onEnabledChanged: state = ""
|
||||
signal clicked
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: rectangleButton
|
||||
anchors.fill: parent
|
||||
radius: borderRadius
|
||||
color: button.enabled ? button.color : "lavender"
|
||||
border.width: borderWidth
|
||||
border.color: "black"
|
||||
|
||||
ToolButton {
|
||||
id: innerElements
|
||||
|
||||
Image
|
||||
{
|
||||
id: icon
|
||||
source: (iconUrl)? iconUrl: ""
|
||||
height: (iconUrl)? iconHeight: 0
|
||||
width: (iconUrl)? iconHeight: 0
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
visible: (iconUrl)? true: false
|
||||
anchors.left: innerElements.left
|
||||
|
||||
anchors.margins:(iconUrl)? innerMargin : 0
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
sourceSize.height: height
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
anchors.margins: innerMargin
|
||||
id: innerText
|
||||
font.pointSize: fontSize
|
||||
anchors.left: icon.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: button.enabled ? "black" : "grey"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State
|
||||
{
|
||||
name: "Hovering"
|
||||
PropertyChanges
|
||||
{
|
||||
target: rectangleButton
|
||||
color: hoverColor
|
||||
}
|
||||
},
|
||||
State
|
||||
{
|
||||
name: "Pressed"
|
||||
PropertyChanges
|
||||
{
|
||||
target: rectangleButton
|
||||
color: pressColor
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: [
|
||||
Transition
|
||||
{
|
||||
from: ""; to: "Hovering"
|
||||
ColorAnimation { duration: 200 }
|
||||
},
|
||||
Transition
|
||||
{
|
||||
from: "*"; to: "Pressed"
|
||||
ColorAnimation { duration: 10 }
|
||||
}
|
||||
]
|
||||
|
||||
MouseArea
|
||||
{
|
||||
hoverEnabled: true
|
||||
anchors.fill: button
|
||||
onEntered: { button.state='Hovering'}
|
||||
onExited: { button.state=''}
|
||||
onClicked: { button.clicked();}
|
||||
onPressed: { button.state="Pressed" }
|
||||
onReleased:
|
||||
{
|
||||
if (containsMouse)
|
||||
button.state="Hovering";
|
||||
else
|
||||
button.state="";
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on scale
|
||||
{
|
||||
NumberAnimation
|
||||
{
|
||||
duration: 100
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
@ -28,8 +28,13 @@ Rectangle
|
||||
|
||||
Image
|
||||
{
|
||||
source: "qrc:/icons/edit-image-face-detect.png"
|
||||
anchors.fill: parent
|
||||
source: "qrc:/icons/edit-image-face-detect.svg"
|
||||
anchors.centerIn: parent
|
||||
height: parent.height
|
||||
width: parent.width
|
||||
sourceSize.height: height
|
||||
sourceSize.width: width
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
Rectangle
|
60
retroshare-qml-app/src/components/CustomFileChooser.qml
Normal file
@ -0,0 +1,60 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Dialogs 1.2
|
||||
|
||||
import "../URI.js" as UriJs
|
||||
|
||||
Item
|
||||
{
|
||||
id: compRoot
|
||||
|
||||
property var resultFile
|
||||
|
||||
FileDialog
|
||||
{
|
||||
id: fileDialog
|
||||
title: "Please choose a file"
|
||||
folder: shortcuts.pictures
|
||||
nameFilters: [ "Image files (*.png *.jpg)"]
|
||||
visible: false
|
||||
selectMultiple: false
|
||||
onAccepted: {
|
||||
console.log("You chose: " + fileDialog.fileUrl)
|
||||
resultFile = fileDialog.fileUrl
|
||||
}
|
||||
onRejected: {
|
||||
console.log("Canceled")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function open()
|
||||
{
|
||||
if (Qt.platform.os === "android")
|
||||
{
|
||||
console.log("ImagePicker Android platform detected")
|
||||
mainWindow.addUriHandler("file", androidResult)
|
||||
androidImagePicker.openPicker()
|
||||
}
|
||||
else
|
||||
{
|
||||
fileDialog.visible = true
|
||||
}
|
||||
}
|
||||
|
||||
function androidResult (uri)
|
||||
{
|
||||
console.log("QML Android image uri found" , uri)
|
||||
resultFile = normalizeUriToFilePath (uri)
|
||||
mainWindow.delUriHandler("media", androidResult)
|
||||
}
|
||||
|
||||
function normalizeUriToFilePath (uriStr)
|
||||
{
|
||||
var uri = new UriJs.URI(uriStr)
|
||||
var hPath = uri.path()
|
||||
return "file:///"+hPath
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
202
retroshare-qml-app/src/components/Faces.qml
Normal file
@ -0,0 +1,202 @@
|
||||
import QtQuick 2.7
|
||||
import Qt.labs.settings 1.0
|
||||
import "../" // Needed by ChatCache (where stores generated faces)
|
||||
|
||||
Item
|
||||
{
|
||||
|
||||
id: faces
|
||||
|
||||
property string hash
|
||||
property var facesCache: ChatCache.facesCache
|
||||
|
||||
|
||||
Image
|
||||
{
|
||||
id: imageAvatar
|
||||
width: height
|
||||
height: iconSize
|
||||
visible: true
|
||||
}
|
||||
|
||||
Canvas
|
||||
{
|
||||
id: canvasAvatar
|
||||
width: height
|
||||
height: canvasSizes
|
||||
visible: false
|
||||
|
||||
renderStrategy: Canvas.Threaded;
|
||||
renderTarget: Canvas.Image;
|
||||
|
||||
property var images
|
||||
property var callback
|
||||
|
||||
|
||||
onPaint:
|
||||
{
|
||||
var ctx = getContext("2d");
|
||||
|
||||
if (images)
|
||||
{
|
||||
for (y = 0 ; y< nPieces ; y++)
|
||||
{
|
||||
ctx.drawImage(images[y], 0, 0, iconSize, iconSize )
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onPainted:
|
||||
{
|
||||
if (callback)
|
||||
{
|
||||
var data = toDataURL('image/png')
|
||||
callback(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
createFromHex(hash)
|
||||
}
|
||||
|
||||
|
||||
property var facesPath: "/icons/faces/"
|
||||
|
||||
property var iconSize: 32
|
||||
property var canvasSizes: iconSize > 32 ? 64 : 32;
|
||||
|
||||
property var nPieces: 6
|
||||
property var pieces: []
|
||||
|
||||
// Number of image files corresponding to heach part:
|
||||
// [background, face, hair|head, mouth, clothes, eye ]
|
||||
property var total:
|
||||
({
|
||||
female: [5, 4, 33, 17, 59, 53],
|
||||
male: [5, 4, 36, 26, 65, 32]
|
||||
})
|
||||
|
||||
function src (gender, piece, random)
|
||||
{
|
||||
var head = gender === 'female' ? 'head' : 'hair';
|
||||
var pieces = ['background', 'face', head, 'mouth', 'clothes', 'eye'];
|
||||
var num = random % total[gender][piece] + 1;
|
||||
return facesPath+gender+canvasSizes+'/'+pieces[piece]+num+'.png';
|
||||
}
|
||||
|
||||
function calcDataFromFingerprint(dataHex)
|
||||
{
|
||||
var females = total.female.reduce(function(previousValue, currentValue)
|
||||
{
|
||||
return previousValue * currentValue;
|
||||
});
|
||||
var males = total.male.reduce(function(previousValue, currentValue)
|
||||
{
|
||||
return previousValue * currentValue;
|
||||
});
|
||||
|
||||
var ret = [];
|
||||
var data = parseInt(dataHex, 16) % (females+males);
|
||||
if (data - females < 0)
|
||||
{
|
||||
var i = 0;
|
||||
ret = ['female'];
|
||||
while (data > 0 || i < total.female.length)
|
||||
{
|
||||
ret.push(data % total.female[i]);
|
||||
data = parseInt(data / total.female[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data = data - females;
|
||||
var i = 0;
|
||||
ret = ['male'];
|
||||
while (data > 0 || i < total.male.length)
|
||||
{
|
||||
ret.push(data % total.male[i]);
|
||||
data = parseInt(data / total.male[i]);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function generateImage(data, callback)
|
||||
{
|
||||
var gender = data[0];
|
||||
var onloads = [];
|
||||
for (var i=0; i<nPieces; i++)
|
||||
{
|
||||
var url = src(gender, i, data[i+1])
|
||||
onloads.push(url)
|
||||
canvasAvatar.loadImage(url)
|
||||
}
|
||||
canvasAvatar.images = onloads
|
||||
canvasAvatar.callback = callback
|
||||
canvasAvatar.requestPaint()
|
||||
}
|
||||
|
||||
// Create the identicon
|
||||
function createFromHex(dataHex)
|
||||
{
|
||||
var iconId = [dataHex, iconSize];
|
||||
var update = function(data)
|
||||
{
|
||||
// This conditions are for solve a bug on an Lg S3.
|
||||
// On this device the toDataURL() is incompleted.
|
||||
// So for see the complete avatar at least at first execution we'll show the canvas,
|
||||
// instead of the image component.
|
||||
// See issue: https://gitlab.com/angesoc/RetroShare/issues/37
|
||||
if (facesCache.iconCache[iconId])
|
||||
{
|
||||
imageAvatar.source = data
|
||||
imageAvatar.visible = true
|
||||
canvasAvatar.visible = false
|
||||
|
||||
canvasAvatar.height = 0
|
||||
imageAvatar.height = iconSize
|
||||
}
|
||||
else
|
||||
{
|
||||
canvasAvatar.visible = true
|
||||
imageAvatar.visible = false
|
||||
|
||||
canvasAvatar.height = iconSize
|
||||
imageAvatar.height = 0
|
||||
}
|
||||
|
||||
facesCache.iconCache[iconId] = data;
|
||||
}
|
||||
|
||||
if (facesCache.iconCache.hasOwnProperty(iconId))
|
||||
{
|
||||
update(facesCache.iconCache[iconId])
|
||||
}
|
||||
else if(facesCache.callbackCache.hasOwnProperty(iconId))
|
||||
{
|
||||
facesCache.callbackCache[iconId].push(update)
|
||||
}
|
||||
else
|
||||
{
|
||||
var onImageGenerated = function(data)
|
||||
{
|
||||
|
||||
facesCache.callbackCache[iconId].forEach(function(callback)
|
||||
{
|
||||
callback(data);
|
||||
})
|
||||
}
|
||||
|
||||
facesCache.callbackCache[iconId] = [update];
|
||||
if (dataHex)
|
||||
{
|
||||
generateImage(calcDataFromFingerprint(dataHex), onImageGenerated);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
283
retroshare-qml-app/src/components/SideBar.qml
Normal file
@ -0,0 +1,283 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import QtQuick.Layouts 1.3
|
||||
import "../URI.js" as UriJs
|
||||
import "../"
|
||||
import "../components"
|
||||
|
||||
|
||||
Drawer
|
||||
{
|
||||
property var styles: StyleSideBar
|
||||
id: drawer
|
||||
height: parent.height
|
||||
width: Math.min(parent.width, parent.height) / 3 * styles.width
|
||||
dragMargin: 10
|
||||
|
||||
Rectangle
|
||||
{
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
|
||||
|
||||
ListView
|
||||
{
|
||||
id: listView
|
||||
currentIndex: -1
|
||||
|
||||
anchors.fill:parent
|
||||
|
||||
clip: true
|
||||
|
||||
snapMode: ListView.SnapToItem
|
||||
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
|
||||
header:Rectangle
|
||||
{
|
||||
id:header
|
||||
property var styles: StyleSideBar.header
|
||||
|
||||
width: parent.width
|
||||
height: colorHash.height + nickText.height + gxsText.height + 40
|
||||
color: styles.color
|
||||
|
||||
AvatarOrColorHash
|
||||
{
|
||||
id: colorHash
|
||||
|
||||
gxs_id: (ChatCache.contactsCache.own)?ChatCache.contactsCache.own.gxs_id : ""
|
||||
height: styles.avatarHeight
|
||||
anchors.margins: styles.avatarMargins
|
||||
anchors.horizontalCenter: header.horizontalCenter
|
||||
anchors.top: header.top
|
||||
onClicked: drawer.close()
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: nickText
|
||||
text: (ChatCache.contactsCache.own)?ChatCache.contactsCache.own.name : "Retroshare"
|
||||
height: contentHeight
|
||||
anchors.top: colorHash.bottom
|
||||
anchors.left: header.left
|
||||
anchors.right: header.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
horizontalAlignment:Text.AlignHCenter
|
||||
wrapMode: Text.Wrap
|
||||
color: styles.textColor
|
||||
font.bold: true
|
||||
font.pixelSize: styles.textNickSize
|
||||
}
|
||||
Text
|
||||
{
|
||||
id: gxsText
|
||||
text: (ChatCache.contactsCache.own)?ChatCache.contactsCache.own.gxs_id : ""
|
||||
// height: contentHeight
|
||||
wrapMode: Text.WrapAnywhere
|
||||
width: header.width
|
||||
anchors.top: nickText.bottom
|
||||
anchors.left: header.left
|
||||
anchors.right: header.right
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
horizontalAlignment:Text.AlignHCenter
|
||||
color: styles.textColor
|
||||
font.pixelSize: styles.textGxsidSize
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
delegate: Item
|
||||
{
|
||||
|
||||
property var styles: StyleSideBar.item
|
||||
|
||||
id: menuItem
|
||||
width: parent.width
|
||||
height: styles.height
|
||||
|
||||
Connections
|
||||
{
|
||||
target: mainWindow
|
||||
onCoreReadyChanged:
|
||||
{
|
||||
if (model.showOnCoreReady)
|
||||
{
|
||||
setVisible(mainWindow.coreReady)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ButtonText
|
||||
{
|
||||
text: model.title
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: menuItem.styles.defaultColor
|
||||
hoverColor: menuItem.styles.hoverColor
|
||||
innerAnchors.left: rectangleButton.left
|
||||
innerAnchors.verticalCenter: rectangleButton.verticalCenter
|
||||
iconUrl: (model.icon)? model.icon : undefined
|
||||
innerMargin: 20
|
||||
buttonTextPixelSize: menuItem.styles.pixelSize
|
||||
borderRadius: 0
|
||||
}
|
||||
|
||||
|
||||
MouseArea
|
||||
{
|
||||
property var lastItem
|
||||
id: itemArea
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
onClicked:
|
||||
{
|
||||
if (listView.currentIndex != index || stackView.currentItem != lastItem)
|
||||
{
|
||||
listView.currentIndex = index
|
||||
menuList.actions[model.title]();
|
||||
lastItem = stackView.currentItem
|
||||
// titleLabel.text = model.title
|
||||
// stackView.replace(model.source)
|
||||
}
|
||||
drawer.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
visible: (model.showOnCoreReady)? setVisible(mainWindow.coreReady) : true
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
if (model.showOnOsAndroid && !Q_OS_ANDROID)
|
||||
{
|
||||
menuItem.visible = false
|
||||
menuItem.height = 0
|
||||
}
|
||||
}
|
||||
|
||||
function setVisible(b)
|
||||
{
|
||||
menuItem.visible = b
|
||||
if (!b)
|
||||
{
|
||||
menuItem.height = 0
|
||||
}
|
||||
else
|
||||
{
|
||||
menuItem.height = styles.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: ListModel
|
||||
{
|
||||
id: menuList
|
||||
|
||||
property var actions :
|
||||
{
|
||||
"Contacts": function()
|
||||
{
|
||||
stackView.push("qrc:/Contacts.qml" )
|
||||
},
|
||||
"Trusted Nodes": function()
|
||||
{
|
||||
stackView.push("qrc:/TrustedNodesView.qml");
|
||||
},
|
||||
"Paste Link": function()
|
||||
{
|
||||
UriJs.URI.withinString(
|
||||
ClipboardWrapper.getFromClipBoard(),
|
||||
handleIntentUri);
|
||||
},
|
||||
"Share identity": function()
|
||||
{
|
||||
rsApi.request(
|
||||
"/peers/self/certificate/", "",
|
||||
function(par)
|
||||
{
|
||||
var radix = JSON.parse(par.response).data.cert_string
|
||||
var name = mainWindow.user_name
|
||||
var encodedName = UriJs.URI.encode(name)
|
||||
var nodeUrl = (
|
||||
"retroshare://certificate?" +
|
||||
"name=" + encodedName +
|
||||
"&radix=" + UriJs.URI.encode(radix) +
|
||||
"&location=" + encodedName )
|
||||
ClipboardWrapper.postToClipBoard(nodeUrl)
|
||||
linkCopiedPopup.itemName = name
|
||||
linkCopiedPopup.open()
|
||||
platformGW.shareUrl(nodeUrl);
|
||||
})
|
||||
},
|
||||
"Terminate Core": function()
|
||||
{
|
||||
rsApi.request("/control/shutdown");
|
||||
}
|
||||
}
|
||||
|
||||
ListElement
|
||||
{
|
||||
title: "Contacts"
|
||||
showOnCoreReady: true
|
||||
icon: "/icons/search.svg"
|
||||
}
|
||||
ListElement
|
||||
{
|
||||
title: "Trusted Nodes"
|
||||
showOnCoreReady: true
|
||||
icon: "/icons/netgraph.svg"
|
||||
}
|
||||
ListElement
|
||||
{
|
||||
title: "Paste Link"
|
||||
showOnCoreReady: true
|
||||
icon: "/icons/add.svg"
|
||||
}
|
||||
ListElement
|
||||
{
|
||||
title: "Share identity"
|
||||
showOnCoreReady: true
|
||||
icon: "/icons/share.svg"
|
||||
}
|
||||
ListElement
|
||||
{
|
||||
title: "Terminate Core"
|
||||
showOnOsAndroid: false
|
||||
icon: "/icons/exit.svg"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator { }
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
property var styles: StyleSideBar.footer
|
||||
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
height: Label.contentHeight
|
||||
color: styles.color
|
||||
|
||||
Label
|
||||
{
|
||||
horizontalAlignment: Text.AlignRight
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
text: parent.styles.text
|
||||
color: parent.styles.textColor
|
||||
anchors.rightMargin: parent.styles.margins
|
||||
anchors.bottomMargin: parent.styles.margins
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
38
retroshare-qml-app/src/components/TextAndIcon.qml
Normal file
@ -0,0 +1,38 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 2.0
|
||||
|
||||
|
||||
Item
|
||||
{
|
||||
height: innerText.implicitHeight
|
||||
|
||||
property int iconHeight: 25
|
||||
|
||||
property alias iconUrl: icon.source
|
||||
property alias innerText: innerText.text
|
||||
|
||||
Image
|
||||
{
|
||||
id: icon
|
||||
|
||||
height: iconHeight
|
||||
width: height
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text
|
||||
{
|
||||
id: innerText
|
||||
wrapMode: Text.WrapAnywhere
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: icon.right
|
||||
anchors.leftMargin: 5
|
||||
}
|
||||
|
||||
|
||||
}
|
56
retroshare-qml-app/src/components/emoji/EmojiButton.qml
Normal file
@ -0,0 +1,56 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
|
||||
Rectangle {
|
||||
id: emojiButton
|
||||
|
||||
property var fontName
|
||||
|
||||
Text {
|
||||
id: emojiText
|
||||
color: "gray"
|
||||
text: qsTr(eCatText)
|
||||
font.pixelSize: emojiButton.width - 8
|
||||
anchors.centerIn: parent
|
||||
font.family: fontName
|
||||
}
|
||||
|
||||
|
||||
state: "RELEASED"
|
||||
states: [
|
||||
State {
|
||||
name: "PRESSED"
|
||||
PropertyChanges {
|
||||
target: emojiText
|
||||
font.pixelSize: emojiButton.width - 10
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "RELEASED"
|
||||
PropertyChanges {
|
||||
target: emojiText
|
||||
font.pixelSize: emojiButton.width - 8
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
onEntered: {
|
||||
emojiText.color = "black"
|
||||
}
|
||||
onExited: {
|
||||
emojiText.color = "gray"
|
||||
}
|
||||
onPressedChanged: {
|
||||
emojiButton.state = emojiButton.state == "PRESSED" ? "RELEASED" : "PRESSED"
|
||||
}
|
||||
onClicked: {
|
||||
Qt.emojiClickedHandler(emojiText.text)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls.Styles 1.2
|
||||
|
||||
Rectangle {
|
||||
id: emojiCategoryButton
|
||||
property string categoryName
|
||||
|
||||
property var fontName
|
||||
|
||||
|
||||
function completedHandler() {
|
||||
categoryName = eCatName
|
||||
|
||||
//initialize
|
||||
if (parent.currSelEmojiButton === undefined) {
|
||||
clickedHandler()
|
||||
}
|
||||
}
|
||||
|
||||
function pressedHandler() {
|
||||
if (state != "SELECTED") {
|
||||
state = state == "PRESSED" ? "RELEASED" : "PRESSED"
|
||||
}
|
||||
}
|
||||
|
||||
function clickedHandler() {
|
||||
if (parent.currSelEmojiButton !== undefined) {
|
||||
parent.currSelEmojiButton.state = "RELEASED"
|
||||
}
|
||||
|
||||
parent.currSelEmojiButton = emojiCategoryButton
|
||||
state = "SELECTED"
|
||||
Qt.emojiCategoryChangedHandler(emojiCategoryButton.categoryName)
|
||||
}
|
||||
|
||||
|
||||
Text {
|
||||
id: emojiText
|
||||
color: "gray"
|
||||
text: qsTr(eCatText)
|
||||
font.pixelSize: emojiCategoryButton.width - 8
|
||||
anchors.centerIn: parent
|
||||
font.family: fontName
|
||||
}
|
||||
|
||||
|
||||
state: "RELEASED"
|
||||
states: [
|
||||
State {
|
||||
name: "PRESSED"
|
||||
PropertyChanges {
|
||||
target: emojiText
|
||||
font.pixelSize: emojiCategoryButton.width - 10
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "RELEASED"
|
||||
PropertyChanges {
|
||||
target: emojiText
|
||||
font.pixelSize: emojiCategoryButton.width - 8
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "SELECTED"
|
||||
PropertyChanges {
|
||||
target: emojiCategoryButton
|
||||
color: "#ADD6FF"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: emojiText.color = "black"
|
||||
onExited: emojiText.color = "gray"
|
||||
onPressedChanged: pressedHandler()
|
||||
onClicked: clickedHandler()
|
||||
}
|
||||
|
||||
Component.onCompleted: completedHandler()
|
||||
}
|
130
retroshare-qml-app/src/components/emoji/EmojiPicker.qml
Normal file
@ -0,0 +1,130 @@
|
||||
import QtQuick 2.7
|
||||
import QtQuick.Controls 2.0
|
||||
import "emoji.js" as EmojiJSON
|
||||
|
||||
Rectangle {
|
||||
id: emojiPicker
|
||||
property EmojiCategoryButton currSelEmojiButton
|
||||
property variant emojiParsedJson
|
||||
property int buttonWidth: 40
|
||||
property TextArea textArea
|
||||
|
||||
property var rootFontName: theme.emojiFontName
|
||||
|
||||
//displays all Emoji of one categroy by modifying the ListModel of emojiGrid
|
||||
function categoryChangedHandler (newCategoryName){
|
||||
emojiByCategory.clear()
|
||||
|
||||
for (var i = 0; i < emojiParsedJson.emoji_by_category[newCategoryName].length; i++) {
|
||||
var elem = emojiParsedJson.emoji_by_category[newCategoryName][i]
|
||||
emojiByCategory.append({eCatName: newCategoryName, eCatText: elem})
|
||||
}
|
||||
}
|
||||
|
||||
//adds the clicked Emoji (and one ' ' if the previous character isn't an Emoji) to textArea
|
||||
function emojiClickedHandler(selectedEmoji) {
|
||||
var strAppnd = ""
|
||||
var plainText = textArea.getText(0, textArea.length)
|
||||
|
||||
if (plainText.length > 0) {
|
||||
var lastChar = plainText[plainText.length-1]
|
||||
if ((lastChar !== ' ') && (lastChar.charCodeAt(0) < 255)) {
|
||||
strAppnd = " "
|
||||
}
|
||||
}
|
||||
strAppnd += selectedEmoji
|
||||
|
||||
textArea.insert(textArea.cursorPosition, strAppnd)
|
||||
}
|
||||
|
||||
//parses JSON, publishes button handlers and inits textArea
|
||||
function completedHandler() {
|
||||
// emojiParsedJson = JSON.parse(EmojiJSON.emoji_json)
|
||||
emojiParsedJson = EmojiJSON.emoji_json
|
||||
for (var i = 0; i < emojiParsedJson.emoji_categories.length; i++) {
|
||||
var elem = emojiParsedJson.emoji_categories[i]
|
||||
emojiCategoryButtons.append({eCatName: elem.name, eCatText: elem.emoji_unified})
|
||||
}
|
||||
|
||||
Qt.emojiCategoryChangedHandler = categoryChangedHandler
|
||||
Qt.emojiClickedHandler = emojiClickedHandler
|
||||
|
||||
textArea.cursorPosition = textArea.length
|
||||
textArea.Keys.pressed.connect(keyPressedHandler)
|
||||
}
|
||||
|
||||
|
||||
//checks if the previous character is an Emoji and adds a ' ' if that's the case
|
||||
//this is necessary, because Emoji use a bigger font-size, and that font-size is kept using without a ' '
|
||||
function keyPressedHandler(event) {
|
||||
var testStr = textArea.getText(textArea.length-2, textArea.length)
|
||||
var ptrn = new RegExp("[\uD800-\uDBFF][\uDC00-\uDFFF]")
|
||||
if ((event.key !== Qt.Key_Backspace) && (ptrn.test(testStr))) {
|
||||
textArea.text += " "
|
||||
textArea.cursorPosition = textArea.length
|
||||
}
|
||||
}
|
||||
|
||||
//all emoji of one category
|
||||
ListModel {
|
||||
id: emojiByCategory
|
||||
}
|
||||
|
||||
GridView {
|
||||
id: emojiGrid
|
||||
width: parent.width
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: buttonWidth
|
||||
cellWidth: buttonWidth; cellHeight: buttonWidth
|
||||
|
||||
model: emojiByCategory
|
||||
delegate: EmojiButton {
|
||||
width: buttonWidth
|
||||
height: buttonWidth
|
||||
color: emojiPicker.color
|
||||
fontName: rootFontName
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//seperator
|
||||
Rectangle {
|
||||
color: emojiPicker.color
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: buttonWidth
|
||||
}
|
||||
Rectangle {
|
||||
color: "black"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: buttonWidth
|
||||
width: parent.width
|
||||
height: 1
|
||||
}
|
||||
|
||||
//emoji category selector
|
||||
ListView {
|
||||
width: parent.width
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: buttonWidth
|
||||
orientation: ListView.Horizontal
|
||||
|
||||
model: emojiCategoryButtons
|
||||
delegate: EmojiCategoryButton {
|
||||
width: buttonWidth
|
||||
height: buttonWidth
|
||||
color: emojiPicker.color
|
||||
fontName: rootFontName
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: emojiCategoryButtons
|
||||
}
|
||||
|
||||
Component.onCompleted:
|
||||
{
|
||||
completedHandler()
|
||||
}
|
||||
}
|
||||
|
1068
retroshare-qml-app/src/components/emoji/emoji.js
Normal file
BIN
retroshare-qml-app/src/fonts/OpenSansEmoji.ttf
Normal file
54
retroshare-qml-app/src/icons/add.svg
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg4155"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
xml:space="preserve"
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 80 80"
|
||||
sodipodi:docname="add.svg"><metadata
|
||||
id="metadata4161"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs4159" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="706"
|
||||
id="namedview4157"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.11875"
|
||||
inkscape:cx="28.462795"
|
||||
inkscape:cy="38.239659"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4163" /><g
|
||||
id="g4163"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="ink_ext_XXXXXX"
|
||||
transform="matrix(1.25,0,0,-1.25,0,80)"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4167"
|
||||
style="fill:#039bd5;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 64,32 C 64,14.327 49.673,0 32,0 14.327,0 0,14.327 0,32 0,49.673 14.327,64 32,64 49.673,64 64,49.673 64,32" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff"
|
||||
d="m 52.357845,35.83752 -15.874967,0 c -0.560293,0 -0.933823,0.373529 -0.933823,0.933822 l 0,15.874965 c 0,1.120588 -0.747056,1.867645 -1.867641,1.867645 l -3.735288,0 c -1.120584,0 -1.867644,-0.747057 -1.867644,-1.867645 l 0,-15.874965 c 0,-0.560293 -0.373527,-0.933822 -0.933819,-0.933822 l -15.874968,0 c -1.120586,0 -1.8676431,-0.747057 -1.8676431,-1.867644 l 0,-3.735285 c 0,-1.120585 0.7470571,-1.867644 1.8676431,-1.867644 l 15.874968,0 c 0.560292,0 0.933819,-0.373527 0.933819,-0.933821 l 0,-15.874967 c 0,-1.120585 0.74706,-1.8676415 1.867644,-1.8676415 l 3.735288,0 c 1.120585,0 1.867641,0.7470565 1.867641,1.8676415 l 0,15.874967 c 0,0.560294 0.37353,0.933821 0.933823,0.933821 l 15.874967,0 c 1.120585,0 1.867641,0.747059 1.867641,1.867644 l 0,3.735285 c 0,1.120587 -0.747056,1.867644 -1.867641,1.867644 z"
|
||||
id="path4" /></g></svg>
|
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 387 B |
13
retroshare-qml-app/src/icons/application-menu.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#4d4d4d;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 2 3 L 2 5 L 14 5 L 14 3 L 2 3 z M 2 7 L 2 9 L 14 9 L 14 7 L 2 7 z M 2 11 L 2 13 L 14 13 L 14 11 L 2 11 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 443 B |
78
retroshare-qml-app/src/icons/attach-image.svg
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg4311"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
xml:space="preserve"
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 80 80"
|
||||
sodipodi:docname="send-image.svg"><metadata
|
||||
id="metadata4317"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs4315" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1009"
|
||||
id="namedview4313"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.085965"
|
||||
inkscape:cx="-284.73674"
|
||||
inkscape:cy="65.541346"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4319" /><g
|
||||
id="g4319"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="ink_ext_XXXXXX"
|
||||
transform="matrix(1.25,0,0,-1.25,0,80)"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4323"
|
||||
style="fill:#039bd5;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 64,32 C 64,14.327 49.673,0 32,0 14.327,0 0,14.327 0,32 0,49.673 14.327,64 32,64 49.673,64 64,49.673 64,32" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4325"
|
||||
style="fill:#000000;fill-opacity:0.15686275;fill-rule:nonzero;stroke:none"
|
||||
d="m 45.545501,13.221531 -27.091002,0 c -1.870464,0 -3.386375,1.516249 -3.386375,3.386375 l 0,27.091001 c 0,1.870464 1.515911,3.386375 3.386375,3.386375 l 27.091002,0 c 1.870464,0 3.386375,-1.515911 3.386375,-3.386375 l 0,-27.091001 c 0,-1.870126 -1.515911,-3.386375 -3.386375,-3.386375" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4327"
|
||||
style="fill:#7db6d8;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 18.454499,16.607906 c -0.933877,0 -1.693187,0.759987 -1.693187,1.693187 l 0,27.091001 c 0,0.933201 0.75931,1.693188 1.693187,1.693188 l 27.091002,0 c 0.933877,0 1.693187,-0.759987 1.693187,-1.693188 l 0,-27.091001 c 0,-0.9332 -0.75931,-1.693187 -1.693187,-1.693187 l -27.091002,0" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4329"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 45.545501,48.778469 -27.091002,0 c -1.870464,0 -3.386375,-1.51591 -3.386375,-3.386375 l 0,-27.091001 c 0,-1.870125 1.515911,-3.386375 3.386375,-3.386375 l 27.091002,0 c 1.870464,0 3.386375,1.51625 3.386375,3.386375 l 0,27.091001 c 0,1.870465 -1.515911,3.386375 -3.386375,3.386375 z m 0,-3.386375 0,-27.091001 -27.091002,0 0,27.091001 27.091002,0" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4331"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 18.454499,18.301093 27.091002,0 0,3.386375 -27.091002,0 0,-3.386375 z" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4333"
|
||||
style="fill:#495672;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 16.752423,21.687468 29.542736,0 0,2.472985 c 0,0 -1.444119,2.520564 -2.782499,4.099716 -0.995425,1.174987 -2.481875,1.507275 -3.778856,0.06417 l -2.330165,-2.237886 -5.078547,6.458241 c -1.891629,2.405173 -4.986607,2.405173 -6.877897,0 L 16.752423,21.687468" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4335"
|
||||
style="fill:#bcd9ff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 41.312532,38.619344 c 0,-1.402806 -1.137314,-2.539781 -2.539782,-2.539781 -1.402806,0 -2.539781,1.136975 -2.539781,2.539781 0,1.402806 1.136975,2.539781 2.539781,2.539781 1.402468,0 2.539782,-1.136975 2.539782,-2.539781" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4337"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 45.545501,48.778469 -27.091002,0 c -1.870464,0 -3.386375,-1.51591 -3.386375,-3.386375 l 0,-27.091001 c 0,-1.870125 1.515911,-3.386375 3.386375,-3.386375 l 27.091002,0 c 1.870464,0 3.386375,1.51625 3.386375,3.386375 l 0,27.091001 c 0,1.870465 -1.515911,3.386375 -3.386375,3.386375 z m 0,-3.386375 0,-27.091001 -27.091002,0 0,27.091001 27.091002,0" /></g></svg>
|
After Width: | Height: | Size: 4.7 KiB |
61
retroshare-qml-app/src/icons/attach.svg
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="80"
|
||||
style="enable-background:new 0 0 110 110;"
|
||||
version="1.0"
|
||||
viewBox="0 0 110 110"
|
||||
width="80"
|
||||
xml:space="preserve"
|
||||
id="svg2"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="attach.svg"><metadata
|
||||
id="metadata20"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs18" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1009"
|
||||
id="namedview16"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.0341309"
|
||||
inkscape:cx="-75.947202"
|
||||
inkscape:cy="16.755676"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" /><g
|
||||
id="Artboard" /><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
style="display:inline"><circle
|
||||
id="circle6"
|
||||
style="fill:#039bd5;fill-opacity:1"
|
||||
r="55"
|
||||
cy="55"
|
||||
cx="55" /><path
|
||||
style="fill:#000000;fill-opacity:0.15686275"
|
||||
sodipodi:nodetypes="ccccccccccsasccsccccccccccccccsasccccccccccc"
|
||||
id="use4188"
|
||||
d="m 32.658016,88.493222 c -3.615848,8.28e-4 -7.263315,-1.424329 -10.041357,-4.202659 -2.77944,-2.779018 -4.205861,-6.427875 -4.204884,-10.04303 -0.0049,-3.403438 1.278081,-6.83375 3.862101,-9.413455 l 0.593216,-0.593494 35.641173,-35.642841 c 3.391596,-3.396336 7.894417,-5.081409 12.383162,-5.075283 4.771078,-0.002 9.594249,1.87938 13.276261,5.560136 3.678385,3.680194 5.559013,8.500861 5.558044,13.271665 0.0042,4.490277 -1.679643,8.991979 -5.075281,12.385393 -10.716723,10.715527 -20.64003,20.648354 -29.983159,29.721144 -1.137853,1.104931 -3.354856,1.236196 -4.391767,0.200648 -1.067827,-1.066425 -1.137299,-3.218296 0.200684,-4.522981 9.420341,-9.185906 29.722264,-29.852779 29.722264,-29.852779 2.153348,-2.157945 3.224312,-4.981261 3.229331,-7.9316 -4.97e-4,-3.126264 -1.22794,-6.331216 -3.714323,-8.818435 -2.48722,-2.488056 -5.693845,-3.714325 -8.821914,-3.716554 -2.949092,0.0058 -5.771848,1.076122 -7.928541,3.230447 L 27.31921,68.693496 26.726551,69.286573 c -1.343545,1.346615 -2.011143,3.098819 -2.016991,4.960511 0.002,1.972136 0.773588,4.003619 2.360607,5.591332 1.585346,1.583812 3.615429,2.354624 5.587991,2.356842 1.861408,-0.0058 3.613479,-0.673018 4.961066,-2.016569 L 62.839621,54.959584 64.95174,52.847599 c 0.506025,-0.509784 0.757852,-1.157881 0.76273,-1.899994 0,-0.782361 -0.304758,-1.604703 -0.959542,-2.259626 -0.656167,-0.656451 -1.478649,-0.960092 -2.260316,-0.962322 -0.741838,0.0064 -1.388963,0.258657 -1.899442,0.765517 0,0 -17.740223,17.818894 -20.832589,21.018568 -1.223513,1.265969 -3.596261,1.141861 -4.639005,3e-6 -1.036591,-1.135117 -0.92155,-3.247964 0.417589,-4.592681 3.215314,-3.228707 20.59883,-20.877036 20.59883,-20.877036 1.744968,-1.750676 4.072705,-2.616475 6.354335,-2.610904 2.425928,-0.002 4.864259,0.956613 6.715226,2.80674 1.845669,1.848595 2.803537,4.284834 2.803537,6.711885 0.0049,2.28024 -0.859952,4.607559 -2.608679,6.353359 l -2.112816,2.111986 -25.22026,25.220118 c -2.576358,2.578305 -5.997345,3.860847 -9.393545,3.860151 l -0.01975,-1.41e-4 0,0 z"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 32.658016,86.241133 c -3.615848,8.27e-4 -7.263315,-1.424329 -10.041357,-4.202659 -2.77944,-2.779019 -4.205861,-6.427875 -4.204884,-10.043031 -0.0049,-3.403437 1.278081,-6.83375 3.862101,-9.413456 l 0.593216,-0.593494 35.641173,-35.642841 c 3.391596,-3.396336 7.894417,-5.081408 12.383162,-5.075281 4.771078,-0.002 9.594249,1.879379 13.276261,5.560134 3.678385,3.680195 5.559013,8.500863 5.558044,13.271665 0.0042,4.490278 -1.679643,8.991981 -5.075281,12.385394 -10.716723,10.715526 -20.64003,20.648355 -29.983159,29.721144 -1.137853,1.10493 -3.354856,1.236196 -4.391767,0.200647 -1.067827,-1.066424 -1.137299,-3.218295 0.200684,-4.522981 9.420341,-9.185904 29.722264,-29.852778 29.722264,-29.852778 2.153348,-2.157945 3.224312,-4.981262 3.229331,-7.9316 -4.97e-4,-3.126264 -1.22794,-6.331217 -3.714323,-8.818435 -2.48722,-2.488056 -5.693845,-3.714325 -8.821914,-3.716555 -2.949092,0.0058 -5.771848,1.076123 -7.928541,3.230448 L 27.31921,66.441405 26.726551,67.034482 c -1.343545,1.346615 -2.011143,3.098821 -2.016991,4.960512 0.002,1.972136 0.773588,4.003619 2.360607,5.591331 1.585346,1.583813 3.615429,2.354624 5.587991,2.356843 1.861408,-0.0058 3.613479,-0.673017 4.961066,-2.01657 L 62.839621,52.707493 64.95174,50.595508 c 0.506025,-0.509784 0.757852,-1.15788 0.76273,-1.899994 0,-0.78236 -0.304758,-1.604703 -0.959542,-2.259625 -0.656167,-0.656452 -1.478649,-0.960092 -2.260316,-0.962321 -0.741838,0.0064 -1.388963,0.258655 -1.899442,0.765515 0,0 -17.740223,17.818896 -20.832589,21.01857 -1.223513,1.265968 -3.596261,1.14186 -4.639005,3e-6 -1.036591,-1.135118 -0.92155,-3.247965 0.417589,-4.592682 3.215314,-3.228708 20.59883,-20.877037 20.59883,-20.877037 1.744968,-1.750675 4.072705,-2.616473 6.354335,-2.610903 2.425928,-0.002 4.864259,0.956612 6.715226,2.806741 1.845669,1.848594 2.803537,4.284833 2.803537,6.711884 0.0049,2.28024 -0.859952,4.607558 -2.608679,6.353358 l -2.112816,2.111987 -25.22026,25.220118 c -2.576358,2.578306 -5.997345,3.860847 -9.393545,3.860151 l -0.01975,-1.4e-4 0,0 z"
|
||||
id="path3"
|
||||
sodipodi:nodetypes="ccccccccccsasccsccccccccccccccsasccccccccccc"
|
||||
style="fill:#ffffff;fill-opacity:1" /></g></svg>
|
After Width: | Height: | Size: 6.0 KiB |
BIN
retroshare-qml-app/src/icons/back.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
108
retroshare-qml-app/src/icons/chat-bubble.svg
Normal file
@ -0,0 +1,108 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
xml:space="preserve"
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 80 80"
|
||||
sodipodi:docname="chat-bubble.svg"><metadata
|
||||
id="metadata8"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs6"><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath16"><path
|
||||
d="M 0,64 64,64 64,0 0,0 0,64 Z"
|
||||
id="path18"
|
||||
inkscape:connector-curvature="0" /></clipPath><clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath28"><path
|
||||
d="M 12,50 52,50 52,8 12,8 12,50 Z"
|
||||
id="path30"
|
||||
inkscape:connector-curvature="0" /></clipPath></defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1009"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="5.9"
|
||||
inkscape:cx="-34.290603"
|
||||
inkscape:cy="29.334046"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g10" /><g
|
||||
id="g10"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="Elegant_circle-icons"
|
||||
transform="matrix(1.25,0,0,-1.25,0,80)"><g
|
||||
id="g26" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path22"
|
||||
style="fill:#039bd5;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 64,32 C 64,14.327 49.673,0 32,0 14.327,0 0,14.327 0,32 0,49.673 14.327,64 32,64 49.673,64 64,49.673 64,32" /><g
|
||||
id="use4160"
|
||||
transform="translate(0,-1.9728814)"
|
||||
style="fill:#000000;fill-opacity:0.15686275"><g
|
||||
id="g4164"
|
||||
transform="translate(49,35.2)"
|
||||
style="fill:#000000;fill-opacity:0.15686275"><path
|
||||
style="fill:#000000;fill-opacity:0.15686275;fill-rule:nonzero;stroke:none"
|
||||
d="M 40 17.466797 C 26.1925 17.466797 15 27.540547 15 39.966797 C 15 51.933049 25.379902 61.713849 38.478516 62.421875 C 39.323921 66.895192 40 69.966797 40 69.966797 C 40 69.966797 46.28026 66.159283 52.013672 59.699219 C 59.752513 55.874654 65 48.473572 65 39.966797 C 65 27.540547 53.8075 17.466797 40 17.466797 z "
|
||||
transform="matrix(0.8,0,0,-0.8,-49,30.772881)"
|
||||
id="path4166" /></g><g
|
||||
id="g4168"
|
||||
transform="translate(52,34)"
|
||||
style="fill:#000000;fill-opacity:0.15686275" /></g><g
|
||||
id="g4154"><g
|
||||
transform="translate(49,35.2)"
|
||||
id="g40"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path42"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,-14.963 -17,-25.2 -17,-25.2 0,0 -9.389,42 0,42 C -7.611,16.8 0,9.278 0,0" /></g><g
|
||||
transform="translate(52,34)"
|
||||
id="g44"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path46"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="m 0,0 c 0,-9.941 -8.954,-18 -20,-18 -11.046,0 -20,8.059 -20,18 0,9.941 8.954,18 20,18 C -8.954,18 0,9.941 0,0" /></g></g><g
|
||||
id="g48"
|
||||
transform="translate(34,34)"
|
||||
style="fill:#036ea7;fill-opacity:1"><path
|
||||
d="m 0,0 c 0,1.105 -0.895,2 -2,2 -1.105,0 -2,-0.895 -2,-2 0,-1.105 0.895,-2 2,-2 1.105,0 2,0.895 2,2"
|
||||
style="fill:#036ea7;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path50"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g52"
|
||||
transform="translate(42,34)"
|
||||
style="fill:#036ea7;fill-opacity:1"><path
|
||||
d="m 0,0 c 0,1.105 -0.895,2 -2,2 -1.105,0 -2,-0.895 -2,-2 0,-1.105 0.895,-2 2,-2 1.105,0 2,0.895 2,2"
|
||||
style="fill:#036ea7;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path54"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
id="g56"
|
||||
transform="translate(26,34)"
|
||||
style="fill:#036ea7;fill-opacity:1"><path
|
||||
d="m 0,0 c 0,1.105 -0.895,2 -2,2 -1.105,0 -2,-0.895 -2,-2 0,-1.105 0.895,-2 2,-2 1.105,0 2,0.895 2,2"
|
||||
style="fill:#036ea7;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
id="path58"
|
||||
inkscape:connector-curvature="0" /></g></g></svg>
|
After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 2.9 KiB |
13
retroshare-qml-app/src/icons/edit-image-face-detect.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text {
|
||||
color:#4d4d4d;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
d="M 2 2 L 2 5 L 3 5 L 3 3 L 5 3 L 5 2 L 2 2 z M 6 2 L 6 3 L 10 3 L 10 2 L 6 2 z M 11 2 L 11 3 L 13 3 L 13 5 L 14 5 L 14 2 L 11 2 z M 6 5 A 1 1 0 0 0 5 6 A 1 1 0 0 0 6 7 A 1 1 0 0 0 7 6 A 1 1 0 0 0 6 5 z M 10 5 A 1 1 0 0 0 9 6 A 1 1 0 0 0 10 7 A 1 1 0 0 0 11 6 A 1 1 0 0 0 10 5 z M 2 6 L 2 10 L 3 10 L 3 6 L 2 6 z M 13 6 L 13 10 L 14 10 L 14 6 L 13 6 z M 4 9 A 4 3 0 0 0 8 12 A 4 3 0 0 0 12 9 L 11 9 A 3 2 0 0 1 8 11 A 3 2 0 0 1 5 9 L 4 9 z M 2 11 L 2 14 L 5 14 L 5 13 L 3 13 L 3 11 L 2 11 z M 13 11 L 13 13 L 11 13 L 11 14 L 14 14 L 14 11 L 13 11 z M 6 13 L 6 14 L 10 14 L 10 13 L 6 13 z "
|
||||
class="ColorScheme-Text"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 922 B |
Before Width: | Height: | Size: 2.2 KiB |
18
retroshare-qml-app/src/icons/emblem-locked.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8">
|
||||
<defs id="defs3051">
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-NeutralText {
|
||||
color:#f67400;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
style="fill:currentColor;fill-opacity:1;stroke:none"
|
||||
class="ColorScheme-NeutralText"
|
||||
d="M 1 0 C 0.4459807 0 0 0.446 0 1 L 0 7 C 0 7.5541 0.4459807 8 1 8 L 7 8 C 7.554019 8 8 7.5541 8 7 L 8 1 C 8 0.446 7.554019 0 7 0 L 1 0 z "
|
||||
/>
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||
d="M 4 1 C 2.8954237 1 1.9999904 1.8954 2 3 L 2.0019531 4 L 1 4 L 1 7 L 7 7 L 7 4 L 6 4 L 6 3 C 6.00001 1.8954 5.104576 1 4 1 z M 4 2 C 4.552285 2 5 2.4477 5 3 L 5 4 L 3 4 L 3 3 C 3 2.4477 3.447715 2 4 2 z "
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 782 B |
65
retroshare-qml-app/src/icons/exit.svg
Normal file
@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg4155"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
xml:space="preserve"
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 80 80"
|
||||
sodipodi:docname="exit.svg"><metadata
|
||||
id="metadata4161"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs4159" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="706"
|
||||
id="namedview4157"
|
||||
showgrid="true"
|
||||
inkscape:zoom="5.11875"
|
||||
inkscape:cx="39.840819"
|
||||
inkscape:cy="37.23375"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g4163"
|
||||
showguides="false"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid4142" /></sodipodi:namedview><g
|
||||
id="g4163"
|
||||
inkscape:groupmode="layer"
|
||||
inkscape:label="ink_ext_XXXXXX"
|
||||
transform="matrix(1.25,0,0,-1.25,0,80)"><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4167"
|
||||
style="fill:#039bd5;fill-opacity:1;fill-rule:nonzero;stroke:none"
|
||||
d="M 64,32 C 64,14.327 49.673,0 32,0 14.327,0 0,14.327 0,32 0,49.673 14.327,64 32,64 49.673,64 64,49.673 64,32" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4.02773762;stroke-linecap:round;stroke-miterlimit:10"
|
||||
d="m 39.607341,43.825636 c 4.92391,-2.756986 8.252834,-8.023253 8.252834,-14.06788 0,-8.89828 -7.21267,-16.11095 -16.11095,-16.11095 -8.89828,0 -16.110951,7.21267 -16.110951,16.11095 0,5.927822 3.201045,11.107492 7.96888,13.904757"
|
||||
stroke-miterlimit="10"
|
||||
id="path13" /><line
|
||||
style="fill:none;stroke:#ffffff;stroke-width:4.02773762;stroke-linecap:round;stroke-miterlimit:10"
|
||||
stroke-miterlimit="10"
|
||||
x1="31.749226"
|
||||
x2="31.749226"
|
||||
y1="29.757759"
|
||||
y2="49.89645"
|
||||
id="line15" /></g></svg>
|
After Width: | Height: | Size: 2.6 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background1.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background1.png.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background2.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background2.png.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background3.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background3.png.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background4.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background4.png.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background5.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/background5.png.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes1.png
Normal file
After Width: | Height: | Size: 387 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes1.png.png
Normal file
After Width: | Height: | Size: 387 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes10.png
Normal file
After Width: | Height: | Size: 432 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes10.png.png
Normal file
After Width: | Height: | Size: 432 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes11.png
Normal file
After Width: | Height: | Size: 792 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes11.png.png
Normal file
After Width: | Height: | Size: 792 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes12.png
Normal file
After Width: | Height: | Size: 803 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes12.png.png
Normal file
After Width: | Height: | Size: 803 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes13.png
Normal file
After Width: | Height: | Size: 789 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes13.png.png
Normal file
After Width: | Height: | Size: 789 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes14.png
Normal file
After Width: | Height: | Size: 792 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes14.png.png
Normal file
After Width: | Height: | Size: 792 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes15.png
Normal file
After Width: | Height: | Size: 803 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes15.png.png
Normal file
After Width: | Height: | Size: 803 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes16.png
Normal file
After Width: | Height: | Size: 801 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes16.png.png
Normal file
After Width: | Height: | Size: 801 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes17.png
Normal file
After Width: | Height: | Size: 719 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes17.png.png
Normal file
After Width: | Height: | Size: 719 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes18.png
Normal file
After Width: | Height: | Size: 743 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes18.png.png
Normal file
After Width: | Height: | Size: 743 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes19.png
Normal file
After Width: | Height: | Size: 720 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes19.png.png
Normal file
After Width: | Height: | Size: 720 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes2.png
Normal file
After Width: | Height: | Size: 440 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes2.png.png
Normal file
After Width: | Height: | Size: 440 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes20.png
Normal file
After Width: | Height: | Size: 728 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes20.png.png
Normal file
After Width: | Height: | Size: 728 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes21.png
Normal file
After Width: | Height: | Size: 731 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes21.png.png
Normal file
After Width: | Height: | Size: 731 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes22.png
Normal file
After Width: | Height: | Size: 733 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes22.png.png
Normal file
After Width: | Height: | Size: 733 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes23.png
Normal file
After Width: | Height: | Size: 709 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes23.png.png
Normal file
After Width: | Height: | Size: 709 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes24.png
Normal file
After Width: | Height: | Size: 717 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes24.png.png
Normal file
After Width: | Height: | Size: 717 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes25.png
Normal file
After Width: | Height: | Size: 723 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes25.png.png
Normal file
After Width: | Height: | Size: 723 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes26.png
Normal file
After Width: | Height: | Size: 571 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes26.png.png
Normal file
After Width: | Height: | Size: 571 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes27.png
Normal file
After Width: | Height: | Size: 426 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes27.png.png
Normal file
After Width: | Height: | Size: 426 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes28.png
Normal file
After Width: | Height: | Size: 610 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes28.png.png
Normal file
After Width: | Height: | Size: 610 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes29.png
Normal file
After Width: | Height: | Size: 619 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes29.png.png
Normal file
After Width: | Height: | Size: 619 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes3.png
Normal file
After Width: | Height: | Size: 439 B |
BIN
retroshare-qml-app/src/icons/faces/female32/clothes3.png.png
Normal file
After Width: | Height: | Size: 439 B |