Commit 3b71cdad authored by Yang Wang's avatar Yang Wang

chatview: initial commit for chatview folder created in lrc

Change-Id: I0b960170ce4a89f162f7a804bdbca229757db0d7
parent 2d0e0a45
......@@ -92,6 +92,7 @@ MessageWebView::MessageWebView(QWidget *parent)
webChannel_ = new QWebChannel(page());
webChannel_->registerObject(QStringLiteral("jsbridge"), jsBridge_);
page()->setWebChannel(webChannel_);
page()->profile()->setHttpUserAgent("jami-windows");
connect(this, &QWebEngineView::renderProcessTerminated,
[this](QWebEnginePage::RenderProcessTerminationStatus termStatus, int statusCode) {
......@@ -239,20 +240,21 @@ void MessageWebView::runJsText()
void MessageWebView::buildView()
{
auto html = Utils::QByteArrayFromFile(":/web/chatview.html");
page()->setHtml(html, QUrl(":/web/chatview.html"));
auto html = Utils::QByteArrayFromFile(":/chatview.html");
page()->setHtml(html, QUrl(":/chatview.html"));
connect(this, &QWebEngineView::loadFinished, this, &MessageWebView::slotLoadFinished);
}
void
MessageWebView::slotLoadFinished()
{
insertStyleSheet("chatcss", Utils::QByteArrayFromFile(":/web/chatview.css"));
page()->runJavaScript(Utils::QByteArrayFromFile(":/web/linkify.js"), QWebEngineScript::MainWorld);
page()->runJavaScript(Utils::QByteArrayFromFile(":/web/linkify-html.js"), QWebEngineScript::MainWorld);
page()->runJavaScript(Utils::QByteArrayFromFile(":/web/linkify-string.js"), QWebEngineScript::MainWorld);
page()->runJavaScript(Utils::QByteArrayFromFile(":/web/qwebchannel.js"), QWebEngineScript::MainWorld);
page()->runJavaScript(Utils::QByteArrayFromFile(":/web/chatview.js"), QWebEngineScript::MainWorld);
insertStyleSheet("chatcss", Utils::QByteArrayFromFile(":/chatview.css"));
insertStyleSheet("chatwin",Utils::QByteArrayFromFile(":/chatview-windows.css"));
page()->runJavaScript(Utils::QByteArrayFromFile(":/linkify.js"), QWebEngineScript::MainWorld);
page()->runJavaScript(Utils::QByteArrayFromFile(":/linkify-html.js"), QWebEngineScript::MainWorld);
page()->runJavaScript(Utils::QByteArrayFromFile(":/linkify-string.js"), QWebEngineScript::MainWorld);
page()->runJavaScript(Utils::QByteArrayFromFile(":/qwebchannel.js"), QWebEngineScript::MainWorld);
page()->runJavaScript(Utils::QByteArrayFromFile(":/chatview.js"), QWebEngineScript::MainWorld);
}
void
......
/***************************************************************************
* Copyright (C) 2019-2019 by Savoir-faire Linux *
* Copyright (C) 2019-2019 by Savoir-faire Linux *
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> *
* *
* This program is free software; you can redistribute it and/or modify *
......
......@@ -67,13 +67,14 @@
<file>images/icons/round-remove_circle-24px.svg</file>
<file>images/icons/round-settings-24px.svg</file>
<file>images/icons/round-undo-24px.svg</file>
<file>web/chatview.css</file>
<file>web/chatview.html</file>
<file>web/chatview.js</file>
<file>web/linkify.js</file>
<file>web/linkify-html.js</file>
<file>web/linkify-string.js</file>
<file>web/qwebchannel.js</file>
<file alias="chatview.css">../lrc/src/web-chatview/chatview.css</file>
<file alias="chatview.html">../lrc/src/web-chatview/chatview.html</file>
<file alias="chatview.js">../lrc/src/web-chatview/chatview.js</file>
<file alias="linkify.js">../lrc/src/web-chatview/linkify.js</file>
<file alias="linkify-html.js">../lrc/src/web-chatview/linkify-html.js</file>
<file alias="linkify-string.js">../lrc/src/web-chatview/linkify-string.js</file>
<file alias="qwebchannel.js">../lrc/src/web-chatview/qwebchannel.js</file>
<file alias="chatview-windows.css">../lrc/src/web-chatview/chatview-windows.css</file>
<file>images/icons/round-check_circle-24px.svg</file>
<file>images/icons/round-error-24px.svg</file>
<file>images/icons/round-save_alt-24px.svg</file>
......
......@@ -786,6 +786,14 @@ del /s /q $(OutDir)\Jami.exp</Command>
<ClInclude Include="ui_videoview.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\lrc\src\web-chatview\chatview-windows.css" />
<None Include="..\lrc\src\web-chatview\chatview.css" />
<None Include="..\lrc\src\web-chatview\chatview.html" />
<None Include="..\lrc\src\web-chatview\chatview.js" />
<None Include="..\lrc\src\web-chatview\linkify-html.js" />
<None Include="..\lrc\src\web-chatview\linkify-string.js" />
<None Include="..\lrc\src\web-chatview\linkify.js" />
<None Include="..\lrc\src\web-chatview\qwebchannel.js" />
<None Include="translations\ring_client_windows.ts" />
<None Include="translations\ring_client_windows_ar.ts" />
<None Include="translations\ring_client_windows_bg.ts" />
......@@ -838,13 +846,6 @@ del /s /q $(OutDir)\Jami.exp</Command>
<None Include="translations\ring_client_windows_zh.ts" />
<None Include="translations\ring_client_windows_zh_CN.ts" />
<None Include="translations\ring_client_windows_zh_TW.ts" />
<None Include="web\chatview.css" />
<None Include="web\chatview.html" />
<None Include="web\chatview.js" />
<None Include="web\linkify-html.js" />
<None Include="web\linkify-string.js" />
<None Include="web\linkify.js" />
<None Include="web\qwebchannel.js" />
</ItemGroup>
<ItemGroup>
<QtUic Include="aboutdialog.ui">
......
......@@ -618,25 +618,28 @@
<None Include="translations\ring_client_windows_zh_TW.ts">
<Filter>Translation Files</Filter>
</None>
<None Include="web\chatview.css">
<None Include="..\lrc\src\web-chatview\chatview.css">
<Filter>Resource Files\web</Filter>
</None>
<None Include="web\chatview.html">
<None Include="..\lrc\src\web-chatview\chatview.html">
<Filter>Resource Files\web</Filter>
</None>
<None Include="web\linkify.js">
<None Include="..\lrc\src\web-chatview\chatview.js">
<Filter>Resource Files\web</Filter>
</None>
<None Include="web\linkify-html.js">
<None Include="..\lrc\src\web-chatview\chatview-windows.css">
<Filter>Resource Files\web</Filter>
</None>
<None Include="web\linkify-string.js">
<None Include="..\lrc\src\web-chatview\linkify.js">
<Filter>Resource Files\web</Filter>
</None>
<None Include="web\chatview.js">
<None Include="..\lrc\src\web-chatview\linkify-html.js">
<Filter>Resource Files\web</Filter>
</None>
<None Include="web\qwebchannel.js">
<None Include="..\lrc\src\web-chatview\linkify-string.js">
<Filter>Resource Files\web</Filter>
</None>
<None Include="..\lrc\src\web-chatview\qwebchannel.js">
<Filter>Resource Files\web</Filter>
</None>
</ItemGroup>
......@@ -876,4 +879,4 @@
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>
</Project>
\ No newline at end of file
</Project>
This diff is collapsed.
<html>
<!-- Empty head might be needed for setSenderImage -->
<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8">
<!--<link rel="stylesheet" href="chatview.css">
<script type="text/javascript" src="linkify.js"></script>
<script type="text/javascript" src="linkify-html.js"></script>
<script type="text/javascript" src="linkify-string.js"></script>
<script type="text/javascript" src="qwebchannel.js"></script>-->
</head>
<body>
<div class="navbar-wrapper">
<div id="invitation">
<div id="invite_header">
<span id="invite_image"></span>
<div id="text"></div>
</div>
<div id="actions">
<div id="accept-btn" class="nav-button action-button invite-btn-green" onclick="acceptInvitation()" title="Accept">
<svg height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" />
</svg>
</div>
<div id="refuse-btn" class="nav-button action-button invite-btn-red" onclick="refuseInvitation()" title="Refuse">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" d="M0 0h24v24H0V0z" />
<path d="M18.3 5.71c-.39-.39-1.02-.39-1.41 0L12 10.59 7.11 5.7c-.39-.39-1.02-.39-1.41 0-.39.39-.39 1.02 0 1.41L10.59 12 5.7 16.89c-.39.39-.39 1.02 0 1.41.39.39 1.02.39 1.41 0L12 13.41l4.89 4.89c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L13.41 12l4.89-4.89c.38-.38.38-1.02 0-1.4z" />
</svg>
</div>
<div id="block-btn" class="nav-button action-button invite-btn-red" onclick="blockConversation()" title="Block">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zM4 12c0-4.42 3.58-8 8-8 1.85 0 3.55.63 4.9 1.69L5.69 16.9C4.63 15.55 4 13.85 4 12zm8 8c-1.85 0-3.55-.63-4.9-1.69L18.31 7.1C19.37 8.45 20 10.15 20 12c0 4.42-3.58 8-8 8z" />
</svg>
</div>
</div>
</div>
</div>
<div id="container">
<div id="messages" onscroll="onScrolled()"></div>
<div id="back_to_bottom_button_container">
<div id="back_to_bottom_button" onclick="back_to_bottom()">Jump to latest &#9660;</div>
</div>
<div id="file_image_send_container"></div>
<div id="sendMessage">
<div class="nav-button action-button" onclick="selectFile()" title="Send File">
<svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z" />
<path d="M0 0h24v24H0z" fill="none" />
</svg>
</div>
<textarea id="message" autofocus placeholder="Type a message" onkeyup="grow_text_area()" onkeydown="process_messagebar_keydown()" rows="1"></textarea>
<div class="nav-button action-button" onclick="sendMessage(); grow_text_area()" title="Send">
<svg class="svgicon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
<path d="M0 0h24v24H0z" fill="none" />
</svg>
</div>
</div>
</div>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2016 SoapBox Innovations Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
'use strict';
;(function (window, linkify) {
var linkifyString = function (linkify) {
'use strict';
/**
Convert strings of text into linkable HTML text
*/
var tokenize = linkify.tokenize;
var options = linkify.options;
var Options = options.Options;
function escapeText(text) {
return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
function escapeAttr(href) {
return href.replace(/"/g, '&quot;');
}
function attributesToString(attributes) {
if (!attributes) {
return '';
}
var result = [];
for (var attr in attributes) {
var val = attributes[attr] + '';
result.push(attr + '="' + escapeAttr(val) + '"');
}
return result.join(' ');
}
function linkifyStr(str) {
var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
opts = new Options(opts);
var tokens = tokenize(str);
var result = [];
for (var i = 0; i < tokens.length; i++) {
var token = tokens[i];
if (token.type === 'nl' && opts.nl2br) {
result.push('<br>\n');
continue;
} else if (!token.isLink || !opts.check(token)) {
result.push(escapeText(token.toString()));
continue;
}
var _opts$resolve = opts.resolve(token);
var formatted = _opts$resolve.formatted;
var formattedHref = _opts$resolve.formattedHref;
var tagName = _opts$resolve.tagName;
var className = _opts$resolve.className;
var target = _opts$resolve.target;
var attributes = _opts$resolve.attributes;
var link = '<' + tagName + ' href="' + escapeAttr(formattedHref) + '"';
if (className) {
link += ' class="' + escapeAttr(className) + '"';
}
if (target) {
link += ' target="' + escapeAttr(target) + '"';
}
if (attributes) {
link += ' ' + attributesToString(attributes);
}
link += '>' + escapeText(formatted) + '</' + tagName + '>';
result.push(link);
}
return result.join('');
}
if (!String.prototype.linkify) {
String.prototype.linkify = function (opts) {
return linkifyStr(this, opts);
};
}
return linkifyStr;
}(linkify);
window.linkifyStr = linkifyString;
})(window, linkify);
This diff is collapsed.
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebChannel module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
"use strict";
var QWebChannelMessageTypes = {
signal: 1,
propertyUpdate: 2,
init: 3,
idle: 4,
debug: 5,
invokeMethod: 6,
connectToSignal: 7,
disconnectFromSignal: 8,
setProperty: 9,
response: 10,
};
var QWebChannel = function(transport, initCallback)
{
if (typeof transport !== "object" || typeof transport.send !== "function") {
console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." +
" Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send));
return;
}
var channel = this;
this.transport = transport;
this.send = function(data)
{
if (typeof(data) !== "string") {
data = JSON.stringify(data);
}
channel.transport.send(data);
}
this.transport.onmessage = function(message)
{
var data = message.data;
if (typeof data === "string") {
data = JSON.parse(data);
}
switch (data.type) {
case QWebChannelMessageTypes.signal:
channel.handleSignal(data);
break;
case QWebChannelMessageTypes.response:
channel.handleResponse(data);
break;
case QWebChannelMessageTypes.propertyUpdate:
channel.handlePropertyUpdate(data);
break;
default:
console.error("invalid message received:", message.data);
break;
}
}
this.execCallbacks = {};
this.execId = 0;
this.exec = function(data, callback)
{
if (!callback) {
// if no callback is given, send directly
channel.send(data);
return;
}
if (channel.execId === Number.MAX_VALUE) {
// wrap
channel.execId = Number.MIN_VALUE;
}
if (data.hasOwnProperty("id")) {
console.error("Cannot exec message with property id: " + JSON.stringify(data));
return;
}
data.id = channel.execId++;
channel.execCallbacks[data.id] = callback;
channel.send(data);
};
this.objects = {};
this.handleSignal = function(message)
{
var object = channel.objects[message.object];
if (object) {
object.signalEmitted(message.signal, message.args);
} else {
console.warn("Unhandled signal: " + message.object + "::" + message.signal);
}
}
this.handleResponse = function(message)
{
if (!message.hasOwnProperty("id")) {
console.error("Invalid response message received: ", JSON.stringify(message));
return;
}
channel.execCallbacks[message.id](message.data);
delete channel.execCallbacks[message.id];
}
this.handlePropertyUpdate = function(message)
{
for (var i in message.data) {
var data = message.data[i];
var object = channel.objects[data.object];
if (object) {
object.propertyUpdate(data.signals, data.properties);
} else {
console.warn("Unhandled property update: " + data.object + "::" + data.signal);
}
}
channel.exec({type: QWebChannelMessageTypes.idle});
}
this.debug = function(message)
{
channel.send({type: QWebChannelMessageTypes.debug, data: message});
};
channel.exec({type: QWebChannelMessageTypes.init}, function(data) {
for (var objectName in data) {
var object = new QObject(objectName, data[objectName], channel);
}
// now unwrap properties, which might reference other registered objects
for (var objectName in channel.objects) {
channel.objects[objectName].unwrapProperties();
}
if (initCallback) {
initCallback(channel);
}
channel.exec({type: QWebChannelMessageTypes.idle});
});
};
function QObject(name, data, webChannel)
{
this.__id__ = name;
webChannel.objects[name] = this;
// List of callbacks that get invoked upon signal emission
this.__objectSignals__ = {};
// Cache of all properties, updated when a notify signal is emitted
this.__propertyCache__ = {};
var object = this;
// ----------------------------------------------------------------------
this.unwrapQObject = function(response)
{
if (response instanceof Array) {
// support list of objects
var ret = new Array(response.length);
for (var i = 0; i < response.length; ++i) {
ret[i] = object.unwrapQObject(response[i]);
}
return ret;
}
if (!response
|| !response["__QObject*__"]
|| response.id === undefined) {
return response;
}
var objectId = response.id;
if (webChannel.objects[objectId])
return webChannel.objects[objectId];
if (!response.data) {
console.error("Cannot unwrap unknown QObject " + objectId + " without data.");
return;
}
var qObject = new QObject( objectId, response.data, webChannel );
qObject.destroyed.connect(function() {
if (webChannel.objects[objectId] === qObject) {
delete webChannel.objects[objectId];
// reset the now deleted QObject to an empty {} object
// just assigning {} though would not have the desired effect, but the
// below also ensures all external references will see the empty map
// NOTE: this detour is necessary to workaround QTBUG-40021
var propertyNames = [];
for (var propertyName in qObject) {
propertyNames.push(propertyName);
}
for (var idx in propertyNames) {
delete qObject[propertyNames[idx]];
}
}
});
// here we are already initialized, and thus must directly unwrap the properties
qObject.unwrapProperties();
return qObject;
}
this.unwrapProperties = function()
{
for (var propertyIdx in object.__propertyCache__) {
object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]);
}
}
function addSignal(signalData, isPropertyNotifySignal)
{
var signalName = signalData[0];
var signalIndex = signalData[1];
object[signalName] = {
connect: function(callback) {
if (typeof(callback) !== "function") {
console.error("Bad callback given to connect to signal " + signalName);
return;
}
object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
object.__objectSignals__[signalIndex].push(callback);
if (!isPropertyNotifySignal && signalName !== "destroyed") {
// only required for "pure" signals, handled separately for properties in propertyUpdate
// also note that we always get notified about the destroyed signal
webChannel.exec({
type: QWebChannelMessageTypes.connectToSignal,
object: object.__id__,
signal: signalIndex
});
}
},
disconnect: function(callback) {
if (typeof(callback) !== "function") {
console.error("Bad callback given to disconnect from signal " + signalName);
return;
}
object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || [];
var idx = object.__objectSignals__[signalIndex].indexOf(callback);
if (idx === -1) {
console.error("Cannot find connection of signal " + signalName + " to " + callback.name);
return;
}
object.__objectSignals__[signalIndex].splice(idx, 1);
if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) {
// only required for "pure" signals, handled separately for properties in propertyUpdate