mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-01-14 08:59:50 -05:00
Implement a working Distant Chat prototype in Qml
Deprecate id field in JSON API as it may cause problems in Qml Offer gxs_id field in JSON API as an id alternative LibresapiLocalClient support callbacks now an instance may be shared for different tasks Expose an instance of LibresapiLocalClient to Qml, type exposure is kept for retrocompatibility but deprecated Qml app now has a tab that permit to exchange some message with selected distant peer
This commit is contained in:
parent
242338d10c
commit
c3aca0cf26
@ -121,32 +121,12 @@ void IdentityHandler::notifyGxsChange(const RsGxsChanges &changes)
|
||||
}
|
||||
}
|
||||
|
||||
void IdentityHandler::handleWildcard(Request &req, Response &resp)
|
||||
void IdentityHandler::handleWildcard(Request & /*req*/, Response &resp)
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
if(req.isPut())
|
||||
{
|
||||
#ifdef REMOVE
|
||||
RsIdentityParameters params;
|
||||
req.mStream << makeKeyValueReference("name", params.nickname);
|
||||
if(req.mStream.isOK())
|
||||
{
|
||||
uint32_t token;
|
||||
mRsIdentity->createIdentity(token, params);
|
||||
// not sure if should acknowledge the token
|
||||
// for now go the easier way
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
RS_STACK_MUTEX(mMtx); // ********** LOCKED **********
|
||||
RS_STACK_MUTEX(mMtx);
|
||||
resp.mStateToken = mStateToken;
|
||||
}
|
||||
RsTokReqOptions opts;
|
||||
@ -163,7 +143,7 @@ void IdentityHandler::handleWildcard(Request &req, Response &resp)
|
||||
#ifdef WINDOWS_SYS
|
||||
Sleep(500);
|
||||
#else
|
||||
usleep(500*1000) ;
|
||||
usleep(500*1000);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -174,33 +154,22 @@ void IdentityHandler::handleWildcard(Request &req, Response &resp)
|
||||
for(std::vector<RsGxsIdGroup>::iterator vit = grps.begin(); vit != grps.end(); vit++)
|
||||
{
|
||||
RsGxsIdGroup& grp = *vit;
|
||||
KeyValueReference<RsGxsGroupId> id("id", grp.mMeta.mGroupId);
|
||||
KeyValueReference<RsPgpId> pgp_id("pgp_id",grp.mPgpId );
|
||||
// not very happy about this, i think the flags should stay hidden in rsidentities
|
||||
//electron: not very happy about this, i think the flags should stay hidden in rsidentities
|
||||
bool own = (grp.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_ADMIN);
|
||||
bool pgp_linked = (grp.mMeta.mGroupFlags & RSGXSID_GROUPFLAG_REALID);
|
||||
resp.mDataStream.getStreamToMember()
|
||||
<< id
|
||||
<< pgp_id
|
||||
<< makeKeyValueReference("id", grp.mMeta.mGroupId) /// @deprecated using "id" as key can cause problems in some JS based languages like Qml @see gxs_id instead
|
||||
<< makeKeyValueReference("gxs_id", grp.mMeta.mGroupId)
|
||||
<< makeKeyValueReference("pgp_id",grp.mPgpId )
|
||||
<< makeKeyValueReference("name", grp.mMeta.mGroupName)
|
||||
<< makeKeyValueReference("own", own)
|
||||
<< makeKeyValueReference("pgp_linked", pgp_linked);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else ok = false;
|
||||
|
||||
if(ok)
|
||||
{
|
||||
resp.setOk();
|
||||
}
|
||||
else
|
||||
{
|
||||
resp.setFail();
|
||||
}
|
||||
if(ok) resp.setOk();
|
||||
else resp.setFail();
|
||||
}
|
||||
|
||||
ResponseTask* IdentityHandler::handleOwn(Request & /* req */, Response &resp)
|
||||
|
@ -482,8 +482,8 @@ bool RsDirUtil::checkCreateDirectory(const std::string& dir)
|
||||
|
||||
std::string RsDirUtil::removeSymLinks(const std::string& path)
|
||||
{
|
||||
#if defined(WINDOWS_SYS) || defined(__APPLE__)
|
||||
#warning (Mr.Alice): I don't know how to do this on windows/MacOS. See https://msdn.microsoft.com/en-us/library/windows/desktop/hh707084(v=vs.85).aspx'
|
||||
#if defined(WINDOWS_SYS) || defined(__APPLE__) || defined(__ANDROID__)
|
||||
#warning (Mr.Alice): I don't know how to do this on windows/MacOS/Android. See https://msdn.microsoft.com/en-us/library/windows/desktop/hh707084(v=vs.85).aspx'
|
||||
//if(!S_OK == PathCchCanonicalizeEx(tmp,...) ;
|
||||
return path ;
|
||||
#else
|
||||
|
@ -1,15 +0,0 @@
|
||||
#ifndef DEBUGUTILS_H
|
||||
#define DEBUGUTILS_H
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
//To switch between debugging and normal mode, un-/comment next line
|
||||
#define DEBUGGING
|
||||
#ifdef DEBUGGING
|
||||
#define myDebug(line) qDebug() << "| FILE:" << __FILE__ << " | LINE_NUMBER:"\
|
||||
<< __LINE__ << " | FUNCTION:" << __FUNCTION__ << " | CONTENT:" << line
|
||||
#else
|
||||
#define myDebug(line)
|
||||
#endif
|
||||
|
||||
#endif // DEBUGUTILS_H
|
@ -18,8 +18,8 @@
|
||||
*/
|
||||
|
||||
#include "libresapilocalclient.h"
|
||||
#include "debugutils.h"
|
||||
#include <QChar>
|
||||
|
||||
#include <QJSEngine>
|
||||
|
||||
|
||||
void LibresapiLocalClient::openConnection(QString socketPath)
|
||||
@ -31,12 +31,13 @@ void LibresapiLocalClient::openConnection(QString socketPath)
|
||||
mLocalSocket.connectToServer(socketPath);
|
||||
}
|
||||
|
||||
int LibresapiLocalClient::request(const QString & path, const QString & jsonData)
|
||||
int LibresapiLocalClient::request( const QString& path, const QString& jsonData,
|
||||
QJSValue callback )
|
||||
{
|
||||
qDebug() << "LibresapiLocalClient::request()" << path << jsonData;
|
||||
QByteArray data;
|
||||
data.append(path); data.append('\n');
|
||||
data.append(jsonData); data.append('\n');
|
||||
callbackQueue.enqueue(callback);
|
||||
mLocalSocket.write(data);
|
||||
|
||||
return 1;
|
||||
@ -44,41 +45,21 @@ int LibresapiLocalClient::request(const QString & path, const QString & jsonData
|
||||
|
||||
void LibresapiLocalClient::socketError(QLocalSocket::LocalSocketError)
|
||||
{
|
||||
myDebug("error!!!!\n" + mLocalSocket.errorString());
|
||||
qDebug() << "Socket Eerror!!" << mLocalSocket.errorString();
|
||||
}
|
||||
|
||||
void LibresapiLocalClient::read()
|
||||
{
|
||||
receivedBytes = mLocalSocket.readLine();
|
||||
|
||||
qDebug() << receivedBytes;
|
||||
|
||||
if(parseResponse()) // pensar en fer un buffer per parsejar, per evitar errors.
|
||||
emit goodResponseReceived(QString(receivedBytes));
|
||||
else
|
||||
QString receivedMsg(mLocalSocket.readLine());
|
||||
QJSValue callback(callbackQueue.dequeue());
|
||||
if(callback.isCallable())
|
||||
{
|
||||
QString errMess = "The message was not understood!\n"
|
||||
"It should be a JSON formatted text file\n"
|
||||
"Its contents were:\n" + receivedBytes;
|
||||
myDebug(errMess.replace(QChar('\n'), QChar::LineSeparator));
|
||||
QJSValue params = callback.engine()->newObject();
|
||||
params.setProperty("response", receivedMsg);
|
||||
|
||||
callback.call(QJSValueList { params });
|
||||
}
|
||||
}
|
||||
|
||||
bool LibresapiLocalClient::parseResponse()
|
||||
{
|
||||
QJsonParseError error;
|
||||
json = QJsonDocument::fromJson(receivedBytes, &error);
|
||||
myDebug(QString(json.toJson()).replace(QChar('\n'), QChar::LineSeparator));
|
||||
|
||||
if(error.error == QJsonParseError::NoError){
|
||||
return true;
|
||||
}
|
||||
myDebug(error.errorString());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const QJsonDocument & LibresapiLocalClient::getJson()
|
||||
{
|
||||
return json;
|
||||
|
||||
emit goodResponseReceived(receivedMsg); /// @deprecated
|
||||
emit responseReceived(receivedMsg);
|
||||
}
|
||||
|
@ -21,9 +21,8 @@
|
||||
#define LIBRESAPILOCALCLIENT_H
|
||||
|
||||
#include <QLocalSocket>
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
#include <QVector>
|
||||
#include <QQueue>
|
||||
#include <QJSValue>
|
||||
|
||||
class LibresapiLocalClient : public QObject
|
||||
{
|
||||
@ -32,25 +31,27 @@ class LibresapiLocalClient : public QObject
|
||||
public:
|
||||
LibresapiLocalClient() : mLocalSocket(this) {}
|
||||
|
||||
// potser abstreure el següent amb QUrl urlPath (path) i amb QJson jsonData.
|
||||
Q_INVOKABLE int request(const QString & path, const QString & jsonData);
|
||||
const QJsonDocument & getJson();
|
||||
Q_INVOKABLE int request( const QString& path, const QString& jsonData = "",
|
||||
QJSValue callback = QJSValue::NullValue);
|
||||
Q_INVOKABLE void openConnection(QString socketPath);
|
||||
|
||||
private:
|
||||
QLocalSocket mLocalSocket;
|
||||
QByteArray receivedBytes;
|
||||
QJsonDocument json;
|
||||
//QVector<QJsonDocument> responses;
|
||||
|
||||
bool parseResponse(); //std::string msg);
|
||||
QQueue<QJSValue> callbackQueue;
|
||||
|
||||
private slots:
|
||||
void socketError(QLocalSocket::LocalSocketError error);
|
||||
void read();
|
||||
|
||||
signals:
|
||||
void goodResponseReceived(const QString & msg);//, int requestId);
|
||||
/// @deprecated @see LibresapiLocalClient::responseReceived instead
|
||||
void goodResponseReceived(const QString & msg);
|
||||
|
||||
/**
|
||||
* @brief responseReceived emitted when a response is received
|
||||
* @param msg
|
||||
*/
|
||||
void responseReceived(const QString & msg);
|
||||
};
|
||||
|
||||
#endif // LIBRESAPILOCALCLIENT_H
|
||||
|
@ -38,6 +38,8 @@ int main(int argc, char *argv[])
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
/// @deprecated
|
||||
qmlRegisterType<LibresapiLocalClient>(
|
||||
"org.retroshare.qml_components.LibresapiLocalClient", 1, 0,
|
||||
"LibresapiLocalClient");
|
||||
@ -45,16 +47,14 @@ int main(int argc, char *argv[])
|
||||
QString sockPath = QString::fromStdString(RsAccounts::ConfigDirectory());
|
||||
sockPath.append("/libresapi.sock");
|
||||
|
||||
LibresapiLocalClient rsApi;
|
||||
rsApi.openConnection(sockPath);
|
||||
|
||||
engine.rootContext()->setContextProperty("apiSocketPath", sockPath);
|
||||
engine.rootContext()->setContextProperty("rsApi", &rsApi);
|
||||
engine.load(QUrl(QLatin1String("qrc:/qml/main.qml")));
|
||||
|
||||
QFileInfo fileInfo(sockPath);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
qDebug() << "Is main.cpp running as a service?" << QtAndroid::androidService().isValid();
|
||||
qDebug() << "Is main.cpp running as an activity?" << QtAndroid::androidActivity().isValid();
|
||||
#endif
|
||||
|
||||
qDebug() << "QML APP:" << sockPath << fileInfo.exists() << fileInfo.lastModified().toString();
|
||||
|
||||
return app.exec();
|
||||
|
@ -22,5 +22,6 @@
|
||||
<file>qml/AddTrustedNode.qml</file>
|
||||
<file>qml/RsLoginPassView.qml</file>
|
||||
<file>qml/TrustedNodesView.qml</file>
|
||||
<file>qml/ChatView.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
92
retroshare-qml-app/src/qml/ChatView.qml
Normal file
92
retroshare-qml-app/src/qml/ChatView.qml
Normal file
@ -0,0 +1,92 @@
|
||||
import QtQuick 2.0
|
||||
import QtQuick.Controls 1.4
|
||||
import org.retroshare.qml_components.LibresapiLocalClient 1.0
|
||||
|
||||
Item
|
||||
{
|
||||
id: chatView
|
||||
property string chatId
|
||||
|
||||
function refreshData()
|
||||
{
|
||||
rsApi.request("/statetokenservice/*")
|
||||
rsApi.request("/chat/messages/"+ chatId, "", function(par) { console.log("Callback called! -> " + par.response ); chatModel.json = par.response })
|
||||
}
|
||||
|
||||
onFocusChanged: focus && refreshData()
|
||||
|
||||
JSONListModel
|
||||
{
|
||||
id: chatModel
|
||||
query: "$.data[*]"
|
||||
}
|
||||
|
||||
Component
|
||||
{
|
||||
id: chatMessageDelegate
|
||||
Item
|
||||
{
|
||||
height: 20
|
||||
Row
|
||||
{
|
||||
Text { text: author_name }
|
||||
Text { text: ": " + msg }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListView
|
||||
{
|
||||
width: parent.width
|
||||
height: 300
|
||||
model: chatModel.model
|
||||
delegate: chatMessageDelegate
|
||||
}
|
||||
|
||||
Rectangle
|
||||
{
|
||||
color: "green"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
width: chatView.width - sendButton.width
|
||||
height: Math.max(20, msgComposer.height)
|
||||
}
|
||||
|
||||
TextEdit
|
||||
{
|
||||
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:
|
||||
{
|
||||
var jsonData = {"chat_id":chatView.chatId, "msg":msgComposer.text}
|
||||
sendRsApi.request("/chat/send_message", JSON.stringify(jsonData))
|
||||
}
|
||||
|
||||
LibresapiLocalClient
|
||||
{
|
||||
id: sendRsApi
|
||||
onGoodResponseReceived: { msgComposer.text = ""; console.log(msg)}
|
||||
Component.onCompleted: { openConnection(apiSocketPath) }
|
||||
}
|
||||
}
|
||||
|
||||
Timer
|
||||
{
|
||||
id: refreshTimer
|
||||
interval: 500
|
||||
repeat: true
|
||||
onTriggered: if(chatView.visible) chatView.refreshData()
|
||||
Component.onCompleted: start()
|
||||
}
|
||||
}
|
@ -22,17 +22,33 @@ import org.retroshare.qml_components.LibresapiLocalClient 1.0
|
||||
|
||||
Item
|
||||
{
|
||||
function refreshData() { rsApi.request("/identity/*/", "") }
|
||||
id: contactsView
|
||||
property string own_gxs_id: ""
|
||||
property string own_nick: ""
|
||||
|
||||
Component.onCompleted: refreshOwn()
|
||||
|
||||
function refreshData() { rsApi.request("/identity/*/", "", function(par) { locationsModel.json = par.response; if(contactsView.own_gxs_id == "") refreshOwn() }) }
|
||||
function refreshOwn()
|
||||
{
|
||||
rsApi.request("/identity/own", "", function(par)
|
||||
{
|
||||
var json = JSON.parse(par.response)
|
||||
if(json.data.length > 0)
|
||||
{
|
||||
contactsView.own_gxs_id = json.data[0].gxs_id
|
||||
contactsView.own_nick = json.data[0].name
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedOwnIdentityView.color = "red"
|
||||
selectedOwnIdentityView.text = "You need to create a GXS identity to chat!"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onFocusChanged: focus && refreshData()
|
||||
|
||||
LibresapiLocalClient
|
||||
{
|
||||
id: rsApi
|
||||
onGoodResponseReceived: locationsModel.json = msg
|
||||
Component.onCompleted: { openConnection(apiSocketPath) }
|
||||
}
|
||||
|
||||
JSONListModel
|
||||
{
|
||||
id: locationsModel
|
||||
@ -45,8 +61,48 @@ Item
|
||||
width: parent.width
|
||||
height: 300
|
||||
model: locationsModel.model
|
||||
delegate: Text { text: model.name }
|
||||
delegate: Item
|
||||
{
|
||||
height: 20
|
||||
width: parent.width
|
||||
|
||||
MouseArea
|
||||
{
|
||||
anchors.fill: parent
|
||||
onClicked:
|
||||
{
|
||||
if(model.own) contactsView.own_gxs_id = model.gxs_id
|
||||
else
|
||||
{
|
||||
var jsonData = { "own_gxs_hex": contactsView.own_gxs_id, "remote_gxs_hex": model.gxs_id }
|
||||
rsApi.request("/chat/initiate_distant_chat", JSON.stringify(jsonData), function (par) { mainWindow.activeChatId = JSON.parse(par.response).data.chat_id })
|
||||
}
|
||||
}
|
||||
Text
|
||||
{
|
||||
color: model.own ? "blue" : "black"
|
||||
text: model.name + " " + model.gxs_id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text { text: "Contacts View"; anchors.bottom: parent.bottom }
|
||||
Text
|
||||
{
|
||||
id: selectedOwnIdentityView
|
||||
color: "green"
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
width: parent.width
|
||||
text: "Open Chat as: " + contactsView.own_nick + " " + contactsView.own_gxs_id
|
||||
}
|
||||
|
||||
Timer
|
||||
{
|
||||
id: refreshTimer
|
||||
interval: 5000
|
||||
repeat: true
|
||||
onTriggered: if(contactsView.visible) contactsView.refreshData()
|
||||
Component.onCompleted: start()
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ ApplicationWindow
|
||||
width: 400
|
||||
height: 400
|
||||
|
||||
property string activeChatId;
|
||||
|
||||
Rectangle
|
||||
{
|
||||
id: mainView
|
||||
@ -102,8 +104,13 @@ ApplicationWindow
|
||||
|
||||
Tab
|
||||
{
|
||||
title: "Blue"
|
||||
Rectangle { color: "blue"; anchors.fill: parent }
|
||||
title: "Chat"
|
||||
ChatView
|
||||
{
|
||||
id: chatView
|
||||
chatId: mainWindow.activeChatId
|
||||
onVisibleChanged: focus = visible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,13 +4,14 @@ QT += qml quick
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
HEADERS += libresapilocalclient.h
|
||||
SOURCES += main.cpp \
|
||||
libresapilocalclient.cpp
|
||||
|
||||
RESOURCES += qml.qrc
|
||||
|
||||
# Additional import path used to resolve QML modules in Qt Creator's code model
|
||||
QML_IMPORT_PATH =
|
||||
#QML_IMPORT_PATH =
|
||||
|
||||
# Default rules for deployment.
|
||||
include(deployment.pri)
|
||||
@ -30,7 +31,3 @@ DEPENDPATH *= ../../libretroshare/src
|
||||
INCLUDEPATH *= ../../libretroshare/src
|
||||
PRE_TARGETDEPS *= ../../libretroshare/src/lib/libretroshare.a
|
||||
LIBS *= ../../libretroshare/src/lib/libretroshare.a
|
||||
|
||||
HEADERS += \
|
||||
libresapilocalclient.h \
|
||||
debugutils.h
|
||||
|
Loading…
Reference in New Issue
Block a user