Browse Source

Merge remote-tracking branch 'justin/justin-groups-features' into feature/group-features

pull/127/head
Phillip Lang Martinez 2 years ago
parent
commit
a91c8f9576
  1. BIN
      qortal-ui-core/font/KoHo.ttf
  2. BIN
      qortal-ui-core/font/Livvic.ttf
  3. BIN
      qortal-ui-core/font/Montserrat.ttf
  4. BIN
      qortal-ui-core/font/Raleway.ttf
  5. 34
      qortal-ui-core/font/material-icons.css
  6. 4
      qortal-ui-core/font/switch-theme.css
  7. 16
      qortal-ui-core/language/us.json
  8. 3
      qortal-ui-core/package.json
  9. 104
      qortal-ui-core/src/components/app-info.js
  10. 82
      qortal-ui-core/src/components/computePowWorker.js
  11. 4
      qortal-ui-core/src/styles/switch-theme.css
  12. 3
      qortal-ui-core/tooling/generateBuildConfig.js
  13. 2
      qortal-ui-core/tooling/generateES5BuildConfig.js
  14. 7
      qortal-ui-crypto/api/transactions/PublicizeTransaction.js
  15. 4
      qortal-ui-plugins/package.json
  16. 2
      qortal-ui-plugins/plugins/core/components/ChatGroupSettings.js
  17. 1
      qortal-ui-plugins/plugins/core/components/ChatModals.js
  18. 898
      qortal-ui-plugins/plugins/core/components/ChatPage.js
  19. 98
      qortal-ui-plugins/plugins/core/components/ChatRightPanel.js
  20. 48
      qortal-ui-plugins/plugins/core/components/ChatScroller-css.js
  21. 104
      qortal-ui-plugins/plugins/core/components/ChatScroller.js
  22. 66
      qortal-ui-plugins/plugins/core/components/ChatSeachResults.js
  23. 120
      qortal-ui-plugins/plugins/core/components/ChatSearchResults-css.js
  24. 61
      qortal-ui-plugins/plugins/core/components/ChatSelect.js
  25. 15
      qortal-ui-plugins/plugins/core/components/ChatSideNavHeads.js
  26. 20
      qortal-ui-plugins/plugins/core/components/ChatTextEditor.js
  27. 12
      qortal-ui-plugins/plugins/core/components/ChatWelcomePage.js
  28. 6
      qortal-ui-plugins/plugins/core/components/NameMenu.js
  29. 6
      qortal-ui-plugins/plugins/core/components/WrapperModal.js
  30. 82
      qortal-ui-plugins/plugins/core/messaging/q-chat/computePowWorker.src.js
  31. 368
      qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat-css.src.js
  32. 819
      qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js

BIN
qortal-ui-core/font/KoHo.ttf

Binary file not shown.

BIN
qortal-ui-core/font/Livvic.ttf

Binary file not shown.

BIN
qortal-ui-core/font/Montserrat.ttf

Binary file not shown.

BIN
qortal-ui-core/font/Raleway.ttf

Binary file not shown.

34
qortal-ui-core/font/material-icons.css

@ -2,7 +2,8 @@
font-family: 'Material Icons'; font-family: 'Material Icons';
font-style: normal; font-style: normal;
font-weight: 400; font-weight: 400;
src: url(MaterialIcons-Regular.eot); /* For IE6-8 */ src: url(MaterialIcons-Regular.eot);
/* For IE6-8 */
src: local('Material Icons'), src: local('Material Icons'),
local('MaterialIcons-Regular'), local('MaterialIcons-Regular'),
url(MaterialIcons-Regular.woff2) format('woff2'), url(MaterialIcons-Regular.woff2) format('woff2'),
@ -10,11 +11,40 @@
url(MaterialIcons-Regular.ttf) format('truetype'); url(MaterialIcons-Regular.ttf) format('truetype');
} }
@font-face {
font-family: 'Montserrat';
src: local('Montserrat'),
local('Montserrat'),
url(Montserrat.ttf) format('truetype');
}
@font-face {
font-family: 'Raleway';
src: local('Raleway'),
local('Raleway'),
url(Raleway.ttf) format('truetype');
}
@font-face {
font-family: 'KoHo';
src: local('KoHo'),
local('KoHo'),
url(KoHo.ttf) format('truetype');
}
@font-face {
font-family: 'Livvic';
src: local('Livvic'),
local('Livvic'),
url(Livvic.ttf) format('truetype');
}
.material-icons { .material-icons {
font-family: 'Material Icons'; font-family: 'Material Icons';
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
font-size: 24px; /* Preferred icon size */ font-size: 24px;
/* Preferred icon size */
display: inline-block; display: inline-block;
line-height: 1; line-height: 1;
text-transform: none; text-transform: none;

4
qortal-ui-core/font/switch-theme.css

@ -48,6 +48,8 @@ html {
--chatHeadText: #080808; --chatHeadText: #080808;
--chatHeadTextActive: #080808; --chatHeadTextActive: #080808;
--lightChatHeadHover: #1e1f201a; --lightChatHeadHover: #1e1f201a;
--group-header: #929292;
--group-drop-shadow: rgb(17 17 26 / 10%) 0px 1px 0px;
} }
html[theme="dark"] { html[theme="dark"] {
@ -100,4 +102,6 @@ html[theme="dark"] {
--chatHeadText: #ffffff; --chatHeadText: #ffffff;
--chatHeadTextActive: #ffffff; --chatHeadTextActive: #ffffff;
--lightChatHeadHover: #e0e1e31a; --lightChatHeadHover: #e0e1e31a;
--group-header: #c8c8c8;
--group-drop-shadow: rgb(191 191 191 / 32%) 0px 1px 0px
} }

16
qortal-ui-core/language/us.json

@ -465,8 +465,8 @@
"cchange3": "Blocked Users", "cchange3": "Blocked Users",
"cchange4": "New Message", "cchange4": "New Message",
"cchange5": "(Click to scroll down)", "cchange5": "(Click to scroll down)",
"cchange6": "Type the name or address of who you want to chat with to send a private message!", "cchange6": "Type the name or address of who you want to chat with to send a private message! You can validate the person's name by clicking on the book icon.",
"cchange7": "Name / Address", "cchange7": "Username / Address",
"cchange8": "Message...", "cchange8": "Message...",
"cchange9": "Send", "cchange9": "Send",
"cchange10": "Blocked Users List", "cchange10": "Blocked Users List",
@ -478,7 +478,7 @@
"cchange16": "Successfully unblocked this user.", "cchange16": "Successfully unblocked this user.",
"cchange17": "Error occurred when trying to unblock this user. Please try again!", "cchange17": "Error occurred when trying to unblock this user. Please try again!",
"cchange18": "unblock", "cchange18": "unblock",
"cchange19": "Invalid Name / Address, Check the name / address and retry...", "cchange19": "Invalid Username / Address, Check the name / address and retry...",
"cchange20": "Message Sent Successfully!", "cchange20": "Message Sent Successfully!",
"cchange21": "Sending failed, Please retry...", "cchange21": "Sending failed, Please retry...",
"cchange22": "Loading Messages...", "cchange22": "Loading Messages...",
@ -492,7 +492,13 @@
"cchange30": "Uploading image. This may take up to one minute.", "cchange30": "Uploading image. This may take up to one minute.",
"cchange31": "Deleting image. This may take up to one minute.", "cchange31": "Deleting image. This may take up to one minute.",
"cchange33": "Cancel", "cchange33": "Cancel",
"cchange34": "This chat message is using an older message version and cannot use this feature." "cchange34": "This chat message is using an older message version and cannot use this feature.",
"cchange35": "Error when trying to fetch the user's name. Please try again!",
"cchange36": "Search Results",
"cchange37": "No Results Found",
"cchange38": "User Verified",
"cchange39": "Cannot send an encrypted message to this user since they do not have their publickey on chain.",
"cchange40": "IMAGE (click to view)"
}, },
"welcomepage": { "welcomepage": {
"wcchange1": "Welcome to Q-Chat", "wcchange1": "Welcome to Q-Chat",
@ -521,7 +527,7 @@
"bcchange13": "Reaction", "bcchange13": "Reaction",
"bcchange14": "Forward", "bcchange14": "Forward",
"bcchange15": "Message Forwarded", "bcchange15": "Message Forwarded",
"bcchange16": "Choose recipient", "bcchange16": "Choose Recipient or Search for One Below",
"bcchange17": "FORWARDED" "bcchange17": "FORWARDED"
}, },
"grouppage": { "grouppage": {

3
qortal-ui-core/package.json

@ -77,7 +77,8 @@
"rollup-plugin-postcss": "4.0.2", "rollup-plugin-postcss": "4.0.2",
"rollup-plugin-progress": "1.1.2", "rollup-plugin-progress": "1.1.2",
"rollup-plugin-scss": "3.0.0", "rollup-plugin-scss": "3.0.0",
"rollup-plugin-terser": "7.0.2" "rollup-plugin-terser": "7.0.2",
"rollup-plugin-web-worker-loader": "^1.6.1"
}, },
"engines": { "engines": {
"node": ">=16.15.0" "node": ">=16.15.0"

104
qortal-ui-core/src/components/app-info.js

@ -3,6 +3,8 @@ import { connect } from 'pwa-helpers'
import { store } from '../store.js' import { store } from '../store.js'
import { doPageUrl } from '../redux/app/app-actions.js' import { doPageUrl } from '../redux/app/app-actions.js'
import { translate, translateUnsafeHTML } from 'lit-translate' import { translate, translateUnsafeHTML } from 'lit-translate'
import WebWorker from 'web-worker:./computePowWorker.js';
import { routes } from '../plugins/routes.js';
import '@material/mwc-icon' import '@material/mwc-icon'
import '@material/mwc-button' import '@material/mwc-button'
@ -94,6 +96,8 @@ class AppInfo extends connect(store)(LitElement) {
this.nodeStatus = {} this.nodeStatus = {}
this.pageUrl = '' this.pageUrl = ''
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
this.publicKeyisOnChainConfirmation = false
this.interval
} }
render() { render() {
@ -107,9 +111,109 @@ class AppInfo extends connect(store)(LitElement) {
` `
} }
async confirmPublicKeyOnChain(address) {
const _computePow2 = async (chatBytes) => {
const difficulty = 15;
const path = window.parent.location.origin + '/memory-pow/memory-pow.wasm.full'
const worker = new WebWorker();
let nonce = null
let chatBytesArray = null
await new Promise((res, rej) => {
worker.postMessage({chatBytes, path, difficulty});
worker.onmessage = e => {
worker.terminate()
chatBytesArray = e.data.chatBytesArray
nonce = e.data.nonce
res()
}
})
let _response = await routes.sign_chat({
data: {
nonce: store.getState().app.selectedAddress.nonce,
chatBytesArray: chatBytesArray,
chatNonce: nonce
},
});
return _response
};
let stop = false
const checkPublicKey = async () => {
if (!stop) {
stop = true;
try {
const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
const url = `${nodeUrl}/addresses/publickey/${address}`;
const res = await fetch(url)
let data = ''
try {
data = await res.text();
} catch (error) {
data = {
error: 'error'
}
}
if(data === 'false' && this.nodeInfo.isSynchronizing !== true){
let _reference = new Uint8Array(64);
window.crypto.getRandomValues(_reference);
let reference = window.parent.Base58.encode(_reference);
const chatRes = await routes.chat({
data: {
type: 19,
nonce: store.getState().app.selectedAddress.nonce,
params: {
lastReference: reference,
proofOfWorkNonce: 0,
fee: 0,
timestamp: Date.now(),
},
disableModal: true
},
disableModal: true,
});
try {
const powRes = await _computePow2(chatRes)
if(powRes === true){
clearInterval(this.interval)
this.publicKeyisOnChainConfirmation = true
}
} catch (error) {
console.error(error)
}
}
if (!data.error && data !== 'false' && data) {
clearInterval(this.interval)
this.publicKeyisOnChainConfirmation = true
}
} catch (error) {
}
stop = false
}
};
this.interval = setInterval(checkPublicKey, 5000);
}
firstUpdated() { firstUpdated() {
this.getNodeInfo() this.getNodeInfo()
this.getCoreInfo() this.getCoreInfo()
try {
this.confirmPublicKeyOnChain(store.getState().app.selectedAddress.address)
} catch (error) {
console.error(error)
}
setInterval(() => { setInterval(() => {
this.getNodeInfo() this.getNodeInfo()

82
qortal-ui-core/src/components/computePowWorker.js

@ -0,0 +1,82 @@
import { Sha256 } from 'asmcrypto.js'
function sbrk(size, heap){
let brk = 512 * 1024 // stack top
let old = brk
brk += size
if (brk > heap.length)
throw new Error('heap exhausted')
return old
}
self.addEventListener('message', async e => {
const response = await computePow(e.data.chatBytes, e.data.path, e.data.difficulty)
postMessage(response)
})
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
const heap = new Uint8Array(memory.buffer)
const computePow = async (chatBytes, path, difficulty) => {
let response = null
await new Promise((resolve, reject)=> {
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; });
const chatBytesArray = new Uint8Array(_chatBytesArray);
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
const hashPtr = sbrk(32, heap);
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
hashAry.set(chatBytesHash);
const workBufferLength = 8 * 1024 * 1024;
const workBufferPtr = sbrk(workBufferLength, heap);
const importObject = {
env: {
memory: memory
},
};
function loadWebAssembly(filename, imports) {
// Fetch the file and compile it
return fetch(filename)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer))
.then(module => {
// Create the instance.
return new WebAssembly.Instance(module, importObject);
});
}
loadWebAssembly(path)
.then(wasmModule => {
response = {
nonce : wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
chatBytesArray
}
resolve()
});
})
return response
}

4
qortal-ui-core/src/styles/switch-theme.css

@ -46,6 +46,8 @@ html {
--chatHeadBgActive: #ebebeb; --chatHeadBgActive: #ebebeb;
--chatHeadText: #080808; --chatHeadText: #080808;
--chatHeadTextActive: #080808; --chatHeadTextActive: #080808;
--group-header: #929292;
--group-drop-shadow: rgb(17 17 26 / 10%) 0px 1px 0px;
} }
html[theme="dark"] { html[theme="dark"] {
@ -96,4 +98,6 @@ html[theme="dark"] {
--chatHeadBgActive: #0f1a2e; --chatHeadBgActive: #0f1a2e;
--chatHeadText: #ffffff; --chatHeadText: #ffffff;
--chatHeadTextActive: #ffffff; --chatHeadTextActive: #ffffff;
--group-header: #c8c8c8;
--group-drop-shadow: rgb(191 191 191 / 32%) 0px 1px 0px
} }

3
qortal-ui-core/tooling/generateBuildConfig.js

@ -7,6 +7,8 @@ const commonjs = require('@rollup/plugin-commonjs')
const alias = require('@rollup/plugin-alias') const alias = require('@rollup/plugin-alias')
const { terser } = require('rollup-plugin-terser') const { terser } = require('rollup-plugin-terser')
const scss = require('rollup-plugin-scss') const scss = require('rollup-plugin-scss')
const webWorkerLoader = require('rollup-plugin-web-worker-loader');
const generateES5BuildConfig = require('./generateES5BuildConfig') const generateES5BuildConfig = require('./generateES5BuildConfig')
@ -61,6 +63,7 @@ const generateBuildConfig = ({ elementComponents, functionalComponents, otherOut
commonjs(), commonjs(),
globals(), globals(),
progress(), progress(),
webWorkerLoader(),
scss({ scss({
output: options.sassOutputDir output: options.sassOutputDir
}), }),

2
qortal-ui-core/tooling/generateES5BuildConfig.js

@ -6,6 +6,7 @@ const progress = require('rollup-plugin-progress');
const { terser } = require("rollup-plugin-terser"); const { terser } = require("rollup-plugin-terser");
const path = require('path'); const path = require('path');
const alias = require('@rollup/plugin-alias'); const alias = require('@rollup/plugin-alias');
const webWorkerLoader = require('rollup-plugin-web-worker-loader');
const generateRollupConfig = (file, { outputDir, aliases }) => { const generateRollupConfig = (file, { outputDir, aliases }) => {
@ -36,6 +37,7 @@ const generateRollupConfig = (file, { outputDir, aliases }) => {
}), }),
commonjs(), commonjs(),
progress(), progress(),
webWorkerLoader(),
babel.babel({ babel.babel({
babelHelpers: 'bundled', babelHelpers: 'bundled',
exclude: 'node_modules/**' exclude: 'node_modules/**'

7
qortal-ui-crypto/api/transactions/PublicizeTransaction.js

@ -1,5 +1,6 @@
"use strict"; "use strict";
import ChatBase from "./chat/ChatBase.js" import ChatBase from "./chat/ChatBase.js"
import { QORT_DECIMALS } from "../constants.js"
export default class PublicizeTransaction extends ChatBase { export default class PublicizeTransaction extends ChatBase {
constructor() { constructor() {
@ -11,13 +12,17 @@ export default class PublicizeTransaction extends ChatBase {
set proofOfWorkNonce(proofOfWorkNonce) { set proofOfWorkNonce(proofOfWorkNonce) {
this._proofOfWorkNonce = this.constructor.utils.int32ToBytes(proofOfWorkNonce) this._proofOfWorkNonce = this.constructor.utils.int32ToBytes(proofOfWorkNonce)
} }
set fee(fee) {
this._fee = fee * QORT_DECIMALS
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
}
get params() { get params() {
const params = super.params; const params = super.params;
params.push( params.push(
this._proofOfWorkNonce, this._proofOfWorkNonce,
this._feeBytes this._feeBytes
) )
console.log({params})
return params; return params;
} }
} }

4
qortal-ui-plugins/package.json

@ -51,12 +51,12 @@
"@rollup/plugin-commonjs": "23.0.0", "@rollup/plugin-commonjs": "23.0.0",
"@rollup/plugin-node-resolve": "15.0.0", "@rollup/plugin-node-resolve": "15.0.0",
"@rollup/plugin-replace": "5.0.0", "@rollup/plugin-replace": "5.0.0",
"@vaadin/avatar": "23.2.5",
"@vaadin/button": "23.2.5", "@vaadin/button": "23.2.5",
"@vaadin/grid": "23.2.5", "@vaadin/grid": "23.2.5",
"@vaadin/horizontal-layout": "23.2.5",
"@vaadin/icons": "23.2.5", "@vaadin/icons": "23.2.5",
"@vaadin/tabs": "23.2.5", "@vaadin/tabs": "23.2.5",
"@vaadin/avatar": "23.2.5",
"@vaadin/horizontal-layout": "23.2.5",
"epml": "0.3.3", "epml": "0.3.3",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"html-escaper": "3.0.3", "html-escaper": "3.0.3",

2
qortal-ui-plugins/plugins/core/components/ChatGroupSettings.js

@ -43,9 +43,11 @@ class ChatGroupSettings extends LitElement {
width: 18px; width: 18px;
transition: .2s all; transition: .2s all;
} }
.top-bar-icon:hover { .top-bar-icon:hover {
color: var(--black) color: var(--black)
} }
.modal-button { .modal-button {
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
font-size: 16px; font-size: 16px;

1
qortal-ui-plugins/plugins/core/components/ChatModals.js

@ -366,7 +366,6 @@ class ChatModals extends LitElement {
<textarea class='textarea' @keydown=${(e) => this._textArea(e)} ?disabled=${this.isLoading} id='messageBox' placeholder='${translate('welcomepage.wcchange5')}' rows='1'></textarea> <textarea class='textarea' @keydown=${(e) => this._textArea(e)} ?disabled=${this.isLoading} id='messageBox' placeholder='${translate('welcomepage.wcchange5')}' rows='1'></textarea>
</p> </p>
<mwc-button ?disabled='${this.isLoading}' slot='primaryAction' @click=${() => { <mwc-button ?disabled='${this.isLoading}' slot='primaryAction' @click=${() => {
console.log("here500");
this._sendMessage(); this._sendMessage();
} }
}>${translate('welcomepage.wcchange6')} }>${translate('welcomepage.wcchange6')}

898
qortal-ui-plugins/plugins/core/components/ChatPage.js

File diff suppressed because it is too large Load Diff

98
qortal-ui-plugins/plugins/core/components/ChatRightPanel.js

@ -52,9 +52,11 @@ class ChatRightPanel extends LitElement {
width: 18px; width: 18px;
transition: 0.2s all; transition: 0.2s all;
} }
.top-bar-icon:hover { .top-bar-icon:hover {
color: var(--black); color: var(--black);
} }
.modal-button { .modal-button {
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
font-size: 16px; font-size: 16px;
@ -65,6 +67,7 @@ class ChatRightPanel extends LitElement {
border: none; border: none;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
} }
.close-row { .close-row {
width: 100%; width: 100%;
display: flex; display: flex;
@ -73,16 +76,18 @@ class ChatRightPanel extends LitElement {
flex:0 flex:0
} }
.container-body { .container-body {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-grow: 1; flex-grow: 1;
overflow:auto; overflow:auto;
margin-top: 15px; margin-top: 5px;
padding: 0px 5px; padding: 0px 6px;
box-sizing: border-box; box-sizing: border-box;
} }
.container-body::-webkit-scrollbar-track { .container-body::-webkit-scrollbar-track {
background-color: whitesmoke; background-color: whitesmoke;
border-radius: 7px; border-radius: 7px;
@ -104,18 +109,68 @@ class ChatRightPanel extends LitElement {
background-color: rgb(148, 146, 146); background-color: rgb(148, 146, 146);
cursor: pointer; cursor: pointer;
} }
p { p {
color: var(--black); color: var(--black);
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
word-break: break-all; word-break: break-all;
} }
.container { .container {
display: flex; display: flex;
width: 100%; width: 100%;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
.chat-right-panel-label {
font-family: Montserrat, sans-serif;
color: var(--group-header);
padding: 5px;
font-size: 13px;
user-select: none;
}
.group-info {
display: flex;
flex-direction: column;
justify-content: flex-start;
gap: 10px;
}
.group-name {
font-family: Raleway, sans-serif;
font-size: 20px;
color: var(--chat-bubble-msg-color);
text-align: center;
user-select: none;
}
.group-description {
font-family: Roboto, sans-serif;
color: var(--chat-bubble-msg-color);
letter-spacing: 0.3px;
font-weight: 300;
font-size: 14px;
margin-top: 15px;
word-break: break-word;
user-select: none;
}
.group-subheader {
font-family: Montserrat, sans-serif;
font-size: 14px;
color: var(--chat-bubble-msg-color);
}
.group-data {
font-family: Roboto, sans-serif;
letter-spacing: 0.3px;
font-weight: 300;
font-size: 14px;
color: var(--chat-bubble-msg-color);
}
` `
} }
@ -167,20 +222,20 @@ class ChatRightPanel extends LitElement {
} }
async unitFee(txType) { async unitFee(txType) {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
const url = `${nodeUrl}/transactions/unitfee?txType=${txType}` const url = `${nodeUrl}/transactions/unitfee?txType=${txType}`;
let fee = null let fee = null;
try { try {
const res = await fetch(url) const res = await fetch(url);
const data = await res.json() const data = await res.json();
fee = (Number(data) / 1e8).toFixed(3) fee = (Number(data) / 1e8).toFixed(3);
} catch (error) { } catch (error) {
fee = null fee = null;
} }
return fee return fee;
} }
async getLastRef() { async getLastRef() {
@ -188,7 +243,7 @@ class ChatRightPanel extends LitElement {
type: "api", type: "api",
url: `/addresses/lastreference/${this.selectedAddress.address}`, url: `/addresses/lastreference/${this.selectedAddress.address}`,
}) })
return myRef return myRef;
} }
getTxnRequestResponse(txnResponse, reference) { getTxnRequestResponse(txnResponse, reference) {
@ -381,7 +436,8 @@ class ChatRightPanel extends LitElement {
} }
} }
render() { render() {
console.log('this.groupMembers', this.groupMembers) console.log('this.groupMembers', this.groupMembers);
console.log(5, "Chat Right Panel Here");
const owner = this.groupAdmin.filter((admin)=> admin.address === this.leaveGroupObj.owner) const owner = this.groupAdmin.filter((admin)=> admin.address === this.leaveGroupObj.owner)
return html` return html`
<div class="container"> <div class="container">
@ -389,13 +445,15 @@ class ChatRightPanel extends LitElement {
<vaadin-icon class="top-bar-icon" @click=${()=> this.toggle(false)} style="margin: 0px 10px" icon="vaadin:close" slot="icon"></vaadin-icon> <vaadin-icon class="top-bar-icon" @click=${()=> this.toggle(false)} style="margin: 0px 10px" icon="vaadin:close" slot="icon"></vaadin-icon>
</div> </div>
<div id="viewElement" class="container-body"> <div id="viewElement" class="container-body">
<p style="font-size: 20px;">${this.leaveGroupObj && this.leaveGroupObj.groupName}</p> <p class="group-name">${this.leaveGroupObj && this.leaveGroupObj.groupName}</p>
<p style="font-size: 14px;margin-top: 5px">${this.leaveGroupObj && this.leaveGroupObj.description}</p> <div class="group-info">
<p style="font-size: 14px;margin-top: 10px">Members: ${this.leaveGroupObj && this.leaveGroupObj.memberCount}</p> <p class="group-description">${this.leaveGroupObj && this.leaveGroupObj.description}</p>
<p class="group-subheader">Members: <span class="group-data">${this.leaveGroupObj && this.leaveGroupObj.memberCount}</span></p>
<p style="font-size: 14px;margin-top: 5px">Date created : ${new Date(this.leaveGroupObj.created).toLocaleDateString("en-US")}</p> <p class="group-subheader">Date created : <span class="group-data">${new Date(this.leaveGroupObj.created).toLocaleDateString("en-US")}</span></p>
</div>
<br /> <br />
<p class="chat-right-panel-label">Group Owner</p> <p class="chat-right-panel-label">GROUP OWNER</p>
${owner.map((item) => { ${owner.map((item) => {
return html`<chat-side-nav-heads return html`<chat-side-nav-heads
activeChatHeadUrl="" activeChatHeadUrl=""
@ -403,7 +461,7 @@ class ChatRightPanel extends LitElement {
chatInfo=${JSON.stringify(item)} chatInfo=${JSON.stringify(item)}
></chat-side-nav-heads>` ></chat-side-nav-heads>`
})} })}
<p class="chat-right-panel-label">Admins</p> <p class="chat-right-panel-label">ADMINS</p>
${this.groupAdmin.map((item) => { ${this.groupAdmin.map((item) => {
return html`<chat-side-nav-heads return html`<chat-side-nav-heads
activeChatHeadUrl="" activeChatHeadUrl=""
@ -411,7 +469,7 @@ class ChatRightPanel extends LitElement {
chatInfo=${JSON.stringify(item)} chatInfo=${JSON.stringify(item)}
></chat-side-nav-heads>` ></chat-side-nav-heads>`
})} })}
<p class="chat-right-panel-label">Members</p> <p class="chat-right-panel-label">MEMBERS</p>
${this.groupMembers.map((item) => { ${this.groupMembers.map((item) => {
return html`<chat-side-nav-heads return html`<chat-side-nav-heads
activeChatHeadUrl="" activeChatHeadUrl=""

48
qortal-ui-plugins/plugins/core/components/ChatScroller-css.js

@ -446,4 +446,52 @@ export const chatStyles = css`
width: 100%; width: 100%;
justify-content: center justify-content: center
} }
.delete-image-msg {
font-family: Livvic, sans-serif;
font-size: 20px;
color: var(--chat-bubble-msg-color);
letter-spacing: 0.3px;
font-weight: 300;
text-align: center;
}
.modal-button-row {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.modal-button {
font-family: Roboto, sans-serif;
font-size: 16px;
color: var(--mdc-theme-primary);
background-color: transparent;
padding: 8px 10px;
border-radius: 5px;
border: none;
transition: all 0.3s ease-in-out;
}
.modal-button-red {
font-family: Roboto, sans-serif;
font-size: 16px;
color: #F44336;
background-color: transparent;
padding: 8px 10px;
border-radius: 5px;
border: none;
transition: all 0.3s ease-in-out;
}
.modal-button-red:hover {
cursor: pointer;
background-color: #f4433663;
}
.modal-button:hover {
cursor: pointer;
background-color: #03a8f475;
}
` `

104
qortal-ui-plugins/plugins/core/components/ChatScroller.js

@ -37,6 +37,7 @@ class ChatScroller extends LitElement {
setIsLoadingMessages: {attribute: false}, setIsLoadingMessages: {attribute: false},
chatId: { type: String }, chatId: { type: String },
setForwardProperties: { attribute: false }, setForwardProperties: { attribute: false },
setOpenPrivateMessage: { attribute: false },
} }
} }
@ -53,12 +54,22 @@ class ChatScroller extends LitElement {
render() { render() {
console.log({messages: this.messages}) let formattedMessages = this.messages.reduce((messageArray, message, index) => {
let formattedMessages = this.messages.reduce((messageArray, message) => {
const lastGroupedMessage = messageArray[messageArray.length - 1]; const lastGroupedMessage = messageArray[messageArray.length - 1];
let timestamp; let timestamp;
let sender; let sender;
let repliedToData; let repliedToData;
let firstMessageInChat;
if (index === 0) {
firstMessageInChat = true;
} else {
firstMessageInChat = false;
}
message = {...message, firstMessageInChat}
if (lastGroupedMessage) { if (lastGroupedMessage) {
timestamp = lastGroupedMessage.timestamp; timestamp = lastGroupedMessage.timestamp;
sender = lastGroupedMessage.sender; sender = lastGroupedMessage.sender;
@ -105,6 +116,7 @@ class ChatScroller extends LitElement {
?isLastMessageInGroup=${indexMessage === formattedMessage.messages.length - 1} ?isLastMessageInGroup=${indexMessage === formattedMessage.messages.length - 1}
.setToggledMessage=${this.setToggledMessage} .setToggledMessage=${this.setToggledMessage}
.setForwardProperties=${this.setForwardProperties} .setForwardProperties=${this.setForwardProperties}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
> >
</message-template>` </message-template>`
) )
@ -133,10 +145,9 @@ class ChatScroller extends LitElement {
} }
setToggledMessage (message) { setToggledMessage (message) {
toggledMessage = message toggledMessage = message;
} }
async firstUpdated() { async firstUpdated() {
this.emojiPicker.on('emoji', selection => { this.emojiPicker.on('emoji', selection => {
@ -144,8 +155,6 @@ class ChatScroller extends LitElement {
type: 'reaction', type: 'reaction',
editedMessageObj: toggledMessage, editedMessageObj: toggledMessage,
reaction: selection.emoji, reaction: selection.emoji,
}) })
}); });
this.viewElement = this.shadowRoot.getElementById('viewElement'); this.viewElement = this.shadowRoot.getElementById('viewElement');
@ -226,12 +235,15 @@ class MessageTemplate extends LitElement {
sendMessage: { attribute: false }, sendMessage: { attribute: false },
sendMessageForward: { attribute: false }, sendMessageForward: { attribute: false },
openDialogImage: { attribute: false }, openDialogImage: { attribute: false },
openDeleteImage: { type: Boolean },
isImageLoaded: { type: Boolean }, isImageLoaded: { type: Boolean },
isFirstMessage: { type: Boolean }, isFirstMessage: { type: Boolean },
isSingleMessageInGroup: { type: Boolean }, isSingleMessageInGroup: { type: Boolean },
isLastMessageInGroup: { type: Boolean }, isLastMessageInGroup: { type: Boolean },
setToggledMessage: {attribute: false}, setToggledMessage: {attribute: false},
setForwardProperties: {attribute: false}, setForwardProperties: {attribute: false},
viewImage: {type: Boolean},
setOpenPrivateMessage : { attribute: false }
} }
} }
@ -248,6 +260,7 @@ class MessageTemplate extends LitElement {
this.isFirstMessage = false this.isFirstMessage = false
this.isSingleMessageInGroup = false this.isSingleMessageInGroup = false
this.isLastMessageInGroup = false this.isLastMessageInGroup = false
this.viewImage = false
} }
static styles = [chatStyles] static styles = [chatStyles]
@ -352,11 +365,15 @@ class MessageTemplate extends LitElement {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`; imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`;
if(this.viewImage || this.myAddress === this.messageObj.sender){
imageHTML = createImage(imageUrl); imageHTML = createImage(imageUrl);
imageHTMLDialog = createImage(imageUrl); imageHTMLDialog = createImage(imageUrl)
imageHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px"; imageHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px";
} }
}
nameMenu = html` nameMenu = html`
<span class="${this.messageObj.sender === this.myAddress && 'message-data-my-name'}"> <span class="${this.messageObj.sender === this.myAddress && 'message-data-my-name'}">
${this.messageObj.senderName ? this.messageObj.senderName : cropAddress(this.messageObj.sender)} ${this.messageObj.senderName ? this.messageObj.senderName : cropAddress(this.messageObj.sender)}
@ -453,17 +470,28 @@ class MessageTemplate extends LitElement {
</p> </p>
</div> </div>
`} `}
${image && !isImageDeleted ? html` ${image && !isImageDeleted && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
<div
@click=${()=> {
this.viewImage = true
}}
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
style=${this.isFirstMessage && "margin-top: 10px;"}>
<div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black)">
${translate("chatpage.cchange40")}
</div>
</div>
` : html``}
${image && !isImageDeleted && (this.viewImage || this.myAddress === this.messageObj.sender) ? html`
<div <div
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
style=${this.isFirstMessage && "margin-top: 10px;"}> style=${this.isFirstMessage && "margin-top: 10px;"}>
${imageHTML}<vaadin-icon ${imageHTML}<vaadin-icon
@click=${() => this.sendMessage({ @click=${() => {
type: 'delete', this.openDeleteImage = true;
name: image.name, this.chatE
identifier: image.identifier, }}
editedMessageObj: this.messageObj,
})}
class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon> class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon>
</div> </div>
` : image && isImageDeleted ? html` ` : image && isImageDeleted ? html`
@ -508,6 +536,8 @@ class MessageTemplate extends LitElement {
.emojiPicker=${this.emojiPicker} .emojiPicker=${this.emojiPicker}
.setToggledMessage=${this.setToggledMessage} .setToggledMessage=${this.setToggledMessage}
.setForwardProperties=${this.setForwardProperties} .setForwardProperties=${this.setForwardProperties}
?firstMessageInChat=${this.messageObj.firstMessageInChat}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
> >
</chat-menu> </chat-menu>
</div> </div>
@ -561,6 +591,31 @@ class MessageTemplate extends LitElement {
${translate("general.close")} ${translate("general.close")}
</mwc-button> </mwc-button>
</mwc-dialog> </mwc-dialog>
<mwc-dialog
hideActions
?open=${this.openDeleteImage}
@closed=${()=> {
this.openDeleteImage = false;
}}>
<div class="delete-image-msg">
<p>Are you sure you want to delete this image?</p>
</div>
<div class="modal-button-row" @click=${() => this.openDeleteImage = false}>
<button class="modal-button-red">
Cancel
</button>
<button
class="modal-button"
@click=${() => this.sendMessage({
type: 'delete',
name: image.name,
identifier: image.identifier,
editedMessageObj: this.messageObj,
})}>
Yes
</button>
</div>
</mwc-dialog>
` `
} }
} }
@ -586,7 +641,9 @@ class ChatMenu extends LitElement {
version: { type: String }, version: { type: String },
setToggledMessage: { attribute: false }, setToggledMessage: { attribute: false },
sendMessageForward: { attribute: false }, sendMessageForward: { attribute: false },
setForwardProperties: {attribute: false} setForwardProperties: { attribute: false },
firstMessageInChat: { type: Boolean },
setOpenPrivateMessage: { attribute: false }
} }
} }
@ -666,7 +723,7 @@ class ChatMenu extends LitElement {
return html` return html`
<div class="container"> <div class="container">
<div <div
class="menu-icon tooltip reaction" class=${`menu-icon reaction ${!this.firstMessageInChat ? "tooltip" : ""}`}
data-text="${translate("blockpage.bcchange13")}" data-text="${translate("blockpage.bcchange13")}"
@click=${(e) => { @click=${(e) => {
if(this.version === '0'){ if(this.version === '0'){
@ -685,21 +742,24 @@ class ChatMenu extends LitElement {
<vaadin-icon icon="vaadin:smiley-o" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:smiley-o" slot="icon"></vaadin-icon>
</div> </div>
<div <div
class="menu-icon tooltip" class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`}
data-text="${translate("blockpage.bcchange14")}" data-text="${translate("blockpage.bcchange14")}"
@click="${() => { @click="${() => {
this.messageForwardFunc() this.messageForwardFunc()
}}"> }}">
<vaadin-icon icon="vaadin:arrow-forward" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:arrow-forward" slot="icon"></vaadin-icon>
</div> </div>
<div class="menu-icon tooltip" data-text="${translate("blockpage.bcchange9")}" @click="${() => this.showPrivateMessageModal()}"> <div class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`} data-text="${translate("blockpage.bcchange9")}" @click="${() => this.setOpenPrivateMessage({
name: this.originalMessage.senderName ? this.originalMessage.senderName : this.originalMessage.sender,
open: true
})}">
<vaadin-icon icon="vaadin:paperplane" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:paperplane" slot="icon"></vaadin-icon>
</div> </div>
<div class="menu-icon tooltip" data-text="${translate("blockpage.bcchange8")}" @click="${() => this.copyToClipboard(this.toblockaddress)}"> <div class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`} data-text="${translate("blockpage.bcchange8")}" @click="${() => this.copyToClipboard(this.toblockaddress)}">
<vaadin-icon icon="vaadin:copy" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:copy" slot="icon"></vaadin-icon>
</div> </div>
<div <div
class="menu-icon tooltip" class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`}
data-text="${translate("blockpage.bcchange11")}" data-text="${translate("blockpage.bcchange11")}"
@click="${() => { @click="${() => {
if (this.version === '0') { if (this.version === '0') {
@ -715,7 +775,7 @@ class ChatMenu extends LitElement {
${this.myAddress === this.originalMessage.sender ? ( ${this.myAddress === this.originalMessage.sender ? (
html` html`
<div <div
class="menu-icon tooltip" class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`}
data-text="${translate("blockpage.bcchange12")}" data-text="${translate("blockpage.bcchange12")}"
@click=${() => { @click=${() => {
if(this.version === '0'){ if(this.version === '0'){
@ -729,7 +789,7 @@ class ChatMenu extends LitElement {
</div> </div>
` `
) : html`<div></div>`} ) : html`<div></div>`}
<div class="menu-icon tooltip" data-text="${translate("blockpage.bcchange10")}" @click="${() => this.showBlockIconFunc(true)}"> <div class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`} data-text="${translate("blockpage.bcchange10")}" @click="${() => this.showBlockIconFunc(true)}">
<vaadin-icon icon="vaadin:ellipsis-dots-h" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:ellipsis-dots-h" slot="icon"></vaadin-icon>
</div> </div>
${this.showBlockAddressIcon ${this.showBlockAddressIcon

66
qortal-ui-plugins/plugins/core/components/ChatSeachResults.js

@ -0,0 +1,66 @@
import { LitElement, html } from 'lit';
import { render } from 'lit/html.js';
import { chatSearchResultsStyles } from './ChatSearchResults-css.js'
import { translate } from 'lit-translate';
export class ChatSearchResults extends LitElement {
static get properties() {
return {
onClickFunc: { attribute: false },
closeFunc: { attribute: false },
searchResults: { type: Array },
isOpen: { type: Boolean },
loading: { type: Boolean }
}
}
static styles = [chatSearchResultsStyles]
render() {
return html`
<div class="chat-results-card" style=${this.isOpen ? "display: block;" : "display: none;"}>
<vaadin-icon
@click=${() => this.closeFunc()}
icon="vaadin:close-small"
slot="icon"
class="close-icon"
>
</vaadin-icon>
${this.loading ? (
html`
<div class="spinner-container">
<paper-spinner-lite active></paper-spinner-lite>
</div>
`
) : (
html`
<p class="chat-result-header">${translate("chatpage.cchange36")}</p>
<div class="divider"></div>
<div class="chat-result-container">
${this.searchResults.length === 0 ? (
html`<p class="no-results">${translate("chatpage.cchange37")}</p>`
) : (
html`
${this.searchResults.map((result) => {
return (
html`
<div class="chat-result-card" @click=${() => {
this.shadowRoot.querySelector(".chat-result-card").classList.add("active");
this.onClickFunc(result);
}}>
<p class="chat-result">
${result.name}
</p>
</div>
`
)}
)}
`
)}
</div>
`
)}
</div>
`;
}
}
customElements.define('chat-search-results', ChatSearchResults);

120
qortal-ui-plugins/plugins/core/components/ChatSearchResults-css.js

@ -0,0 +1,120 @@
import { css } from 'lit'
export const chatSearchResultsStyles = css`
.chat-results-card {
position: relative;
padding: 25px 20px;
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
width: 300px;
min-height: 200px;
height: auto;
border-radius: 5px;
background-color: var(--white);
}
.chat-result-header {
color: var(--chat-bubble-msg-color);
font-size: 18px;
font-family: Montserrat, sans-serif;
text-align: center;
margin: 0 0 10px 0;
user-select: none;
}
.divider {
height: 1px;
background: var(--chat-bubble-msg-color);
margin: 0 40px;
user-select: none;
}
.no-results {
font-family: Roboto, sans-serif;
font-weight: 300;
letter-spacing: 0.3px;
font-size: 16px;
color: var(--chat-bubble-msg-color);
text-align: center;
margin: 20px 0 0 0;
user-select: none;
}
.chat-result-container {
height: 200px;
overflow-y: auto;
padding: 0 10px;
}
.chat-result-container::-webkit-scrollbar-track {
background-color: whitesmoke;
border-radius: 7px;
}
.chat-result-container::-webkit-scrollbar {
width: 6px;
border-radius: 7px;
background-color: whitesmoke;
}
.chat-result-container::-webkit-scrollbar-thumb {
background-color: rgb(180, 176, 176);
border-radius: 7px;
transition: all 0.3s ease-in-out;
}
.chat-result-container::-webkit-scrollbar-thumb:hover {
background-color: rgb(148, 146, 146);
cursor: pointer;
}
.chat-result-card {
padding: 12px;
margin-bottom: 15px;
margin-top: 15px;
transition: all 0.2s ease-in-out;
box-shadow: none;
}
.chat-result-card:active {
background-color: #09b814;
}
.chat-result-card:hover {
cursor: pointer;
border: none;
border-radius: 4px;
box-sizing: border-box;
-webkit-box-shadow: rgba(132, 132, 132, 40%) 0px 0px 6px -1px;
box-shadow: rgba(132, 132, 132, 40%) 0px 0px 6px -1px;
}
.chat-result {
font-family: Roboto, sans-serif;
font-weight: 300;
letter-spacing: 0.3px;
font-size: 15px;
color: var(--chat-bubble-msg-color);
margin: 0;
user-select: none;
}
.spinner-container {
display: flex;
width: 100%;
justify-content: center
}
.close-icon {
position: absolute;
top: 5px;
right: 5px;
color: var(--chat-bubble-msg-color);
font-size: 14px;
transition: all 0.1s ease-in-out;
}
.close-icon:hover {
cursor: pointer;
font-size: 15px;
}
`

61
qortal-ui-plugins/plugins/core/components/ChatSelect.js

@ -57,6 +57,10 @@ class ChatSelect extends LitElement {
color: #92959e; color: #92959e;
} }
.name {
user-select: none;
}
.clearfix:after { .clearfix:after {
visibility: hidden; visibility: hidden;
display: block; display: block;
@ -122,13 +126,60 @@ class ChatSelect extends LitElement {
} }
return html` return html`
<li @click=${() => this.getUrl(this.chatInfo.url)} class="clearfix ${this.activeChatHeadUrl === this.chatInfo.url ? 'active' : ''}"> <li
@click=${() => this.getUrl(this.chatInfo.url)}
class="clearfix ${this.activeChatHeadUrl === this.chatInfo.url ? 'active' : ''}">
${this.isImageLoaded ? html`${avatarImg}` : html``} ${this.isImageLoaded ? html`${avatarImg}` : html``}
${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`<mwc-icon class="img-icon">account_circle</mwc-icon>` : html`` } ${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`<mwc-icon class="img-icon">account_circle</mwc-icon>` :
${!this.isImageLoaded && this.chatInfo.name ? html`<div style="width:40px; height:40px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadBgActive)' : 'var(--chatHeadBg)' }; color: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadTextActive)' : 'var(--chatHeadText)' }; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize">${this.chatInfo.name.charAt(0)}</div>`: ''} html``
${!this.isImageLoaded && this.chatInfo.groupName ? html`<div style="width:40px; height:40px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadBgActive)' : 'var(--chatHeadBg)' }; color: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadTextActive)' : 'var(--chatHeadText)' }; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize">${this.chatInfo.groupName.charAt(0)}</div>`: ''} }
${!this.isImageLoaded && this.chatInfo.name ?
html`
<div
style="width:40px; height:40px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ?
'var(--chatHeadBgActive)' :
'var(--chatHeadBg)' };
color: ${this.activeChatHeadUrl === this.chatInfo.url ?
'var(--chatHeadTextActive)' :
'var(--chatHeadText)'};
font-weight:bold;
display: flex;
justify-content: center;
align-items: center;
text-transform: capitalize">
${this.chatInfo.name.charAt(0)}
</div>`:
''}
${!this.isImageLoaded && this.chatInfo.groupName ?
html`
<div
style="width:40px;
height:40px;
float: left;
border-radius:50%;
background: ${this.activeChatHeadUrl === this.chatInfo.url ?
'var(--chatHeadBgActive)' :
'var(--chatHeadBg)' };
color: ${this.activeChatHeadUrl === this.chatInfo.url ?
'var(--chatHeadTextActive)' :
'var(--chatHeadText)' };
font-weight:bold;
display: flex;
justify-content: center;
align-items: center;
text-transform: capitalize">
${this.chatInfo.groupName.charAt(0)}
</div>`:
''}
<div class="about"> <div class="about">
<div class="name"><span style="float:left; padding-left: 8px; color: var(--chat-group);">${this.chatInfo.groupName ? this.chatInfo.groupName : this.chatInfo.name !== undefined ? this.chatInfo.name : this.chatInfo.address.substr(0, 15)} </span> </div> <div class="name">
<span style="float:left; padding-left: 8px; color: var(--chat-group);">
${this.chatInfo.groupName ?
this.chatInfo.groupName :
this.chatInfo.name !== undefined ? this.chatInfo.name :
this.chatInfo.address.substr(0, 15)}
</span>
</div>
</div> </div>
</li> </li>
` `

15
qortal-ui-plugins/plugins/core/components/ChatSideNavHeads.js

@ -47,18 +47,15 @@ class ChatSideNavHeads extends LitElement {
color: var(--chat-group); color: var(--chat-group);
} }
.about {
margin-top: 8px;
}
.about {
padding-left: 8px;
}
.status { .status {
color: #92959e; color: #92959e;
} }
.clearfix {
display: flex;
align-items: center;
}
.clearfix:after { .clearfix:after {
visibility: hidden; visibility: hidden;
display: block; display: block;
@ -129,7 +126,7 @@ class ChatSideNavHeads extends LitElement {
${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`<mwc-icon class="img-icon">account_circle</mwc-icon>` : html`` } ${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`<mwc-icon class="img-icon">account_circle</mwc-icon>` : html`` }
${!this.isImageLoaded && this.chatInfo.name ? html`<div style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadBgActive)' : 'var(--chatHeadBg)' }; color: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadTextActive)' : 'var(--chatHeadText)' }; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize">${this.chatInfo.name.charAt(0)}</div>`: ''} ${!this.isImageLoaded && this.chatInfo.name ? html`<div style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadBgActive)' : 'var(--chatHeadBg)' }; color: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadTextActive)' : 'var(--chatHeadText)' }; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize">${this.chatInfo.name.charAt(0)}</div>`: ''}
${!this.isImageLoaded && this.chatInfo.groupName ? html`<div style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadBgActive)' : 'var(--chatHeadBg)' }; color: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadTextActive)' : 'var(--chatHeadText)' }; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize">${this.chatInfo.groupName.charAt(0)}</div>`: ''} ${!this.isImageLoaded && this.chatInfo.groupName ? html`<div style="width:30px; height:30px; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadBgActive)' : 'var(--chatHeadBg)' }; color: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadTextActive)' : 'var(--chatHeadText)' }; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize">${this.chatInfo.groupName.charAt(0)}</div>`: ''}
<div class="about"> <div>
<div class="name"><span style="float:left; padding-left: 8px; color: var(--chat-group);">${this.chatInfo.groupName ? this.chatInfo.groupName : this.chatInfo.name !== undefined ? this.chatInfo.name : this.chatInfo.address.substr(0, 15)} </span> </div> <div class="name"><span style="float:left; padding-left: 8px; color: var(--chat-group);">${this.chatInfo.groupName ? this.chatInfo.groupName : this.chatInfo.name !== undefined ? this.chatInfo.name : this.chatInfo.address.substr(0, 15)} </span> </div>
</div> </div>
</li> </li>

20
qortal-ui-plugins/plugins/core/components/ChatTextEditor.js

@ -172,9 +172,12 @@ class ChatTextEditor extends LitElement {
} }
return html` return html`
<div <div
class=${["chatbar-container", this.iframeId === "newChat" ? "chatbar-caption" : ""].join(" ")} class=${["chatbar-container", (this.iframeId === "newChat" || this.iframeId === "privateMessage") ? "chatbar-caption" : ""].join(" ")}
style="${scrollHeightBool ? 'align-items: flex-end' : "align-items: center"}"> style="${scrollHeightBool ? 'align-items: flex-end' : "align-items: center"}">
<div class="file-picker-container" @click=${(e) => { <div
style=${this.iframeId === "privateMessage" ? "display: none" : "display: block"}
class="file-picker-container"
@click=${(e) => {
this.preventUserSendingImage(e) this.preventUserSendingImage(e)
}}> }}>
<vaadin-icon <vaadin-icon
@ -223,7 +226,13 @@ class ChatTextEditor extends LitElement {
` `
) : ) :
html` html`
<div style="${ scrollHeightBool ? 'margin-bottom: 5px;' : "margin-bottom: 0;"} ${this.iframeId === 'newChat' ? 'display: none;' : 'display: flex;'}"> <div
style="${scrollHeightBool
? 'margin-bottom: 5px;'
: "margin-bottom: 0;"}
${this.iframeId === 'newChat'
? 'display: none;'
: 'display: flex;'}">
${this.isLoading === false ? html` ${this.isLoading === false ? html`
<img <img
src="/img/qchat-send-message-icon.svg" src="/img/qchat-send-message-icon.svg"
@ -300,7 +309,6 @@ class ChatTextEditor extends LitElement {
} }
}) })
this.emojiPickerHandler = this.shadowRoot.querySelector('.emoji-button'); this.emojiPickerHandler = this.shadowRoot.querySelector('.emoji-button');
this.mirrorChatInput = this.shadowRoot.getElementById('messageBox'); this.mirrorChatInput = this.shadowRoot.getElementById('messageBox');
this.chatMessageInput = this.shadowRoot.getElementById(this.iframeId); this.chatMessageInput = this.shadowRoot.getElementById(this.iframeId);
@ -361,6 +369,7 @@ class ChatTextEditor extends LitElement {
return; return;
}; };
this.chatMessageSize = 0; this.chatMessageSize = 0;
this.chatEditor.updateMirror();
this._sendMessage(props); this._sendMessage(props);
} }
@ -688,11 +697,10 @@ class ChatTextEditor extends LitElement {
}, 0); }, 0);
res(); res();
}) })
// Handle Enter // Handle Enter
if (e.keyCode === 13 && !e.shiftKey) { if (e.keyCode === 13 && !e.shiftKey) {
// Update Mirror
editor.updateMirror();
if (editor.state() === 'false') return false; if (editor.state() === 'false') return false;
if (editorConfig.iframeId === 'newChat') { if (editorConfig.iframeId === 'newChat') {

12
qortal-ui-plugins/plugins/core/components/ChatWelcomePage.js

@ -25,7 +25,8 @@ class ChatWelcomePage extends LitElement {
btnDisable: { type: Boolean }, btnDisable: { type: Boolean },
isLoading: { type: Boolean }, isLoading: { type: Boolean },
balance: { type: Number }, balance: { type: Number },
theme: { type: String, reflect: true } theme: { type: String, reflect: true },
setOpenPrivateMessage: { attribute: false }
} }
} }
@ -212,7 +213,14 @@ class ChatWelcomePage extends LitElement {
<div class="center-box"> <div class="center-box">
<mwc-icon class="img-icon">chat</mwc-icon><br> <mwc-icon class="img-icon">chat</mwc-icon><br>
<span style="font-size: 20px; color: var(--black);">${this.myAddress.address}</span> <span style="font-size: 20px; color: var(--black);">${this.myAddress.address}</span>
<div class="start-chat" @click=${() => this.shadowRoot.querySelector('#startSecondChatDialog').show()}>${translate("welcomepage.wcchange2")}</div> <div
class="start-chat"
@click="${() => this.setOpenPrivateMessage({
name: "",
open: true
})}">
${translate("welcomepage.wcchange2")}
</div>
</div> </div>
</div> </div>

6
qortal-ui-plugins/plugins/core/components/NameMenu.js

@ -250,15 +250,15 @@ class NameMenu extends LitElement {
} }
firstUpdated() { firstUpdated() {
this.getChatBlockedAdresses() this.getChatBlockedAdresses();
setInterval(() => { setInterval(() => {
this.getChatBlockedAdresses(); this.getChatBlockedAdresses();
}, 60000) }, 60000)
window.addEventListener('storage', () => { window.addEventListener('storage', () => {
const checkLanguage = localStorage.getItem('qortalLanguage') const checkLanguage = localStorage.getItem('qortalLanguage');
use(checkLanguage) use(checkLanguage);
}) })
window.onclick = function(event) { window.onclick = function(event) {

6
qortal-ui-plugins/plugins/core/components/WrapperModal.js

@ -5,8 +5,8 @@ import { wrapperModalStyles } from './WrapperModal-css.js'
export class WrapperModal extends LitElement { export class WrapperModal extends LitElement {
static get properties() { static get properties() {
return { return {
removeImage: { type: Function }, customStyle: {type: String},
customStyle: {type: String} onClickFunc: { attribute: false },
} }
} }
@ -16,7 +16,7 @@ export class WrapperModal extends LitElement {
return html` return html`
<div> <div>
<div class="backdrop" @click=${() => { <div class="backdrop" @click=${() => {
this.removeImage() this.onClickFunc();
}}></div> }}></div>
<div class="modal-body" style=${this.customStyle ? this.customStyle : ""}> <div class="modal-body" style=${this.customStyle ? this.customStyle : ""}>
<slot></slot> <slot></slot>

82
qortal-ui-plugins/plugins/core/messaging/q-chat/computePowWorker.src.js

@ -0,0 +1,82 @@
import { Sha256 } from 'asmcrypto.js'
function sbrk(size, heap){
let brk = 512 * 1024 // stack top
let old = brk
brk += size
if (brk > heap.length)
throw new Error('heap exhausted')
return old
}
self.addEventListener('message', async e => {
const response = await computePow(e.data.chatBytes, e.data.path, e.data.difficulty)
postMessage(response)
})
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 })
const heap = new Uint8Array(memory.buffer)
const computePow = async (chatBytes, path, difficulty) => {
let response = null
await new Promise((resolve, reject)=> {
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; });
const chatBytesArray = new Uint8Array(_chatBytesArray);
const chatBytesHash = new Sha256().process(chatBytesArray).finish().result;
const hashPtr = sbrk(32, heap);
const hashAry = new Uint8Array(memory.buffer, hashPtr, 32);
hashAry.set(chatBytesHash);
const workBufferLength = 8 * 1024 * 1024;
const workBufferPtr = sbrk(workBufferLength, heap);
const importObject = {
env: {
memory: memory
},
};
function loadWebAssembly(filename, imports) {
// Fetch the file and compile it
return fetch(filename)
.then(response => response.arrayBuffer())
.then(buffer => WebAssembly.compile(buffer))
.then(module => {
// Create the instance.
return new WebAssembly.Instance(module, importObject);
});
}
loadWebAssembly(path)
.then(wasmModule => {
response = {
nonce : wasmModule.exports.compute2(hashPtr, workBufferPtr, workBufferLength, difficulty),
chatBytesArray
}
resolve()
});
})
return response
}

368
qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat-css.src.js

@ -0,0 +1,368 @@
import { css } from 'lit'
export const qchatStyles = css`
* {
--mdc-theme-primary: rgb(3, 169, 244);
--mdc-theme-secondary: var(--mdc-theme-primary);
--paper-input-container-focus-color: var(--mdc-theme-primary);
--mdc-theme-surface: var(--white);
--mdc-dialog-content-ink-color: var(--black);
--lumo-primary-text-color: rgb(0, 167, 245);
--lumo-primary-color-50pct: rgba(0, 167, 245, 0.5);
--lumo-primary-color-10pct: rgba(0, 167, 245, 0.1);
--lumo-primary-color: hsl(199, 100%, 48%);
--lumo-base-color: var(--white);
--lumo-body-text-color: var(--black);
--_lumo-grid-border-color: var(--border);
--_lumo-grid-secondary-border-color: var(--border2);
--mdc-dialog-min-width: 750px;
}
paper-spinner-lite {
height: 24px;
width: 24px;
--paper-spinner-color: var(--mdc-theme-primary);
--paper-spinner-stroke-width: 2px;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
ul {
list-style: none;
padding: 0;
}
.container {
margin: 0 auto;
width: 100%;
background: var(--white);
}
.people-list {
width: 20vw;
float: left;
height: 100vh;
overflow-y: hidden;
border-right: 3px #ddd solid;
}
.people-list .blockedusers {
position: absolute;
bottom: 0;
width: 20vw;
height: 60px;
background: var(--white);
border-top: 1px solid var(--border);
border-right: 3px #ddd solid;
}
.people-list .search {
padding-top: 20px;
padding-left: 20px;
padding-right: 20px;
}
.center {
margin: 0;
position: absolute;
padding-top: 12px;
left: 50%;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
.people-list .create-chat {
border-radius: 5px;
border: none;
display: inline-block;
padding: 14px;
color: #fff;
background: var(--tradehead);
width: 100%;
font-size: 15px;
text-align: center;
cursor: pointer;
}
.people-list .create-chat:hover {
opacity: .8;
box-shadow: 0 3px 5px rgba(0, 0, 0, .2);
}
.people-list ul {
padding: 0;
height: 85vh;
overflow-y: auto;
overflow-x: hidden;
}
.chat {
width: 80vw;
height: 100vh;
float: left;
background: var(--white);
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
color: #434651;
box-sizing: border-box;
}
.chat .new-message-bar {
display: flex;
flex: 0 1 auto;
align-items: center;
justify-content: space-between;
padding: 0px 25px;
font-size: 14px;
font-weight: 500;
top: 0;
position: absolute;
left: 20vw;
right: 0;
z-index: 5;
background: var(--tradehead);
color: var(--white);
border-radius: 0 0 8px 8px;
min-height: 25px;
transition: opacity .15s;
text-transform: capitalize;
opacity: .85;
cursor: pointer;
}
.chat .new-message-bar:hover {
opacity: .75;
transform: translateY(-1px);
box-shadow: 0 3px 7px rgba(0, 0, 0, .2);
}
.hide-new-message-bar {
display: none !important;
}
.chat .chat-history {
position: absolute;
top: 0;
right: 0;
bottom: 100%;
left: 20vw;
border-bottom: 2px solid var(--white);
overflow-y: hidden;
height: 100vh;
box-sizing: border-box;
}
.chat .chat-message {
padding: 10px;
height: 10%;
display: inline-block;
width: 100%;
background-color: #eee;
}
.chat .chat-message textarea {
width: 90%;
border: none;
font-size: 16px;
padding: 10px 20px;
border-radius: 5px;
resize: none;
}
.chat .chat-message button {
float: right;
color: #94c2ed;
font-size: 16px;
text-transform: uppercase;
border: none;
cursor: pointer;
font-weight: bold;
background: #f2f5f8;
padding: 10px;
margin-top: 4px;
margin-right: 4px;
}
.chat .chat-message button:hover {
color: #75b1e8;
}
.online,
.offline,
.me {
margin-right: 3px;
font-size: 10px;
}
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.red {
--mdc-theme-primary: red;
}
h2 {
margin:0;
}
h2, h3, h4, h5 {
color: var(--black);
font-weight: 400;
}
[hidden] {
display: hidden !important;
visibility: none !important;
}
.details {
display: flex;
font-size: 18px;
}
.title {
font-weight:600;
font-size:12px;
line-height: 32px;
opacity: 0.66;
}
.textarea {
width: 100%;
border: none;
display: inline-block;
font-size: 16px;
padding: 10px 20px;
border-radius: 5px;
height: 120px;
resize: none;
background: #eee;
}
.dialog-container {
position: relative;
display: flex;
align-items: center;
flex-direction: column;
padding: 0 10px;
gap: 10px;
height: 100%;
}
.dialog-header {
color: var(--chat-bubble-msg-color);
}
.dialog-subheader {
color: var(--chat-bubble-msg-color);
}
.modal-button-row {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.modal-button {
font-family: Roboto, sans-serif;
font-size: 16px;
color: var(--mdc-theme-primary);
background-color: transparent;
padding: 8px 10px;
border-radius: 5px;
border: none;
transition: all 0.3s ease-in-out;
}
.modal-button-red {
font-family: Roboto, sans-serif;
font-size: 16px;
color: #F44336;
background-color: transparent;
padding: 8px 10px;
border-radius: 5px;
border: none;
transition: all 0.3s ease-in-out;
}
.modal-button-red:hover {
cursor: pointer;
background-color: #f4433663;
}
.modal-button:hover {
cursor: pointer;
background-color: #03a8f475;
}
.name-input {
width: 100%;
outline: 0;
border-width: 0 0 2px;
border-color: var(--mdc-theme-primary);
background-color: transparent;
padding: 10px;
font-family: Roboto, sans-serif;
font-size: 15px;
color: var(--chat-bubble-msg-color);
}
.name-input::selection {
background-color: var(--mdc-theme-primary);
color: white;
}
.name-input::placeholder {
opacity: 0.9;
color: var(--black);
}
.search-field {
width: 100%;
position: relative;
}
.search-icon {
position: absolute;
right: 3px;
color: var(--chat-bubble-msg-color);
transition: all 0.3s ease-in-out;
background: none;
border-radius: 50%;
padding: 6px 3px;
font-size: 21px;
}
.search-icon:hover {
cursor: pointer;
background: #d7d7d75c;
}
.search-results-div {
position: absolute;
top: 25px;
right: 25px;
}
.user-verified {
position: absolute;
top: 0;
right: 5px;
display: flex;
align-items: center;
gap: 10px;
color: #04aa2e;
font-size: 13px;
}
`

819
qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js

@ -1,7 +1,9 @@
import { LitElement, html, css } from 'lit' import { LitElement, html, css } from 'lit';
import { render } from 'lit/html.js' import { render } from 'lit/html.js';
import { Epml } from '../../../../epml.js' import { Epml } from '../../../../epml.js';
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate';
import { qchatStyles } from './q-chat-css.src.js'
import WebWorker from 'web-worker:./computePowWorker.src.js';
registerTranslateConfig({ registerTranslateConfig({
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
@ -10,6 +12,8 @@ registerTranslateConfig({
import '../../components/ChatWelcomePage.js' import '../../components/ChatWelcomePage.js'
import '../../components/ChatHead.js' import '../../components/ChatHead.js'
import '../../components/ChatPage.js' import '../../components/ChatPage.js'
import '../../components/WrapperModal.js';
import '../../components/ChatSeachResults.js';
import '../../components/ChatGroupsManagement.js' import '../../components/ChatGroupsManagement.js'
import snackbar from '../../components/snackbar.js' import snackbar from '../../components/snackbar.js'
import '@polymer/paper-spinner/paper-spinner-lite.js' import '@polymer/paper-spinner/paper-spinner-lite.js'
@ -36,272 +40,18 @@ class Chat extends LitElement {
theme: { type: String, reflect: true }, theme: { type: String, reflect: true },
blockedUsers: { type: Array }, blockedUsers: { type: Array },
blockedUserList: { type: Array }, blockedUserList: { type: Array },
activeChatHeadUrl: {type: String} privateMessagePlaceholder: { type: String},
chatEditor: { type: Object },
imageFile: { type: Object },
activeChatHeadUrl: { type: String },
openPrivateMessage: { type: Boolean },
userFound: { type: Array},
userFoundModalOpen: { type: Boolean },
userSelected: { type: Object }
} }
} }
static get styles() { static styles = [qchatStyles]
return css`
* {
--mdc-theme-primary: rgb(3, 169, 244);
--mdc-theme-secondary: var(--mdc-theme-primary);
--paper-input-container-focus-color: var(--mdc-theme-primary);
--mdc-theme-surface: var(--white);
--mdc-dialog-content-ink-color: var(--black);
--lumo-primary-text-color: rgb(0, 167, 245);
--lumo-primary-color-50pct: rgba(0, 167, 245, 0.5);
--lumo-primary-color-10pct: rgba(0, 167, 245, 0.1);
--lumo-primary-color: hsl(199, 100%, 48%);
--lumo-base-color: var(--white);
--lumo-body-text-color: var(--black);
--_lumo-grid-border-color: var(--border);
--_lumo-grid-secondary-border-color: var(--border2);
--mdc-dialog-min-width: 750px;
}
paper-spinner-lite {
height: 24px;
width: 24px;
--paper-spinner-color: var(--mdc-theme-primary);
--paper-spinner-stroke-width: 2px;
}
*,
*:before,
*:after {
box-sizing: border-box;
}
ul {
list-style: none;
padding: 0;
}
.container {
margin: 0 auto;
width: 100%;
background: var(--white);
}
.people-list {
width: 20vw;
float: left;
height: 100vh;
overflow-y: hidden;
border-right: 3px #ddd solid;
}
.people-list .blockedusers {
position: absolute;
bottom: 0;
width: 20vw;
height: 60px;
background: var(--white);
border-top: 1px solid var(--border);
border-right: 3px #ddd solid;
}
.people-list .search {
padding-top: 20px;
padding-left: 20px;
padding-right: 20px;
}
.center {
margin: 0;
position: absolute;
padding-top: 12px;
left: 50%;
-ms-transform: translateX(-50%);
transform: translateX(-50%);
}
.people-list .create-chat {
border-radius: 5px;
border: none;
display: inline-block;
padding: 14px;
color: #fff;
background: var(--tradehead);
width: 100%;
font-size: 15px;
text-align: center;
cursor: pointer;
}
.people-list .create-chat:hover {
opacity: .8;
box-shadow: 0 3px 5px rgba(0, 0, 0, .2);
}
.people-list ul {
padding: 0;
height: 85vh;
overflow-y: auto;
overflow-x: hidden;
}
.chat {
width: 80vw;
height: 100vh;
float: left;
background: var(--white);
border-top-right-radius: 5px;
border-bottom-right-radius: 5px;
color: #434651;
box-sizing: border-box;
}
.chat .new-message-bar {
display: flex;
flex: 0 1 auto;
align-items: center;
justify-content: space-between;
padding: 0px 25px;
font-size: 14px;
font-weight: 500;
top: 0;
position: absolute;
left: 20vw;
right: 0;
z-index: 5;
background: var(--tradehead);
color: var(--white);
border-radius: 0 0 8px 8px;
min-height: 25px;
transition: opacity .15s;
text-transform: capitalize;
opacity: .85;
cursor: pointer;
}
.chat .new-message-bar:hover {
opacity: .75;
transform: translateY(-1px);
box-shadow: 0 3px 7px rgba(0, 0, 0, .2);
}
.hide-new-message-bar {
display: none !important;
}
.chat .chat-history {
position: absolute;
top: 0;
right: 0;
bottom: 100%;
left: 20vw;
border-bottom: 2px solid var(--white);
overflow-y: hidden;
height: 100vh;
box-sizing: border-box;
}
.chat .chat-message {
padding: 10px;
height: 10%;
display: inline-block;
width: 100%;
background-color: #eee;
}
.chat .chat-message textarea {
width: 90%;
border: none;
font-size: 16px;
padding: 10px 20px;
border-radius: 5px;
resize: none;
}
.chat .chat-message button {
float: right;
color: #94c2ed;
font-size: 16px;
text-transform: uppercase;
border: none;
cursor: pointer;
font-weight: bold;
background: #f2f5f8;
padding: 10px;
margin-top: 4px;
margin-right: 4px;
}
.chat .chat-message button:hover {
color: #75b1e8;
}
.online,
.offline,
.me {
margin-right: 3px;
font-size: 10px;
}
.clearfix:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
.red {
--mdc-theme-primary: red;
}
h2 {
margin:0;
}
h2, h3, h4, h5 {
color: var(--black);
font-weight: 400;
}
[hidden] {
display: hidden !important;
visibility: none !important;
}
.details {
display: flex;
font-size: 18px;
}
.title {
font-weight:600;
font-size:12px;
line-height: 32px;
opacity: 0.66;
}
.input {
width: 100%;
border: none;
display: inline-block;
font-size: 16px;
padding: 10px 20px;
border-radius: 5px;
resize: none;
background: #eee;
}
.textarea {
width: 100%;
border: none;
display: inline-block;
font-size: 16px;
padding: 10px 20px;
border-radius: 5px;
height: 120px;
resize: none;
background: #eee;
}
`
}
constructor() { constructor() {
super() super()
@ -321,14 +71,21 @@ class Chat extends LitElement {
this.messages = [] this.messages = []
this.btnDisable = false this.btnDisable = false
this.isLoading = false this.isLoading = false
this.showNewMesssageBar = this.showNewMesssageBar.bind(this) this.showNewMessageBar = this.showNewMessageBar.bind(this)
this.hideNewMesssageBar = this.hideNewMesssageBar.bind(this) this.hideNewMessageBar = this.hideNewMessageBar.bind(this)
this.setOpenPrivateMessage = this.setOpenPrivateMessage.bind(this)
this._sendMessage = this._sendMessage.bind(this)
this.insertImage = this.insertImage.bind(this)
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
this.blockedUsers = [] this.blockedUsers = []
this.blockedUserList = [] this.blockedUserList = []
this.privateMessagePlaceholder = ""
this.imageFile = null
this.activeChatHeadUrl = '' this.activeChatHeadUrl = ''
this.openPrivateMessage = false
this.userFound = []
this.userFoundModalOpen = false
this.userSelected = {}
} }
async setActiveChatHeadUrl(url) { async setActiveChatHeadUrl(url) {
@ -342,7 +99,10 @@ class Chat extends LitElement {
<div class="container clearfix"> <div class="container clearfix">
<div class="people-list" id="people-list"> <div class="people-list" id="people-list">
<div class="search"> <div class="search">
<div class="create-chat" @click=${() => this.shadowRoot.querySelector('#startChatDialog').show()}>${translate("chatpage.cchange1")}</div> <div class="create-chat" @click=${() => {
this.openPrivateMessage = true;
}}>${translate("chatpage.cchange1")}
</div>
<chat-groups-management></chat-groups-management> <chat-groups-management></chat-groups-management>
</div> </div>
<ul class="list"> <ul class="list">
@ -367,35 +127,113 @@ class Chat extends LitElement {
</div> </div>
<!-- Start Chatting Dialog --> <!-- Start Chatting Dialog -->
<mwc-dialog id="startChatDialog" scrimClickAction="${this.isLoading ? '' : 'close'}"> <wrapper-modal
<div style="text-align:center"> .onClickFunc=${() => {
this.chatEditor.resetValue();
this.openPrivateMessage = false;
this.shadowRoot.getElementById('sendTo').value = "";
this.userFoundModalOpen = false;
this.userFound = [];
} }
style=${this.openPrivateMessage ? "display: block" : "display: none"}>
<div style=${"position: relative"}>
<div class="dialog-container">
<div class="dialog-header" style="text-align: center">
<h1>${translate("chatpage.cchange1")}</h1> <h1>${translate("chatpage.cchange1")}</h1>
<hr> <hr>
</div> </div>
<p class="dialog-subheader">${translate("chatpage.cchange6")}</p>
<div class="search-field">
<input
type="text"
class="name-input"
?disabled=${this.isLoading}
id="sendTo"
placeholder="${translate("chatpage.cchange7")}"
value=${this.userSelected.name}
@keypress=${() => {
this.userSelected = {};
this.requestUpdate();
}}
/>
${this.userSelected.name ? (
html`
<div class="user-verified">
<p >${translate("chatpage.cchange38")}</p>
<vaadin-icon icon="vaadin:check-circle-o" slot="icon"></vaadin-icon>
</div>
`
) : (
html`
<vaadin-icon
@click=${this.userSearch}
slot="icon"
icon="vaadin:open-book"
class="search-icon">
</vaadin-icon>
`
)}
</div>
<p>${translate("chatpage.cchange6")}</p> <chat-text-editor
iframeId="privateMessage"
<textarea class="input" ?disabled=${this.isLoading} id="sendTo" placeholder="${translate("chatpage.cchange7")}" rows="1"></textarea> ?hasGlobalEvents=${false}
<p style="margin-bottom:0;"> placeholder="${translate("chatpage.cchange8")}"
<textarea class="textarea" @keydown=${(e) => this._textArea(e)} ?disabled=${this.isLoading} id="messageBox" placeholder="${translate("chatpage.cchange8")}" rows="1"></textarea> .setChatEditor=${(editor)=> this.setChatEditor(editor)}
</p> .chatEditor=${this.chatEditor}
.imageFile=${this.imageFile}
<mwc-button ._sendMessage=${this._sendMessage}
?disabled="${this.isLoading}" .insertImage=${this.insertImage}
slot="primaryAction" ?isLoading=${this.isLoading}
@click=${this._sendMessage} .isLoadingMessages=${false}
id="messageBox"
> >
${this.isLoading === false ? this.renderSendText() : html`<paper-spinner-lite active></paper-spinner-lite>`} </chat-text-editor>
</mwc-button> <div class="modal-button-row">
<mwc-button <button
class="modal-button-red"
@click=${() => {
this.chatEditor.resetValue();
this.openPrivateMessage = false;
}}
?disabled="${this.isLoading}" ?disabled="${this.isLoading}"
slot="secondaryAction"
dialogAction="cancel"
class="red"
> >
${translate("general.close")} ${translate("chatpage.cchange33")}
</mwc-button> </button>
</mwc-dialog> <button
class="modal-button"
@click=${()=> {
this.chatEditor.updateMirror()
this._sendMessage()
}}
?disabled="${this.isLoading}">
${this.isLoading === false
? this.renderSendText()
: html`
<paper-spinner-lite active></paper-spinner-lite>
`}
</button>
</div>
</div>
<div class="search-results-div">
<chat-search-results
.onClickFunc=${(result) => {
this.userSelected = result;
this.userFound = [];
this.userFoundModalOpen = false;
}}
.closeFunc=${() => {
this.userFoundModalOpen = false;
this.userFound = [];
}}
.searchResults=${this.userFound}
?isOpen=${this.userFoundModalOpen}
?loading=${this.isLoading}>
</chat-search-results>
</div>
</div>
</wrapper-modal>
<!-- Blocked User Dialog --> <!-- Blocked User Dialog -->
<mwc-dialog id="blockedUserDialog"> <mwc-dialog id="blockedUserDialog">
@ -447,9 +285,24 @@ class Chat extends LitElement {
return false; return false;
} }
this.shadowRoot.getElementById('sendTo').addEventListener('keydown', stopKeyEventPropagation); const nameInput = this.shadowRoot.getElementById('sendTo');
nameInput.addEventListener('keydown', stopKeyEventPropagation);
this.shadowRoot.getElementById('messageBox').addEventListener('keydown', stopKeyEventPropagation); this.shadowRoot.getElementById('messageBox').addEventListener('keydown', stopKeyEventPropagation);
// let typingTimer;
// let doneTypingInterval = 3000;
// //on keyup, start the countdown
// nameInput.addEventListener('keyup', () => {
// clearTimeout(typingTimer);
// if (nameInput.value) {
// console.log("typing started!");
// typingTimer = setTimeout(this.userSearch, doneTypingInterval);
// }
// });
const getDataFromURL = () => { const getDataFromURL = () => {
let tempUrl = document.location.href let tempUrl = document.location.href
let splitedUrl = decodeURI(tempUrl).split('?') let splitedUrl = decodeURI(tempUrl).split('?')
@ -549,6 +402,202 @@ class Chat extends LitElement {
parentEpml.imReady() parentEpml.imReady()
} }
setOpenPrivateMessage(props) {
this.openPrivateMessage = props.open;
this.shadowRoot.getElementById("sendTo").value = props.name
}
async userSearch() {
const nameValue = this.shadowRoot.getElementById('sendTo').value;
if(!nameValue) {
this.userFound = [];
this.userFoundModalOpen = true;
return;
}
try {
const result = await parentEpml.request('apiCall', {
type: 'api',
url: `/names/${nameValue}`
})
if (result.error === 401) {
this.userFound = [];
} else {
this.userFound = [
...this.userFound,
result,
];
}
this.userFoundModalOpen = true;
} catch (error) {
console.error(error);
let err4string = get("chatpage.cchange35");
parentEpml.request('showSnackBar', `${err4string}`)
}
}
setChatEditor(editor) {
this.chatEditor = editor;
}
async _sendMessage() {
this.isLoading = true;
this.chatEditor.disable();
const messageText = this.chatEditor.mirror.value;
// Format and Sanitize Message
const sanitizedMessage = messageText.replace(/&nbsp;/gi, ' ').replace(/<br\s*[\/]?>/gi, '\n');
const trimmedMessage = sanitizedMessage.trim();
if (/^\s*$/.test(trimmedMessage)) {
this.isLoading = false;
this.chatEditor.enable();
} else {
const messageObject = {
messageText: trimmedMessage,
images: [''],
repliedTo: '',
version: 1
}
const stringifyMessageObject = JSON.stringify(messageObject)
this.sendMessage(stringifyMessageObject);
}
}
async sendMessage(messageText) {
this.isLoading = true;
const _recipient = this.shadowRoot.getElementById('sendTo').value;
let recipient;
const validateName = async (receiverName) => {
let myRes;
try {
let myNameRes = await parentEpml.request('apiCall', {
type: 'api',
url: `/names/${receiverName}`
});
if (myNameRes.error === 401) {
myRes = false;
} else {
myRes = myNameRes;
}
return myRes;
} catch (error) {
return "";
}
};
const myNameRes = await validateName(_recipient);
if (!myNameRes) {
recipient = _recipient;
} else {
recipient = myNameRes.owner;
};
const getAddressPublicKey = async () => {
let isEncrypted;
let _publicKey;
let addressPublicKey = await parentEpml.request('apiCall', {
type: 'api',
url: `/addresses/publickey/${recipient}`
})
if (addressPublicKey.error === 102) {
_publicKey = false;
let err4string = get("chatpage.cchange19");
parentEpml.request('showSnackBar', `${err4string}`);
this.chatEditor.enable();
this.isLoading = false;
} else if (addressPublicKey !== false) {
isEncrypted = 1;
_publicKey = addressPublicKey;
sendMessageRequest(isEncrypted, _publicKey);
} else {
let err4string = get("chatpage.cchange39");
parentEpml.request('showSnackBar', `${err4string}`);
this.chatEditor.enable();
this.isLoading = false;
}
};
let _reference = new Uint8Array(64);
window.crypto.getRandomValues(_reference);
let reference = window.parent.Base58.encode(_reference);
const sendMessageRequest = async (isEncrypted, _publicKey) => {
let chatResponse = await parentEpml.request('chat', {
type: 18,
nonce: this.selectedAddress.nonce,
params: {
timestamp: Date.now(),
recipient: recipient,
recipientPublicKey: _publicKey,
hasChatReference: 0,
message: messageText,
lastReference: reference,
proofOfWorkNonce: 0,
isEncrypted: 1,
isText: 1
}
});
_computePow(chatResponse);
};
const _computePow = async (chatBytes) => {
const difficulty = this.balance === 0 ? 12 : 8;
const path = window.parent.location.origin + '/memory-pow/memory-pow.wasm.full';
const worker = new WebWorker();
let nonce = null;
let chatBytesArray = null;
await new Promise((res, rej) => {
worker.postMessage({chatBytes, path, difficulty});
worker.onmessage = e => {
worker.terminate();
chatBytesArray = e.data.chatBytesArray;
nonce = e.data.nonce;
res();
}
});
let _response = await parentEpml.request('sign_chat', {
nonce: this.selectedAddress.nonce,
chatBytesArray: chatBytesArray,
chatNonce: nonce
});
getSendChatResponse(_response);
};
const getSendChatResponse = (response) => {
if (response === true) {
this.setActiveChatHeadUrl(`direct/${recipient}`);
this.shadowRoot.getElementById('sendTo').value = "";
this.openPrivateMessage = false;
this.chatEditor.resetValue();
} else if (response.error) {
parentEpml.request('showSnackBar', response.message);
} else {
let err2string = get("chatpage.cchange21");
parentEpml.request('showSnackBar', `${err2string}`);
}
this.isLoading = false;
this.chatEditor.enable();
};
// Exec..
getAddressPublicKey();
}
insertImage(file) {
if (file.type.includes('image')) {
this.imageFile = file;
this.chatEditor.disable();
return;
}
parentEpml.request('showSnackBar', get("chatpage.cchange28"));
}
renderLoadingText() { renderLoadingText() {
return html`${translate("chatpage.cchange2")}` return html`${translate("chatpage.cchange2")}`
} }
@ -684,7 +733,11 @@ class Chat extends LitElement {
} }
renderChatWelcomePage() { renderChatWelcomePage() {
return html`<chat-welcome-page myAddress=${JSON.stringify(this.selectedAddress)}></chat-welcome-page>` return html`
<chat-welcome-page
myAddress=${JSON.stringify(this.selectedAddress)}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}>
</chat-welcome-page>`
} }
renderChatHead(chatHeadArr) { renderChatHead(chatHeadArr) {
@ -704,7 +757,17 @@ class Chat extends LitElement {
// Else render Welcome to Q-CHat // Else render Welcome to Q-CHat
// TODO: DONE: Do the above in the ChatPage // TODO: DONE: Do the above in the ChatPage
return html`<chat-page .chatHeads=${this.chatHeads} .hideNewMesssageBar=${this.hideNewMesssageBar} .showNewMesssageBar=${this.showNewMesssageBar} myAddress=${window.parent.reduxStore.getState().app.selectedAddress.address} chatId=${this.activeChatHeadUrl} .setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}></chat-page>` return html`
<chat-page
.chatHeads=${this.chatHeads}
.hideNewMessageBar=${this.hideNewMessageBar}
.showNewMessageBar=${this.showNewMessageBar}
myAddress=${window.parent.reduxStore.getState().app.selectedAddress.address}
chatId=${this.activeChatHeadUrl}
.setOpenPrivateMessage=${(val) => this.setOpenPrivateMessage(val)}
.setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)}>
</chat-page>
`
} }
setChatHeads(chatObj) { setChatHeads(chatObj) {
@ -722,7 +785,6 @@ class Chat extends LitElement {
const compareArgs = (a, b) => { const compareArgs = (a, b) => {
return b.timestamp - a.timestamp return b.timestamp - a.timestamp
} }
this.chatHeads = chatHeadMasterList.sort(compareArgs) this.chatHeads = chatHeadMasterList.sort(compareArgs)
} }
@ -737,163 +799,6 @@ class Chat extends LitElement {
} }
} }
_sendMessage() {
this.isLoading = true
const recipient = this.shadowRoot.getElementById('sendTo').value
const messageBox = this.shadowRoot.getElementById('messageBox')
const messageText = messageBox.value
if (recipient.length === 0) {
this.isLoading = false
} else if (messageText.length === 0) {
this.isLoading = false
} else {
this.sendMessage()
}
}
async sendMessage(e) {
this.isLoading = true
const _recipient = this.shadowRoot.getElementById('sendTo').value
const messageBox = this.shadowRoot.getElementById('messageBox')
const messageText = messageBox.value
let recipient
const validateName = async (receiverName) => {
let myRes
let myNameRes = await parentEpml.request('apiCall', {
type: 'api',
url: `/names/${receiverName}`
})
if (myNameRes.error === 401) {
myRes = false
} else {
myRes = myNameRes
}
return myRes
}
const myNameRes = await validateName(_recipient)
if (!myNameRes) {
recipient = _recipient
} else {
recipient = myNameRes.owner
}
let _reference = new Uint8Array(64);
window.crypto.getRandomValues(_reference);
let sendTimestamp = Date.now()
let reference = window.parent.Base58.encode(_reference)
const getAddressPublicKey = async () => {
let isEncrypted
let _publicKey
let addressPublicKey = await parentEpml.request('apiCall', {
type: 'api',
url: `/addresses/publickey/${recipient}`
})
if (addressPublicKey.error === 102) {
_publicKey = false
let err4string = get("chatpage.cchange19")
parentEpml.request('showSnackBar', `${err4string}`)
this.isLoading = false
} else if (addressPublicKey !== false) {
isEncrypted = 1
_publicKey = addressPublicKey
sendMessageRequest(isEncrypted, _publicKey)
} else {
isEncrypted = 0
_publicKey = this.selectedAddress.address
sendMessageRequest(isEncrypted, _publicKey)
}
};
const sendMessageRequest = async (isEncrypted, _publicKey) => {
const messageObject = {
messageText,
images: [''],
repliedTo: '',
version: 1
}
const stringifyMessageObject = JSON.stringify(messageObject)
let chatResponse = await parentEpml.request('chat', {
type: 18,
nonce: this.selectedAddress.nonce,
params: {
timestamp: sendTimestamp,
recipient: recipient,
recipientPublicKey: _publicKey,
hasChatReference: 0,
message: stringifyMessageObject,
lastReference: reference,
proofOfWorkNonce: 0,
isEncrypted: isEncrypted,
isText: 1
}
})
_computePow(chatResponse)
}
const _computePow = async (chatBytes) => {
const _chatBytesArray = Object.keys(chatBytes).map(function (key) { return chatBytes[key]; });
const chatBytesArray = new Uint8Array(_chatBytesArray)
const chatBytesHash = new window.parent.Sha256().process(chatBytesArray).finish().result
const hashPtr = window.parent.sbrk(32, window.parent.heap);
const hashAry = new Uint8Array(window.parent.memory.buffer, hashPtr, 32)
hashAry.set(chatBytesHash);
const difficulty = this.balance === 0 ? 12 : 8
const workBufferLength = 8 * 1024 * 1024;
const workBufferPtr = window.parent.sbrk(workBufferLength, window.parent.heap)
let nonce = window.parent.computePow(hashPtr, workBufferPtr, workBufferLength, difficulty)
let _response = await parentEpml.request('sign_chat', {
nonce: this.selectedAddress.nonce,
chatBytesArray: chatBytesArray,
chatNonce: nonce
})
getSendChatResponse(_response)
}
const getSendChatResponse = (response) => {
if (response === true) {
messageBox.value = ""
let err5string = get("chatpage.cchange20")
parentEpml.request('showSnackBar', `${err5string}`)
this.isLoading = false
} else if (response.error) {
parentEpml.request('showSnackBar', response.message)
this.isLoading = false
} else {
let err6string = get("chatpage.cchange21")
parentEpml.request('showSnackBar', `${err6string}`)
this.isLoading = false
}
}
// Exec..
getAddressPublicKey()
}
_textMenu(event) { _textMenu(event) {
const getSelectedText = () => { const getSelectedText = () => {
@ -952,11 +857,11 @@ class Chat extends LitElement {
viewElement.scroll({ top: viewElement.scrollHeight, left: 0, behavior: 'smooth' }) viewElement.scroll({ top: viewElement.scrollHeight, left: 0, behavior: 'smooth' })
} }
showNewMesssageBar() { showNewMessageBar() {
this.shadowRoot.getElementById('newMessageBar').classList.remove('hide-new-message-bar') this.shadowRoot.getElementById('newMessageBar').classList.remove('hide-new-message-bar')
} }
hideNewMesssageBar() { hideNewMessageBar() {
this.shadowRoot.getElementById('newMessageBar').classList.add('hide-new-message-bar') this.shadowRoot.getElementById('newMessageBar').classList.add('hide-new-message-bar')
} }
} }

Loading…
Cancel
Save