Merge remote-tracking branch 'main/master' into feature/gif-repos-upadate

This commit is contained in:
Phillip 2023-02-17 00:30:31 +02:00
commit 2280447979
81 changed files with 18887 additions and 14606 deletions

View File

@ -27,9 +27,9 @@ Easiest way to install the lastest required packages on Linux is via nvm.
``` source ~/.profile ``` (For Debian based distro) <br/>
``` source ~/.bashrc ``` (For Fedora / CentOS) <br/>
``` nvm ls-remote ``` (Fetch list of available versions) <br/>
``` nvm install v16.17.1 ``` (LTS: Gallium supported by Electron) <br/>
``` nvm install v18.12.1 ``` (LTS: Hydrogen supported by Electron) <br/>
``` npm --location=global install yarn@1.22.19 ``` <br/>
``` npm --location=global install npm@9.2.0 ``` <br/>
``` npm --location=global install npm@9.4.2 ``` <br/>
On BSD do a ``` pkg_add node followed by npm install -g yarn ```

BIN
img/attachment-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
img/badges/Level-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
img/badges/level-10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

BIN
img/badges/level-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
img/badges/level-7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
img/badges/level-9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Актуализацията е готова за инсталиране",
"electron_translate_8": "Беше изтеглена нова версия на Qortal UI.",
"electron_translate_9": "Щракнете върху ИНСТАЛИРАНЕ СЕГА, за да приложите актуализация, МОЖЕ БИ ПО-КЪСНО, за да не актуализирате потребителския интерфейс.",
"electron_translate_10": "Грешка при актуализиране...",
"electron_translate_10": "Временна неуспешна актуализация, ще опитаме отново по-късно",
"electron_translate_11": "ИЗТЕГЛЕТЕ АКТУАЛИЗАЦИЯТА",
"electron_translate_12": "Ще бъде изтеглено ⌛️ във фонов режим!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Update zur Installation bereit",
"electron_translate_8": "Eine neue Qortal-UI-Version wurde heruntergeladen.",
"electron_translate_9": "Klicken Sie auf JETZT INSTALLIEREN, um das Update anzuwenden, VIELLEICHT SPÄTER, um die Benutzeroberfläche nicht zu aktualisieren.",
"electron_translate_10": "Fehler beim Aktualisieren...",
"electron_translate_10": "Vorübergehender Aktualisierungsfehler, wir versuchen es später erneut",
"electron_translate_11": "UPDATE HERUNTERLADEN",
"electron_translate_12": "Das Update wird im Hintergrund heruntergeladen ⌛️!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Update ready to install",
"electron_translate_8": "A new Qortal UI version has been downloaded.",
"electron_translate_9": "Click INSTALL NOW to apply update, MAYBE LATER to not update the UI.",
"electron_translate_10": "Error while Updating...",
"electron_translate_10": "Temporary update failure, will try again later",
"electron_translate_11": "DOWNLOAD UPDATE",
"electron_translate_12": "It will be downloaded ⌛️ in the background!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Actualización lista para instalar",
"electron_translate_8": "Se ha descargado una nueva versión de la interfaz de usuario de Qortal.",
"electron_translate_9": "Haz clic en INSTALAR AHORA para aplicar la actualización, QUIZÁS MÁS TARDE para no actualizar la interfaz de usuario".,
"electron_translate_10": "Error al actualizar...",
"electron_translate_10": "Error de actualización temporal, lo intentaré más tarde",
"electron_translate_11": "DESCARGAR ACTUALIZACIÓN",
"electron_translate_12": "¡Se descargará ⌛️ en segundo plano!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Mise à jour prête à installer",
"electron_translate_8": "Une nouvelle version de l'interface utilisateur de Qortal a été téléchargée.",
"electron_translate_9": "Cliquez sur INSTALLER MAINTENANT pour appliquer la mise à jour, PEUT-ÊTRE PLUS TARD pour ne pas mettre à jour l'interface utilisateur.",
"electron_translate_10": "Erreur lors de la mise à jour...",
"electron_translate_10": "Échec de la mise à jour temporaire, réessayera plus tard",
"electron_translate_11": "TÉLÉCHARGER LA MISE À JOUR",
"electron_translate_12": "Il sera téléchargé ⌛️ en arrière-plan !"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Aggiornamento pronto per l'installazione",
"electron_translate_8": "È stata scaricata una nuova versione dell'interfaccia utente di Qortal.",
"electron_translate_9": "Fai clic su INSTALLA ORA per applicare l'aggiornamento, FORSE DOPO per non aggiornare l'interfaccia utente.",
"electron_translate_10": "Errore durante l'aggiornamento...",
"electron_translate_10": "Errore di aggiornamento temporaneo, riproverà più tardi",
"electron_translate_11": "SCARICA AGGIORNAMENTO",
"electron_translate_12": "Verrà scaricato ⌛️ in background!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "업데이트 설치 준비 완료",
"electron_translate_8": "새로운 Qortal UI 버전이 다운로드되었습니다.",
"electron_translate_9": "업데이트를 적용하려면 지금 설치를 클릭하고 UI를 업데이트하지 않으려면 나중에 할 수도 있습니다.",
"electron_translate_10": "업데이트 중 오류...",
"electron_translate_10": "임시 업데이트 실패, 나중에 다시 시도합니다.",
"electron_translate_11": "업데이트 다운로드",
"electron_translate_12": "백그라운드에서 ⌛️ 다운로드됩니다!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Update klaar om te installeren",
"electron_translate_8": "Er is een nieuwe Qortal UI-versie gedownload.",
"electron_translate_9": "Klik op NU INSTALLEREN om de update toe te passen, MISSCHIEN LATER om de gebruikersinterface niet bij te werken.",
"electron_translate_10": "Fout tijdens updaten...",
"electron_translate_10": "Tijdelijke update mislukt, zal het later opnieuw proberen",
"electron_translate_11": "UPDATE DOWNLOADEN",
"electron_translate_12": "Het wordt ⌛️ op de achtergrond gedownload!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Atualização pronta para instalar",
"electron_translate_8": "Uma nova versão Qortal UI foi baixada.",
"electron_translate_9": "Clique em INSTALAR AGORA para aplicar a atualização, TALVEZ MAIS TARDE para não atualizar a UI.",
"electron_translate_10": "Erro ao atualizar...",
"electron_translate_10": "Falha de atualização temporária, tentará novamente mais tarde",
"electron_translate_11": "BAIXAR ATUALIZAÇÃO",
"electron_translate_12": "Será baixado ⌛️ em segundo plano!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Atualização pronta para instalar",
"electron_translate_8": "Uma nova versão Qortal UI foi baixada.",
"electron_translate_9": "Clique em INSTALAR AGORA para aplicar a atualização, TALVEZ MAIS TARDE para não atualizar a UI.",
"electron_translate_10": "Erro ao atualizar...",
"electron_translate_10": "Falha de atualização temporária, tentará novamente mais tarde",
"electron_translate_11": "BAIXAR ATUALIZAÇÃO",
"electron_translate_12": "Será baixado ⌛️ em segundo plano!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Обновление готово к установке",
"electron_translate_8": "Загружена новая версия пользовательского интерфейса Qortal.",
"electron_translate_9": "Нажмите УСТАНОВИТЬ СЕЙЧАС, чтобы применить обновление, МОЖЕТ БЫТЬ ПОЗЖЕ, чтобы не обновлять пользовательский интерфейс.",
"electron_translate_10": "Ошибка при обновлении...",
"electron_translate_10": "Временная ошибка обновления, повторите попытку позже",
"electron_translate_11": "СКАЧАТЬ ОБНОВЛЕНИЕ",
"electron_translate_12": "Он будет загружен ⌛️ в фоновом режиме!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Güncelleme yüklenmeye hazır",
"electron_translate_8": "Yeni bir Qortal UI sürümü indirildi.",
"electron_translate_9": "Güncellemeyi uygulamak için ŞİMDİ YÜKLE'ye tıklayın, kullanıcı arayüzünü güncellememek için MAYBE SONRA tıklayın.",
"electron_translate_10": "Güncelleme Sırasında Hata...",
"electron_translate_10": "Geçici güncelleme hatası, daha sonra tekrar denenecek",
"electron_translate_11": "GÜNCELLEMEYİ İNDİRİN",
"electron_translate_12": "Arka planda ⌛️ indirilecek!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Update ready to install",
"electron_translate_8": "A new Qortal UI version has been downloaded.",
"electron_translate_9": "Click INSTALL NOW to apply update, MAYBE LATER to not update the UI.",
"electron_translate_10": "Error while Updating...",
"electron_translate_10": "Temporary update failure, will try again later",
"electron_translate_11": "DOWNLOAD UPDATE",
"electron_translate_12": "It will be downloaded ⌛️ in the background!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "更新准备安装",
"electron_translate_8": "已下载新的 Qortal UI 版本。",
"electron_translate_9": "点击现在安装应用更新,可能稍后不更新用户界面。",
"electron_translate_10": "更新时出错...",
"electron_translate_10": "暂时更新失败,稍后再试",
"electron_translate_11": "下载更新",
"electron_translate_12": "它将在后台下载 ⌛️!"
}

View File

@ -10,7 +10,7 @@
"electron_translate_7": "更新準備安裝",
"electron_translate_8": "已下載新的 Qortal UI 版本。",
"electron_translate_9": "點擊現在安裝應用更新,可能稍後不更新用戶界面。",
"electron_translate_10": "更新時出錯...",
"electron_translate_10": "暫時更新失敗,稍後再試",
"electron_translate_11": "下載更新",
"electron_translate_12": "它將在後台下載 ⌛️!"
}

View File

@ -1,6 +1,6 @@
{
"name": "qortal-ui",
"version": "3.0.1",
"version": "3.1.0",
"description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet",
"keywords": [
"QORT",
@ -37,12 +37,12 @@
"os-locale": "3.0.0"
},
"devDependencies": {
"electron": "22.1.0",
"electron": "23.0.0",
"electron-builder": "23.6.0",
"electron-packager": "17.1.1",
"shelljs": "0.8.5"
},
"engines": {
"node": ">=16.17.1"
"node": ">=18.12.1"
}
}

Binary file not shown.

View File

@ -32,6 +32,12 @@
local('MavenPro'),
url(Montserrat.ttf) format('truetype');
}
@font-face {
font-family: 'WorkSans';
src: local('WorkSans'),
local('WorkSans'),
url(WorkSans.ttf) format('truetype');
}
@font-face {
font-family: 'Raleway';

View File

@ -9,7 +9,8 @@ html {
--copybutton: #707584;
--chat-group: #080808;
--chat-bubble: #9f9f9f0a;
--chat-bubble-bg: #f3f3f3;
--chat-bubble-bg: #e6e6e6;
--chat-bubble-myBg: #d1ddf2;
--chat-bubble-msg-color: #080808;
--reaction-bubble-outline: #6b6969;
--chat-menu-bg: #ffffff;
@ -73,6 +74,7 @@ html[theme="dark"] {
--chat-group: #ffffff;
--chat-bubble: #9694941a;
--chat-bubble-bg: #2d3749;
--chat-bubble-myBg: #40444d;
--chat-bubble-msg-color: #ffffff;
--reaction-bubble-outline: #ffffff;
--chat-menu-bg: #32394c;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "qortal-ui-core",
"version": "3.0.0",
"version": "3.1.0",
"description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet",
"keywords": [
"QORT",
@ -17,9 +17,9 @@
"author": "QORTAL <admin@qortal.org>",
"license": "GPL-3.0",
"dependencies": {
"@hapi/hapi": "21.2.1",
"@hapi/inert": "7.0.0",
"sass": "1.57.1"
"@hapi/hapi": "21.3.0",
"@hapi/inert": "7.0.1",
"sass": "1.58.1"
},
"devDependencies": {
"@babel/core": "7.20.12",
@ -59,27 +59,28 @@
"@rollup/plugin-node-resolve": "15.0.1",
"@rollup/plugin-replace": "5.0.2",
"@rollup/plugin-terser": "0.4.0",
"@vaadin/button": "23.3.6",
"@vaadin/grid": "23.3.6",
"@vaadin/icons": "23.3.6",
"@vaadin/password-field": "23.3.6",
"@vaadin/tooltip": "23.3.6",
"@vaadin/button": "23.3.7",
"@vaadin/grid": "23.3.7",
"@vaadin/icons": "23.3.7",
"@vaadin/password-field": "23.3.7",
"@vaadin/tooltip": "23.3.7",
"asmcrypto.js": "2.3.2",
"bcryptjs": "2.4.3",
"epml": "0.3.3",
"file-saver": "2.0.5",
"lit": "2.6.1",
"lit-translate": "2.0.1",
"localforage": "1.10.0",
"pwa-helpers": "0.9.1",
"redux": "4.2.0",
"redux": "4.2.1",
"redux-thunk": "2.4.2",
"rollup": "3.12.0",
"rollup": "3.15.0",
"rollup-plugin-node-globals": "1.4.0",
"rollup-plugin-progress": "1.1.2",
"rollup-plugin-scss": "3.0.0",
"rollup-plugin-web-worker-loader": "1.6.1"
},
"engines": {
"node": ">=16.17.1"
"node": ">=18.12.1"
}
}

View File

@ -18,6 +18,7 @@ class AppInfo extends connect(store)(LitElement) {
coreInfo: { type: Array },
nodeConfig: { type: Object },
pageUrl: { type: String },
publicizeAddress: { type: String },
theme: { type: String, reflect: true }
}
}
@ -95,6 +96,7 @@ class AppInfo extends connect(store)(LitElement) {
this.coreInfo = []
this.nodeStatus = {}
this.pageUrl = ''
this.publicizeAddress = ''
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
this.publicKeyisOnChainConfirmation = false
this.interval
@ -112,24 +114,46 @@ class AppInfo extends connect(store)(LitElement) {
`
}
firstUpdated() {
this.publicizeAddress = store.getState().app.selectedAddress.address + '_publicize'
this.setStorage()
this.getNodeInfo()
this.getCoreInfo()
try {
this.confirmPublicKeyOnChain(store.getState().app.selectedAddress.address)
} catch (error) {
console.error(error)
}
setInterval(() => {
this.getNodeInfo()
this.getCoreInfo()
}, 30000)
}
setStorage() {
if (localStorage.getItem(this.publicizeAddress) === null) {
localStorage.setItem(this.publicizeAddress, 'false')
}
}
async confirmPublicKeyOnChain(address) {
const _computePow2 = async (chatBytes) => {
const difficulty = 14;
const difficulty = 14
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});
await new Promise((res, rej) => {
worker.postMessage({chatBytes, path, difficulty})
worker.onmessage = e => {
worker.terminate()
chatBytesArray = e.data.chatBytesArray
worker.terminate()
chatBytesArray = e.data.chatBytesArray
nonce = e.data.nonce
res()
}
})
})
let _response = await routes.sign_chat({
data: {
@ -137,93 +161,74 @@ class AppInfo extends connect(store)(LitElement) {
chatBytesArray: chatBytesArray,
chatNonce: nonce
},
});
return _response
};
})
return _response
}
let stop = false
const checkPublicKey = async () => {
if (!stop) {
stop = true;
try {
if(this.publicKeyisOnChainConfirmation){
clearInterval(this.interval)
return
}
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 = ''
let stop = false
const checkPublicKey = async () => {
if (!stop) {
stop = true
try {
data = await res.text();
} catch (error) {
data = {
error: 'error'
if(localStorage.getItem(this.publicizeAddress) === 'true') {
clearInterval(this.interval)
return
}
}
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(),
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
},
disableModal: true,
});
disableModal: true,
});
try {
const powRes = await _computePow2(chatRes)
if(powRes === true){
const powRes = await _computePow2(chatRes)
if(powRes === true) {
clearInterval(this.interval)
this.publicKeyisOnChainConfirmation = true
localStorage.removeItem(this.publicizeAddress)
localStorage.setItem(this.publicizeAddress, 'true')
}
} catch (error) {
console.error(error)
}
}
if (!data.error && data !== 'false' && data) {
clearInterval(this.interval)
this.publicKeyisOnChainConfirmation = true
}
if (!data.error && data !== 'false' && data) {
clearInterval(this.interval)
localStorage.removeItem(this.publicizeAddress)
localStorage.setItem(this.publicizeAddress, 'true')
}
} catch (error) {
}
stop = false
}
};
this.interval = setInterval(checkPublicKey, 5000);
}
firstUpdated() {
this.getNodeInfo()
this.getCoreInfo()
try {
this.confirmPublicKeyOnChain(store.getState().app.selectedAddress.address)
} catch (error) {
console.error(error)
}
setInterval(() => {
this.getNodeInfo()
this.getCoreInfo()
}, 30000)
} catch (error) {
}
stop = false
}
}
this.interval = setInterval(checkPublicKey, 5000);
}
async getNodeInfo() {

View File

@ -4,6 +4,11 @@ import { store } from '../store.js'
import { Epml } from '../epml.js'
import { addTradeBotRoutes } from '../tradebot/addTradeBotRoutes.js'
import { get, translate, translateUnsafeHTML } from 'lit-translate'
import localForage from "localforage";
const chatLastSeen = localForage.createInstance({
name: "chat-last-seen",
});
import '@polymer/paper-icon-button/paper-icon-button.js'
import '@polymer/paper-progress/paper-progress.js'
@ -27,6 +32,7 @@ import './user-info-view/user-info-view.js'
import '../functional-components/side-menu.js'
import '../functional-components/side-menu-item.js'
import './start-minting.js'
import { setChatLastSeen } from '../redux/app/app-actions.js'
const parentEpml = new Epml({type: 'WINDOW', source: window.parent})
@ -207,7 +213,7 @@ class AppView extends connect(store)(LitElement) {
#balanceheader {
flex: 0 0 24px;
padding: 12px 12px 20px 12px;
padding: 12px 12px 45px 12px;
border-bottom: 1px solid var(--border);
background: var(--sidetopbar);
}
@ -329,7 +335,16 @@ class AppView extends connect(store)(LitElement) {
.sideBarMenu::-webkit-scrollbar-thumb:hover {
background-color: rgb(148, 146, 146);
cursor: pointer;
}
}
.balanceButton {
background-color: #03a9f4;
color: #ffffff;
margin-left: 12px;
margin-right: 12px;
padding-top: 5px;
padding-bottom: 5px;
}
`
]
}
@ -426,6 +441,8 @@ class AppView extends connect(store)(LitElement) {
</side-menu>
</div>
</div>
<button class="balanceButton" @click="${() => this.shBalanceTicker()}">${translate("grouppage.gchange59")}</button>
<div id="theTicker" style="display: none; margin-bottom: 20px;">
<div id="balanceheader">
<span class="balanceheadertext">
${this.getAllBalancesLoading ? html`
@ -441,6 +458,7 @@ class AppView extends connect(store)(LitElement) {
</div>
${this.getAllBalancesLoading ? html`<paper-progress indeterminate style="width: 100%; margin: 4px;"></paper-progress>` : ''}
${this.balanceTicker}
</div>
<app-info></app-info>
</div>
</app-header-layout>
@ -1386,6 +1404,17 @@ class AppView extends connect(store)(LitElement) {
}
}
const getChatLastSeen=async() => {
let items = [];
await chatLastSeen.iterate(function(value, key, iterationNumber) {
items.push({key, timestamp: value});
})
store.dispatch(setChatLastSeen(items))
return items;
}
await getOpenTradesBTC()
await appDelay(1000)
await getOpenTradesLTC()
@ -1397,6 +1426,16 @@ class AppView extends connect(store)(LitElement) {
await getOpenTradesRVN()
await appDelay(1000)
await getOpenTradesARRR()
await getChatLastSeen()
}
shBalanceTicker() {
const targetDiv = this.shadowRoot.getElementById("theTicker")
if (targetDiv.style.display !== "none") {
targetDiv.style.display = "none";
} else {
targetDiv.style.display = "inline";
}
}
async getNodeType() {

View File

@ -14,8 +14,15 @@ import './login-section.js'
import '../qort-theme-toggle.js'
import settings from '../../functional-components/settings-page.js'
import { addAutoLoadImageChat, removeAutoLoadImageChat, addChatLastSeen } from '../../redux/app/app-actions.js'
window.reduxStore = store
window.reduxAction = {
addAutoLoadImageChat: addAutoLoadImageChat,
removeAutoLoadImageChat: removeAutoLoadImageChat,
addChatLastSeen: addChatLastSeen
}
const animationDuration = 0.7 // Seconds

View File

@ -95,12 +95,12 @@ class AccountView extends connect(store)(LitElement) {
getAvatar() {
let numberBlocks = (this.accountInfo.addressInfo.blocksMinted + this.accountInfo.addressInfo.blocksMintedAdjustment);
if (Number.isNaN(numberBlocks) || numberBlocks == "" || numberBlocks === null) {
return html`<img src="/img/noavatar.png" style="width:150px; height:150px;">`
return html`<img src="/img/noavatar.png" style="width:150px; height:150px; border-radius: 50%;">`
} else {
const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port
const url = `${avatarUrl}/arbitrary/THUMBNAIL/${this.accountInfo.names[0].name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
return html`<img src="${url}" style="width:150px; height:150px;" onerror="this.src='/img/noavatar.png';">`
return html`<img src="${url}" style="width:150px; height:150px; border-radius: 50%;" onerror="this.src='/img/noavatar.png';">`
}
}

View File

@ -1171,7 +1171,7 @@ class UserInfoView extends connect(store)(LitElement) {
</div>
</paper-dialog>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px;" id="userBoughtDialog" modal>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px;" id="userBoughtDialog">
<div class="card-explorer-container">
<div id="first-explorer-section">
${this.boughtBTCTemplate()}
@ -1191,7 +1191,7 @@ class UserInfoView extends connect(store)(LitElement) {
</div>
</paper-dialog>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px; overflow: auto;" id="userSoldDialog" modal>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px; overflow: auto;" id="userSoldDialog">
<div class="card-explorer-container">
<div id="first-explorer-section">
${this.soldBTCTemplate()}

View File

@ -2,7 +2,6 @@ import { store } from '../../store.js'
import { doPageUrl } from '../../redux/app/app-actions.js'
export const newMessage = (data) => {
const alert = playSound(data.sound)
// Should I show notification ?
@ -18,7 +17,7 @@ export const newMessage = (data) => {
}
notify.onclick = (e) => {
const pageUrl = `/app/q-chat/${data.req.url}`
const pageUrl = `/app/q-chat/?chat=${data.req.url}`
store.dispatch(doPageUrl(pageUrl))
}
} else {
@ -26,7 +25,7 @@ export const newMessage = (data) => {
const notify = new Notification(data.title, data.options)
notify.onclick = (e) => {
const pageUrl = `/app/q-chat/${data.req.url}`
const pageUrl = `/app/q-chat/?chat=${data.req.url}`
store.dispatch(doPageUrl(pageUrl))
}
}

View File

@ -9,6 +9,7 @@ const CHAT_HEADS_STREAM_NAME = 'chat_heads'
const NODE_CONFIG_STREAM_NAME = 'node_config'
const COPY_MENU_SWITCH = 'copy_menu_switch'
const FRAME_PASTE_MENU_SWITCH = 'frame_paste_menu_switch'
const CHAT_LAST_SEEN = 'chat_last_seen'
export const loggedInStream = new EpmlStream(LOGIN_STREAM_NAME, () => store.getState().app.loggedIn)
export const configStream = new EpmlStream(CONFIG_STREAM_NAME, () => store.getState().config)
@ -18,6 +19,7 @@ export const chatHeadsStateStream = new EpmlStream(CHAT_HEADS_STREAM_NAME, () =>
export const nodeConfigStream = new EpmlStream(NODE_CONFIG_STREAM_NAME, () => store.getState().app.nodeConfig)
export const copyMenuSwitchStream = new EpmlStream(COPY_MENU_SWITCH, () => store.getState().app.copyMenuSwitch)
export const framePasteMenuSwitchStream = new EpmlStream(FRAME_PASTE_MENU_SWITCH, () => store.getState().app.framePasteMenuSwitch)
export const chatLastSeenStream = new EpmlStream(CHAT_LAST_SEEN, () => store.getState().app.chatLastSeen)
let oldState = {
@ -46,6 +48,9 @@ store.subscribe(() => {
if (oldState.app.framePasteMenuSwitch !== state.app.framePasteMenuSwitch) {
framePasteMenuSwitchStream.emit(state.app.framePasteMenuSwitch)
}
if (oldState.app.chatLastSeen !== state.app.chatLastSeen) {
chatLastSeenStream.emit(state.app.chatLastSeen)
}
if (oldState.app.selectedAddress !== state.app.selectedAddress) {
selectedAddressStream.emit({

View File

@ -1,5 +1,5 @@
// Core App Actions here...
import { UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, CHAT_HEADS, ACCOUNT_INFO, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH } from '../app-action-types.js'
import { UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, CHAT_HEADS, ACCOUNT_INFO, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN } from '../app-action-types.js'
export const doUpdateBlockInfo = (blockObj) => {
return (dispatch, getState) => {
@ -105,3 +105,31 @@ const framePasteMenuSwitch = (payload) => {
payload
}
}
export const addAutoLoadImageChat = (payload) => {
return {
type: ADD_AUTO_LOAD_IMAGES_CHAT,
payload
}
}
export const removeAutoLoadImageChat = (payload) => {
return {
type: REMOVE_AUTO_LOAD_IMAGES_CHAT,
payload
}
}
export const setChatLastSeen = (payload) => {
return {
type: SET_CHAT_LAST_SEEN,
payload
}
}
export const addChatLastSeen = (payload) => {
return {
type: ADD_CHAT_LAST_SEEN,
payload
}
}

View File

@ -20,3 +20,7 @@ export const ADD_NEW_PLUGIN_URL = 'ADD_NEW_PLUGIN_URL'
export const COPY_MENU_SWITCH = 'COPY_MENU_SWITCH'
export const PASTE_MENU_SWITCH = 'PASTE_MENU_SWITCH'
export const FRAME_PASTE_MENU_SWITCH = 'FRAME_PASTE_MENU_SWITCH'
export const ADD_AUTO_LOAD_IMAGES_CHAT = 'ADD_AUTO_LOAD_IMAGES_CHAT'
export const REMOVE_AUTO_LOAD_IMAGES_CHAT = 'REMOVE_AUTO_LOAD_IMAGES_CHAT'
export const SET_CHAT_LAST_SEEN = 'SET_CHAT_LAST_SEEN'
export const ADD_CHAT_LAST_SEEN = 'ADD_CHAT_LAST_SEEN'

View File

@ -1,8 +1,14 @@
// Loading state, login state, isNavDrawOpen state etc. None of this needs to be saved to localstorage.
import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH } from './app-action-types.js'
import { loadStateFromLocalStorage, saveStateToLocalStorage } from '../../localStorageHelpers.js'
import { LOG_IN, LOG_OUT, NETWORK_CONNECTION_STATUS, INIT_WORKERS, ADD_PLUGIN_URL, ADD_PLUGIN, ADD_NEW_PLUGIN_URL, NAVIGATE, SELECT_ADDRESS, ACCOUNT_INFO, CHAT_HEADS, UPDATE_BLOCK_INFO, UPDATE_NODE_STATUS, UPDATE_NODE_INFO, LOAD_NODE_CONFIG, SET_NODE, ADD_NODE, PAGE_URL, COPY_MENU_SWITCH, PASTE_MENU_SWITCH, FRAME_PASTE_MENU_SWITCH, ADD_AUTO_LOAD_IMAGES_CHAT, REMOVE_AUTO_LOAD_IMAGES_CHAT, SET_CHAT_LAST_SEEN, ADD_CHAT_LAST_SEEN } from './app-action-types.js'
import { initWorkersReducer } from './reducers/init-workers.js'
import { loginReducer } from './reducers/login-reducer.js'
import { setNode, addNode } from './reducers/manage-node.js'
import localForage from "localforage";
const chatLastSeen = localForage.createInstance({
name: "chat-last-seen",
});
const INITIAL_STATE = {
loggedIn: false,
@ -42,7 +48,9 @@ const INITIAL_STATE = {
framePasteMenuSwitch: {
isOpen: false,
elementId: ''
}
},
autoLoadImageChats: loadStateFromLocalStorage('autoLoadImageChats') || [],
chatLastSeen: []
}
export default (state = INITIAL_STATE, action) => {
@ -146,6 +154,60 @@ export default (state = INITIAL_STATE, action) => {
...state,
framePasteMenuSwitch: action.payload
}
case ADD_AUTO_LOAD_IMAGES_CHAT: {
const findChat = state.autoLoadImageChats.findIndex((chat)=> chat === action.payload)
console.log({findChat})
if(findChat !== -1) return state
const updatedState = [...state.autoLoadImageChats, action.payload]
saveStateToLocalStorage('autoLoadImageChats', updatedState)
return {
...state,
autoLoadImageChats: updatedState
}
}
case REMOVE_AUTO_LOAD_IMAGES_CHAT: {
const updatedState = state.autoLoadImageChats.filter((chat)=> chat !== action.payload)
saveStateToLocalStorage('autoLoadImageChats', updatedState)
return {
...state,
autoLoadImageChats: updatedState
}
}
case SET_CHAT_LAST_SEEN: {
return {
...state,
chatLastSeen: action.payload
}
}
case ADD_CHAT_LAST_SEEN: {
const chatId = action.payload.key
const timestamp = action.payload.timestamp
if(!chatId || !timestamp) return state
let newChatLastSeen = [...state.chatLastSeen]
const findChatIndex = state.chatLastSeen.findIndex((chat)=> chat.key === chatId)
if(findChatIndex !== -1){
newChatLastSeen[findChatIndex] = {
key: chatId,
timestamp,
}
}
if(findChatIndex === -1){
newChatLastSeen = [...newChatLastSeen, {
key: chatId,
timestamp,
}]
}
chatLastSeen.setItem(chatId, timestamp)
return {
...state,
chatLastSeen: newChatLastSeen
}
}
default:
return state
}

View File

@ -9,7 +9,8 @@ html {
--copybutton: #707584;
--chat-group: #080808;
--chat-bubble: #9f9f9f0a;
--chat-bubble-bg: #f3f3f3;
--chat-bubble-bg: #e6e6e6;
--chat-bubble-myBg: #d1ddf2;
--chat-bubble-msg-color: #080808;
--reaction-bubble-outline: #6b6969;
--chat-menu-bg: #ffffff;
@ -70,6 +71,7 @@ html[theme="dark"] {
--chat-group: #ffffff;
--chat-bubble: #9694941a;
--chat-bubble-bg: #2d3749;
--chat-bubble-myBg: #40444d;
--chat-bubble-msg-color: #ffffff;
--reaction-bubble-outline: #ffffff;
--chat-menu-bg: #32394c;

View File

@ -159,7 +159,7 @@ const ADDRESS_VERSION = 58
const PROXY_URL = "/proxy/"
// Chat reference timestamp
const CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP = 0
const CHAT_REFERENCE_FEATURE_TRIGGER_TIMESTAMP = 1674316800000
// Used as a salt for all qora addresses. Salts used for storing your private keys in local storage will be randomly generated
const STATIC_SALT = new Uint8Array([54, 190, 201, 206, 65, 29, 123, 129, 147, 231, 180, 166, 171, 45, 95, 165, 78, 200, 208, 194, 44, 207, 221, 146, 45, 238, 68, 68, 69, 102, 62, 6])

View File

@ -0,0 +1,73 @@
'use strict'
import TransactionBase from '../TransactionBase.js'
import { QORT_DECIMALS } from '../../constants.js'
export default class UpdateNameTransaction extends TransactionBase {
constructor() {
super()
this.type = 4
}
render(html) {
return html`
${this._dialogUpdateName1}
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
<span style="color: #000;">${this.nameText}</span>
</div>
${this._dialogUpdateName2}
<div style="background: #eee; padding: 8px; margin: 8px 0; border-radius: 5px;">
<span style="color: #000;">${this.newNameText}</span>
</div>
${this._dialogUpdateName3}
`
}
set dialogUpdateName1(dialogUpdateName1) {
this._dialogUpdateName1 = dialogUpdateName1
}
set dialogUpdateName2(dialogUpdateName2) {
this._dialogUpdateName2 = dialogUpdateName2
}
set dialogUpdateName3(dialogUpdateName3) {
this._dialogUpdateName3 = dialogUpdateName3
}
set fee(fee) {
this._fee = fee * QORT_DECIMALS
this._feeBytes = this.constructor.utils.int64ToBytes(this._fee)
}
set name(name) {
this.nameText = name
this._nameBytes = this.constructor.utils.stringtoUTF8Array(name)
this._nameLength = this.constructor.utils.int32ToBytes(this._nameBytes.length)
}
set newName(newName) {
this.newNameText = newName
this._newNameBytes = this.constructor.utils.stringtoUTF8Array(newName)
this._newNameLength = this.constructor.utils.int32ToBytes(this._newNameBytes.length)
}
set newData(newData) {
this.newDataText = newData.length === 0 ? "Registered Name on the Qortal Chain" : newData
this._newDataBytes = this.constructor.utils.stringtoUTF8Array(this.newDataText)
this._newDataLength = this.constructor.utils.int32ToBytes(this._newDataBytes.length)
}
get params() {
const params = super.params
params.push(
this._nameLength,
this._nameBytes,
this._newNameLength,
this._newNameBytes,
this._newDataLength,
this._newDataBytes,
this._feeBytes
)
return params
}
}

View File

@ -1,5 +1,6 @@
import PaymentTransaction from './PaymentTransaction.js'
import RegisterNameTransaction from './names/RegisterNameTransaction.js'
import UpdateNameTransaction from './names/UpdateNameTransaction.js'
import SellNameTransacion from './names/SellNameTransacion.js'
import CancelSellNameTransacion from './names/CancelSellNameTransacion.js'
import BuyNameTransacion from './names/BuyNameTransacion.js'
@ -25,6 +26,7 @@ import TransferPrivsTransaction from './TransferPrivsTransaction.js'
export const transactionTypes = {
2: PaymentTransaction,
3: RegisterNameTransaction,
4: UpdateNameTransaction,
5: SellNameTransacion,
6: CancelSellNameTransacion,
7: BuyNameTransacion,

View File

@ -1,6 +1,6 @@
{
"name": "qortal-ui-crypto",
"version": "2.2.5",
"version": "3.1.0",
"description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet",
"keywords": [
"QORT",
@ -23,6 +23,6 @@
"lodash": "4.17.21"
},
"engines": {
"node": ">=16.17.1"
"node": ">=18.12.1"
}
}
}

View File

@ -1,6 +1,6 @@
{
"name": "qortal-ui-plugins",
"version": "3.0.0",
"version": "3.1.0",
"description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet",
"keywords": [
"QORT",
@ -20,27 +20,28 @@
"@lit-labs/motion": "1.0.3",
"@material/mwc-list": "0.27.0",
"@material/mwc-select": "0.27.0",
"@tiptap/core": "2.0.0-beta.209",
"@tiptap/extension-highlight": "2.0.0-beta.209",
"@tiptap/extension-image": "2.0.0-beta.209",
"@tiptap/extension-placeholder": "2.0.0-beta.209",
"@tiptap/extension-underline": "2.0.0-beta.209",
"@tiptap/html": "2.0.0-beta.209",
"@tiptap/starter-kit": "2.0.0-beta.209",
"@tiptap/pm": "2.0.0-beta.217",
"@tiptap/core": "2.0.0-beta.217",
"@tiptap/extension-highlight": "2.0.0-beta.217",
"@tiptap/extension-image": "2.0.0-beta.217",
"@tiptap/extension-placeholder": "2.0.0-beta.217",
"@tiptap/extension-underline": "2.0.0-beta.217",
"@tiptap/html": "2.0.0-beta.217",
"@tiptap/starter-kit": "2.0.0-beta.217",
"asmcrypto.js": "2.3.2",
"compressorjs": "1.1.1",
"emoji-picker-js": "https://github.com/Qortal/emoji-picker-js",
"localforage": "1.10.0",
"prosemirror-commands": "1.5.0",
"prosemirror-dropcursor": "1.6.1",
"prosemirror-dropcursor": "1.7.0",
"prosemirror-gapcursor": "1.3.1",
"prosemirror-history": "1.3.0",
"prosemirror-keymap": "1.2.0",
"prosemirror-model": "1.18.3",
"prosemirror-keymap": "1.2.1",
"prosemirror-model": "1.19.0",
"prosemirror-schema-list": "1.2.2",
"prosemirror-state": "1.4.2",
"prosemirror-transform": "1.7.0",
"prosemirror-view": "1.29.1",
"prosemirror-transform": "1.7.1",
"prosemirror-view": "1.30.1",
"short-unique-id": "4.4.4"
},
"devDependencies": {
@ -48,12 +49,12 @@
"@material/mwc-button": "0.27.0",
"@material/mwc-checkbox": "0.27.0",
"@material/mwc-dialog": "0.27.0",
"@material/mwc-fab": "0.27.0",
"@material/mwc-formfield": "0.27.0",
"@material/mwc-icon": "0.27.0",
"@material/mwc-icon-button": "0.27.0",
"@material/mwc-slider": "0.27.0",
"@material/mwc-snackbar": "0.27.0",
"@material/mwc-fab": "0.27.0",
"@material/mwc-tab": "0.27.0",
"@material/mwc-tab-bar": "0.27.0",
"@material/mwc-textfield": "0.27.0",
@ -70,11 +71,12 @@
"@rollup/plugin-node-resolve": "15.0.1",
"@rollup/plugin-replace": "5.0.2",
"@rollup/plugin-terser": "0.4.0",
"@vaadin/avatar": "23.3.6",
"@vaadin/button": "23.3.6",
"@vaadin/grid": "23.3.6",
"@vaadin/icons": "23.3.6",
"@vaadin/tooltip": "23.3.6",
"@vaadin/avatar": "23.3.7",
"@vaadin/button": "23.3.7",
"@vaadin/grid": "23.3.7",
"@vaadin/icons": "23.3.7",
"@vaadin/tooltip": "23.3.7",
"axios": "1.3.3",
"@zip.js/zip.js": "^2.6.62",
"epml": "0.3.3",
"file-saver": "2.0.5",
@ -83,13 +85,13 @@
"lit": "2.6.1",
"lit-translate": "2.0.1",
"passive-events-support": "1.0.33",
"rollup": "3.12.0",
"rollup": "3.15.0",
"rollup-plugin-node-globals": "1.4.0",
"rollup-plugin-progress": "1.1.2",
"rollup-plugin-web-worker-loader": "1.6.1",
"validator": "^13.7.0"
"rollup-plugin-web-worker-loader": "1.6.1"
},
"engines": {
"node": ">=16.17.1"
"node": ">=18.12.1"
}
}

View File

@ -1,11 +1,15 @@
import { LitElement, html, css } from 'lit'
import { render } from 'lit/html.js'
import { Epml } from '../../../epml.js'
import localForage from "localforage";
import { translate} from 'lit-translate';
import '@material/mwc-icon'
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
const chatLastSeen = localForage.createInstance({
name: "chat-last-seen",
});
class ChatHead extends LitElement {
static get properties() {
return {
@ -15,7 +19,8 @@ class ChatHead extends LitElement {
iconName: { type: String },
activeChatHeadUrl: { type: String },
isImageLoaded: { type: Boolean },
setActiveChatHeadUrl: {attribute: false}
setActiveChatHeadUrl: {attribute: false},
lastReadMessageTimestamp: {type: Number}
}
}
@ -24,9 +29,13 @@ class ChatHead extends LitElement {
li {
width: 100%;
padding: 7px 5px 7px 5px;
padding: 10px 5px 10px 5px;
cursor: pointer;
width: 100%;
box-sizing: border-box;
display: flex;
align-items: flex-start;
}
li:hover {
@ -44,12 +53,21 @@ class ChatHead extends LitElement {
color: var(--chat-group);
}
.about {
margin-top: 8px;
}
.about {
padding-left: 8px;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
margin: 0px;
}
.inner-container {
display: flex;
width: calc(100% - 45px);
flex-direction: column;
justify-content: center;
}
.status {
@ -64,6 +82,13 @@ class ChatHead extends LitElement {
clear: both;
height: 0;
}
.name {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
`
}
@ -82,9 +107,11 @@ class ChatHead extends LitElement {
this.activeChatHeadUrl = ''
this.isImageLoaded = false
this.imageFetches = 0
this.lastReadMessageTimestamp = 0
this.loggedInAddress = window.parent.reduxStore.getState().app.selectedAddress.address
}
createImage(imageUrl) {
createImage(imageUrl) {
const imageHTMLRes = new Image();
imageHTMLRes.src = imageUrl;
imageHTMLRes.style= "width:40px; height:40px; float: left; border-radius:50%";
@ -99,7 +126,7 @@ class ChatHead extends LitElement {
setTimeout(() => {
this.imageFetches = this.imageFetches + 1;
imageHTMLRes.src = imageUrl;
}, 500);
}, 750);
} else {
@ -109,32 +136,61 @@ class ChatHead extends LitElement {
return imageHTMLRes;
}
render() {
let avatarImg = '';
let backupAvatarImg = ''
let isUnread = false
if(this.chatInfo.name){
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 avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`;
avatarImg= this.createImage(avatarUrl)
}
if(this.lastReadMessageTimestamp && this.chatInfo.timestamp){
if(this.lastReadMessageTimestamp < this.chatInfo.timestamp){
isUnread = true
}
}
if(this.activeChatHeadUrl === this.chatInfo.url){
isUnread = false
}
if(this.chatInfo.sender === this.loggedInAddress){
isUnread = false
}
return html`
<li @click=${() => this.getUrl(this.chatInfo.url)} class="clearfix ${this.activeChatHeadUrl === this.chatInfo.url ? 'active' : ''}">
${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 ? 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>`: ''}
${!this.isImageLoaded && this.chatInfo.name ? html`<div style="width:40px; height:40px; flex-shrink: 0; 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; flex-shrink: 0; 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="inner-container">
<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> <mwc-icon style="float:right; padding: 0 1rem; color: var(--chat-group);">${this.chatInfo.groupId !== undefined ? 'lock_open' : 'lock'}</mwc-icon> </div>
<div class="name"><span style="font-weight: bold;float:left; padding-left: 8px; color: var(--chat-group);font-size:14px;word-break:${this.chatInfo.groupName ? this.chatInfo.groupName : this.chatInfo.name !== undefined ? 'break-word': 'break-all'}">${this.chatInfo.groupName ? this.chatInfo.groupName : this.chatInfo.name !== undefined ? this.chatInfo.name : this.chatInfo.address.substr(0, 15)} </span> <mwc-icon style="font-size:18px; color: var(--chat-group);">${this.chatInfo.groupId !== undefined ? 'lock_open' : 'lock'}</mwc-icon> </div>
</div>
<div class="about" style="margin-top:7px">
<div class="name"><span style="float:left; padding-left: 8px; color: var(--chat-group);font-size:14px"></span>
<div style="color: var(--black); display: flex;font-size: 12px; align-items:center">
<div style="width: 8px; height: 8px;border-radius: 50%;background: ${isUnread ? 'var(--error)' : 'none'} ; margin-right:5px;"></div>
<message-time style="display: ${(this.chatInfo.timestamp && this.chatInfo.timestamp > 100000) ? 'block' : 'none'}" timestamp=${this.chatInfo.timestamp}></message-time>
<span style="font-size:12px;color:var(--black);display: ${(!this.chatInfo.timestamp || this.chatInfo.timestamp > 100000) ? 'none' : 'block'}">${translate('chatpage.cchange90')}</span>
</div>
</div>
</div>
</div>
</li>
`
}
firstUpdated() {
async firstUpdated() {
let configLoaded = false
this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatInfo.url) || 0
parentEpml.ready().then(() => {
parentEpml.subscribe('selected_address', async selectedAddress => {
this.selectedAddress = {}
@ -142,6 +198,15 @@ class ChatHead extends LitElement {
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
this.selectedAddress = selectedAddress
})
parentEpml.subscribe('chat_last_seen', async chatList => {
const parsedChatList = JSON.parse(chatList)
const findChatSeen = parsedChatList.find(chat=> chat.key === this.chatInfo.url)
if(findChatSeen && this.lastReadMessageTimestamp !== findChatSeen.timestamp){
this.lastReadMessageTimestamp = findChatSeen.timestamp
this.requestUpdate()
}
})
parentEpml.subscribe('config', c => {
if (!configLoaded) {
configLoaded = true
@ -156,7 +221,18 @@ class ChatHead extends LitElement {
if(changedProperties.has('activeChatHeadUrl')){
return true
}
if(changedProperties.has('lastReadMessageTimestamp')){
return true
}
if(changedProperties.has('chatInfo')){
const prevChatInfo = changedProperties.get('chatInfo')
if(prevChatInfo.address !== this.chatInfo.address){
this.isImageLoaded = false
this.requestUpdate()
}
return true
}

File diff suppressed because it is too large Load Diff

View File

@ -77,17 +77,18 @@ export const chatStyles = css`
}
.message-data-my-name {
color: #cf21e8;
text-shadow: 0 0 3px #cf21e8;
color: #05be0e;
font-weight: bold;
}
.message-data-time {
color: #888888;
font-size: 13px;
user-select: none;
float: right;
padding-left: 15px;
text-align: right;
display: flex;
justify-content: space-between;
width: 100%;
padding-top: 2px;
}
.message-data-time-hidden {
@ -96,10 +97,10 @@ export const chatStyles = css`
color: #888888;
font-size: 13px;
user-select: none;
float: right;
padding-left: 15px;
padding-bottom: 3px;
text-align: right;
display: flex;
justify-content: space-between;
width: 100%;
padding-top: 2px;
}
.message-user-info {
@ -138,6 +139,11 @@ export const chatStyles = css`
min-width: 150px;
}
.message-myBg {
background-color: var(--chat-bubble-myBg) !important;
}
.message-triangle {
position: relative;
}
@ -154,10 +160,30 @@ export const chatStyles = css`
border-color: transparent transparent var(--chat-bubble-bg) transparent;
}
.message-myTriangle {
position: relative;
}
.message-myTriangle:after {
content: "";
position: absolute;
bottom: 0px;
left: -9px;
width: 0;
height: 0;
border-style: solid;
border-width: 0px 0px 7px 9px;
border-color: transparent transparent var(--chat-bubble-myBg) transparent;
}
.message-reactions {
background-color: transparent;
width: calc(100% - 54px);
margin-left: 54px;
background-color: transparent;
width: calc(100% - 54px);
margin-left: 54px;
display: flex;
flex-flow: row wrap;
justify-content: left;
gap: 8px;
}
.original-message {
@ -186,9 +212,7 @@ export const chatStyles = css`
}
.original-message-sender {
margin: 0 0 5px 0;
color: var(--mdc-theme-primary);
cursor: pointer;
}
.replied-message {
@ -199,6 +223,7 @@ export const chatStyles = css`
max-width: 300px;
max-height: 40px;
}
.replied-message p {
margin: 0px;
padding: 0px;
@ -393,11 +418,11 @@ export const chatStyles = css`
}
.reactions-bg {
position: relative;
background-color: #d5d5d5;
border-radius: 10px;
padding: 5px;
color: black;
margin-right: 10px;
transition: all 0.1s ease-in-out;
border: 0.5px solid transparent;
cursor: pointer;
@ -413,7 +438,7 @@ export const chatStyles = css`
.message-data-level {
height: 21px;
width: 21px;
width: auto;
overflow: hidden;
}
@ -422,6 +447,10 @@ export const chatStyles = css`
height: 40vh;
}
.hideImg {
visibility: hidden;
}
.image-deleted-msg {
font-family: Roboto, sans-serif;
font-size: 14px;
@ -617,6 +646,7 @@ export const chatStyles = css`
white-space: pre-wrap;
margin: 0px;
}
.replied-message pre code {
color: inherit;
padding: 0;
@ -624,12 +654,10 @@ export const chatStyles = css`
font-size: 0.8rem;
}
.replied-message img {
width: 1.7em;
height: 1.5em;
margin: 0px;
}
.replied-message blockquote {
@ -643,6 +671,86 @@ export const chatStyles = css`
margin: 2rem 0;
}
.attachment-container {
display: flex;
align-items: center;
justify-content: space-evenly;
padding: 5px 0 10px 0;
gap: 20px;
cursor: pointer;
}
.attachment-icon-container {
display: flex;
align-items: center;
justify-content: center;
height: 50px;
width: 50px;
border-radius: 50%;
border: none;
background-color: var(--mdc-theme-primary);
}
.attachment-icon {
width: 70%;
}
.attachment-info {
display: flex;
flex-direction: column;
gap: 5px;
}
.attachment-name {
font-family: Work Sans, sans-serif;
font-size: 16px;
color: var(--chat-bubble-msg-color);
margin: 0;
letter-spacing: 0.4px;
padding: 5px 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.attachment-size {
font-family: Roboto, sans-serif;
font-size: 16px;
color: var(--chat-bubble-msg-color);
margin: 0;
letter-spacing: 0.3px;
font-weight: 300;
}
.download-icon {
position: relative;
color: var(--chat-bubble-msg-color);
width: 19px;
background-color: transparent;
}
.download-icon:hover::before {
background-color: rgb(161 158 158 / 41%);
}
.download-icon::before {
content: "";
position: absolute;
border-radius: 50%;
padding: 18px;
background-color: transparent;
transition: all 0.3s ease-in-out;
}
.edited-message-style {
font-family: "Work Sans", sans-serif;
font-style: italic;
font-size: 13px;
visibility: visible;
}
.blink-bg{
border-radius: 8px;
animation: blinkingBackground 3s;
@ -652,4 +760,46 @@ export const chatStyles = css`
100% { background-color:rgba(var(--menuactivergb), 0)}
}
.smallLoading,
.smallLoading:after {
border-radius: 50%;
width: 2px;
height: 2px;
}
.smallLoading {
border-width: 0.8em;
border-style: solid;
border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2)
rgba(3, 169, 244, 0.2) rgb(3, 169, 244);
font-size: 30px;
position: relative;
text-indent: -9999em;
transform: translateZ(0px);
animation: 1.1s linear 0s infinite normal none running loadingAnimation;
}
@-webkit-keyframes loadingAnimation {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loadingAnimation {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
`

View File

@ -6,6 +6,7 @@ import {unsafeHTML} from 'lit/directives/unsafe-html.js';
import { chatStyles } from './ChatScroller-css.js'
import { Epml } from "../../../epml";
import { cropAddress } from "../../utils/cropAddress";
import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js';
import './LevelFounder.js';
import './NameMenu.js';
import './ChatModals.js';
@ -13,11 +14,14 @@ import './WrapperModal';
import "./UserInfo/UserInfo";
import '@vaadin/icons';
import '@vaadin/icon';
import '@vaadin/tooltip';
import '@material/mwc-button';
import '@material/mwc-dialog';
import '@material/mwc-icon';
import { EmojiPicker } from 'emoji-picker-js';
import { generateHTML } from '@tiptap/core'
import { generateHTML } from '@tiptap/core';
import { saveAs } from 'file-saver';
import axios from "axios";
import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline';
import Highlight from '@tiptap/extension-highlight'
@ -52,7 +56,8 @@ class ChatScroller extends LitElement {
userName: { type: String },
selectedHead: { type: Object },
goToRepliedMessage: { attribute: false },
getOldMessageAfter: {attribute: false}
getOldMessageAfter: {attribute: false},
listSeenMessages: {type: Array}
}
}
@ -67,6 +72,11 @@ class ChatScroller extends LitElement {
this.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]")
this.openTipUser = false;
this.openUserInfo = false;
this.listSeenMessages= []
}
addSeenMessage(val){
this.listSeenMessages.push(val)
}
render() {
@ -76,7 +86,6 @@ class ChatScroller extends LitElement {
let timestamp;
let sender;
let repliedToData;
let firstMessageInChat;
if (index === 0) {
@ -139,11 +148,14 @@ class ChatScroller extends LitElement {
.setUserName=${(val) => this.setUserName(val)}
id=${message.reference}
.goToRepliedMessage=${this.goToRepliedMessage}
.addSeenMessage=${(val)=> this.addSeenMessage(val)}
.listSeenMessages=${this.listSeenMessages}
chatId=${this.chatId}
>
</message-template>`
)
})}
<div id='downObserver'></div>
<div style=${"height: 1px;"} id='downObserver'></div>
</ul>
`
}
@ -273,6 +285,7 @@ class MessageTemplate extends LitElement {
openDialogImage: { type: Boolean },
openDialogGif: { type: Boolean },
openDeleteImage: { type: Boolean },
openDeleteAttachment: { type: Boolean },
isImageLoaded: { type: Boolean },
isGifLoaded: { type: Boolean },
isFirstMessage: { type: Boolean },
@ -287,6 +300,9 @@ class MessageTemplate extends LitElement {
setUserName: { attribute: false },
openTipUser:{ type: Boolean },
goToRepliedMessage: { attribute: false },
listSeenMessages: { type: Array },
addSeenMessage: { attribute: false },
chatId: { type: String },
}
}
@ -306,7 +322,7 @@ class MessageTemplate extends LitElement {
this.isFirstMessage = false
this.isSingleMessageInGroup = false
this.isLastMessageInGroup = false
this.viewImage = false
this.viewImage = false
}
static styles = [chatStyles]
@ -337,6 +353,36 @@ class MessageTemplate extends LitElement {
}
}
async downloadAttachment(attachment) {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
try{
axios.get(`${nodeUrl}/arbitrary/QCHAT_ATTACHMENT/${attachment.name}/${attachment.identifier}?apiKey=${myNode.apiKey}`, { responseType: 'blob'})
.then(response =>{
let filename = attachment.attachmentName;
let blob = new Blob([response.data], { type:"application/octet-stream" });
saveAs(blob , filename);
})
} catch (error) {
console.error(error);
}
}
firstUpdated(){
const autoSeeChatList = window.parent.reduxStore.getState().app?.autoLoadImageChats
if(autoSeeChatList.includes(this.chatId) || this.listSeenMessages.includes(this.messageObj.reference)){
this.viewImage = true
}
const tooltips = this.shadowRoot.querySelectorAll('vaadin-tooltip');
tooltips.forEach(tooltip => {
const overlay = tooltip.shadowRoot.querySelector('vaadin-tooltip-overlay');
overlay.shadowRoot.getElementById("overlay").style.cssText = "background-color: transparent; box-shadow: rgb(50 50 93 / 25%) 0px 2px 5px -1px, rgb(0 0 0 / 30%) 0px 1px 3px -1px";
overlay.shadowRoot.getElementById('content').style.cssText = "background-color: var(--reactions-tooltip-bg); color: var(--chat-bubble-msg-color); text-align: center; padding: 20px 10px; border-radius: 8px; font-family: Roboto, sans-serif; letter-spacing: 0.3px; font-weight: 300; font-size: 13.5px; transition: all 0.3s ease-in-out;";
});
}
render() {
const hidemsg = this.hideMessages;
let message = "";
@ -346,8 +392,11 @@ class MessageTemplate extends LitElement {
let image = null;
let gif = null;
let isImageDeleted = false;
let isAttachmentDeleted = false;
let version = 0;
let isForwarded = false
let isEdited = false
let attachment = null;
try {
const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage);
if (parsedMessageObj.version.toString() === '2' && parsedMessageObj.messageText) {
@ -365,9 +414,14 @@ class MessageTemplate extends LitElement {
message = parsedMessageObj.messageText;
repliedToData = this.messageObj.repliedToData;
isImageDeleted = parsedMessageObj.isImageDeleted;
isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted;
reactions = parsedMessageObj.reactions || [];
version = parsedMessageObj.version
isForwarded = parsedMessageObj.type === 'forward'
version = parsedMessageObj.version;
isForwarded = parsedMessageObj.type === 'forward';
isEdited = parsedMessageObj.isEdited && true;
if (parsedMessageObj.attachments && Array.isArray(parsedMessageObj.attachments) && parsedMessageObj.attachments.length > 0) {
attachment = parsedMessageObj.attachments[0];
}
if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) {
image = parsedMessageObj.images[0];
}
@ -389,15 +443,16 @@ class MessageTemplate extends LitElement {
let levelFounder = '';
let hideit = hidemsg.includes(this.messageObj.sender);
let forwarded = ''
let edited = ''
levelFounder = html`<level-founder checkleveladdress="${this.messageObj.sender}"></level-founder>`;
if (this.messageObj.senderName) {
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 avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`;
avatarImg = html`<img src="${avatarUrl}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null; this.src='/img/qortal-chat-logo.png';" />`;
avatarImg = html`<img src="${avatarUrl}" style="max-width:100%; max-height:100%;" onerror="this.onerror=null; this.src='/img/incognito.png';" />`;
} else {
avatarImg = html`<img src='/img/qortal-chat-logo.png' style="max-width:100%; max-height:100%;" onerror="this.onerror=null;" />`
avatarImg = html`<img src='/img/incognito.png' style="max-width:100%; max-height:100%;" onerror="this.onerror=null;" />`
}
const createImage = (imageUrl) => {
@ -415,15 +470,12 @@ class MessageTemplate extends LitElement {
setTimeout(() => {
this.imageFetches = this.imageFetches + 1;
imageHTMLRes.src = imageUrl;
}, 500);
}, 2000);
} else {
imageHTMLRes.src = '/img/chain.png';
imageHTMLRes.style= "max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5)";
imageHTMLRes.onclick= () => {
}
this.isImageLoaded = true
setTimeout(() => {
this.imageFetches = this.imageFetches + 1;
imageHTMLRes.src = imageUrl;
}, 6000);
}
};
return imageHTMLRes;
@ -460,7 +512,7 @@ class MessageTemplate extends LitElement {
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`;
if(this.viewImage || this.myAddress === this.messageObj.sender){
if (this.viewImage || this.myAddress === this.messageObj.sender) {
imageHTML = createImage(imageUrl);
imageHTMLDialog = createImage(imageUrl)
imageHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px";
@ -489,18 +541,23 @@ class MessageTemplate extends LitElement {
</span>
`;
edited = html`
<span class="edited-message-style">
${translate("chatpage.cchange68")}
</span>
`;
if (repliedToData) {
try {
const parsedMsg = JSON.parse(repliedToData.decodedMessage);
repliedToData.decodedMessage = parsedMsg;
} catch (error) {
console.error(error);
}
}
const escapedMessage = this.escapeHTML(message)
const replacedMessage = escapedMessage.replace(new RegExp('\r?\n','g'), '<br />');
return hideit ? html`<li class="clearfix"></li>` : html`
<li
class="clearfix message-parent"
@ -533,9 +590,12 @@ class MessageTemplate extends LitElement {
`}
<div
class="${`message-subcontainer2
${((this.isFirstMessage === true && this.isSingleMessageInGroup === false) ||
(this.isSingleMessageInGroup === true && this.isLastMessageInGroup === true)) &&
'message-triangle'}`}"
${this.myAddress === this.messageObj.sender && "message-myBg" }
${(((this.isFirstMessage === true && this.isSingleMessageInGroup === false) ||
(this.isSingleMessageInGroup === true && this.isLastMessageInGroup === true)) && this.myAddress !== this.messageObj.sender)
? 'message-triangle'
: (((this.isFirstMessage === true && this.isSingleMessageInGroup === false) ||
(this.isSingleMessageInGroup === true && this.isLastMessageInGroup === true)) && this.myAddress === this.messageObj.sender) ? "message-myTriangle" : null}`}"
style="${(this.isSingleMessageInGroup === true && this.isLastMessageInGroup === false) ? 'margin-bottom: 0;' : null}
${(this.isFirstMessage === false && this.isSingleMessageInGroup === true && this.isLastMessageInGroup === false)
? 'border-radius: 8px 25px 25px 8px;'
@ -580,58 +640,66 @@ class MessageTemplate extends LitElement {
${repliedToData && html`
<div class="original-message"
@click=${()=> {
this.goToRepliedMessage(repliedToData)
this.goToRepliedMessage(repliedToData, this.messageObj)
}}>
<p
class="original-message-sender">
<p
style=${"cursor: pointer; margin: 0 0 5px 0;"}
class=${this.myAddress !== repliedToData.sender
? "original-message-sender"
: "message-data-my-name"}>
${repliedToData.senderName ?? cropAddress(repliedToData.sender)}
</p>
<p class="replied-message">
${version.toString() === '1' ? html`
${repliedToData.decodedMessage.messageText}
` : ''}
${version.toString() === '2' ? html`
${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText, [
StarterKit,
Underline,
Highlight
// other extensions …
]))}
` : ''}
<!-- ${repliedToData.decodedMessage.messageText} -->
${version.toString() === '1' ? html`
${repliedToData.decodedMessage.messageText}
` : ''}
${version.toString() === '2' ? html`
${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText, [
StarterKit,
Underline,
Highlight
// other extensions …
]))}
`
: ''}
</p>
</div>
`}
${image && !isImageDeleted && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
${image && !isImageDeleted && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
<div
@click=${()=> {
this.viewImage = true
this.addSeenMessage(this.messageObj.reference)
}}
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")}
${translate("chatpage.cchange40")}
</div>
</div>
` : html``}
${!this.isImageLoaded && image && this.viewImage ? html`
<div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;position:absolute">
<div class=${`smallLoading`}></div>
</div>
`: ''}
${image && !isImageDeleted && (this.viewImage || this.myAddress === this.messageObj.sender) ? html`
<div
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : '', !this.isImageLoaded ? 'hideImg': ''].join(' ')}
style=${this.isFirstMessage && "margin-top: 10px;"}>
${imageHTML}<vaadin-icon
${imageHTML}
${this.myAddress === this.messageObj.sender ? html`
<vaadin-icon
@click=${() => {
this.openDeleteImage = true;
this.chatE
}}
class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon>
` : ''}
</div>
` : image && isImageDeleted ? html`
<p class="image-deleted-msg">This image has been deleted</p>
<p class="image-deleted-msg">${translate("chatpage.cchange80")}</p>
` : html``}
${gif && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
<div
@ -652,6 +720,51 @@ class MessageTemplate extends LitElement {
${gifHTML}
</div>
` : html``}
${attachment && !isAttachmentDeleted ?
html`
<div @click=${async () => await this.downloadAttachment(attachment)} class="attachment-container">
<div class="attachment-icon-container">
<img
src="/img/attachment-icon.png"
alt="attachment-icon"
class="attachment-icon" />
</div>
<div class="attachment-info">
<p class="attachment-name">
${attachment && attachment.attachmentName}
</p>
<p class="attachment-size">
${roundToNearestDecimal(attachment.attachmentSize)} mb
</p>
</div>
<vaadin-icon
icon="vaadin:download-alt"
slot="icon"
class="download-icon">
</vaadin-icon>
${this.myAddress === this.messageObj.sender
? html`
<vaadin-icon
@click=${(e) => {
e.stopPropagation();
this.openDeleteAttachment = true;
}}
class="image-delete-icon" icon="vaadin:close" slot="icon">
</vaadin-icon>
` : html``}
</div>
`
: attachment && isAttachmentDeleted ?
html`
<div class="attachment-container">
<div class="attachment-info">
<p style=${"font-style: italic;"} class="attachment-name">
${translate("chatpage.cchange82")}
</p>
</div>
</div>
`
: html``}
<div
id="messageContent"
class="message"
@ -662,7 +775,14 @@ class MessageTemplate extends LitElement {
${version.toString() === '1' ? html`
${unsafeHTML(this.emojiPicker.parse(replacedMessage))}
` : ''}
<div class="${((this.isFirstMessage === false &&
${version.toString() === '0' ? html`
${unsafeHTML(this.emojiPicker.parse(replacedMessage))}
` : ''}
<div
style=${isEdited
? "justify-content: space-between;"
: "justify-content: flex-end;"}
class="${((this.isFirstMessage === false &&
this.isSingleMessageInGroup === true &&
this.isLastMessageInGroup === true) ||
(this.isFirstMessage === true &&
@ -671,6 +791,14 @@ class MessageTemplate extends LitElement {
? 'message-data-time'
: 'message-data-time-hidden'
}">
${isEdited ?
html`
<span>
${edited}
</span>
`
: ''
}
<message-time timestamp=${this.messageObj.timestamp}></message-time>
</div>
</div>
@ -705,17 +833,66 @@ class MessageTemplate extends LitElement {
</div>
<div class="message-reactions" style="${reactions.length > 0 &&
'margin-top: 10px; margin-bottom: 5px;'}">
${reactions.map((reaction)=> {
${reactions.map((reaction, index)=> {
return html`
<span
@click=${() => this.sendMessage({
@click=${() => this.sendMessage({
type: 'reaction',
editedMessageObj: this.messageObj,
reaction: reaction.type,
})}
class="reactions-bg">
${reaction.type} ${reaction.qty}
</span>`
id=${`reactions-${index}`}
class="reactions-bg">
${reaction.type}
${reaction.qty}
<vaadin-tooltip
for=${`reactions-${index}`}
position="top"
hover-delay=${400}
hide-delay=${1}
text=${reaction.users.length > 3 ?
(
`${reaction.users[0].name
? reaction.users[0].name
: cropAddress(reaction.users[0].address)},
${reaction.users[1].name
? reaction.users[1].name
: cropAddress(reaction.users[1].address)},
${reaction.users[2].name
? reaction.users[2].name
: cropAddress(reaction.users[2].address)}
${get("chatpage.cchange71")} ${reaction.users.length - 3} ${get("chatpage.cchange72")}${(reaction.users.length - 3) > 1 ? html`${get("chatpage.cchange73")}` : ""} ${get("chatpage.cchange74")} ${reaction.type}`
) : reaction.users.length === 3 ?
(
`${reaction.users[0].name
? reaction.users[0].name
: cropAddress(reaction.users[0].address)},
${reaction.users[1].name
? reaction.users[1].name
: cropAddress(reaction.users[1].address)}
${get("chatpage.cchange71")}
${reaction.users[2].name
? reaction.users[2].name
: cropAddress(reaction.users[2].address)} ${get("chatpage.cchange74")} ${reaction.type}`
) : reaction.users.length === 2 ?
(
`${reaction.users[0].name
? reaction.users[0].name
: cropAddress(reaction.users[0].address)}
${get("chatpage.cchange71")}
${reaction.users[1].name
? reaction.users[1].name
: cropAddress(reaction.users[1].address)} ${get("chatpage.cchange74")} ${reaction.type}`
) : reaction.users.length === 1 ?
(
`${reaction.users[0].name
? reaction.users[0].name
: cropAddress(reaction.users[0].address)} ${get("chatpage.cchange74")} ${reaction.type}`
)
: "" }>
</vaadin-tooltip>
</span>
`
})}
</div>
</div>
@ -782,7 +959,7 @@ class MessageTemplate extends LitElement {
this.openDeleteImage = false;
}}>
<div class="delete-image-msg">
<p>Are you sure you want to delete this image?</p>
<p>${translate("chatpage.cchange78")}</p>
</div>
<div class="modal-button-row" @click=${() => this.openDeleteImage = false}>
<button class="modal-button-red">
@ -800,6 +977,34 @@ class MessageTemplate extends LitElement {
</button>
</div>
</mwc-dialog>
<mwc-dialog
hideActions
?open=${this.openDeleteAttachment}
@closed=${()=> {
this.openDeleteAttachment = false;
}}>
<div class="delete-image-msg">
<p>${translate("chatpage.cchange79")}</p>
</div>
<div class="modal-button-row" @click=${() => this.openDeleteAttachment = false}>
<button class="modal-button-red">
Cancel
</button>
<button
class="modal-button"
@click=${() => {
this.sendMessage({
type: 'deleteAttachment',
attachment: attachment,
name: attachment.name,
identifier: attachment.identifier,
editedMessageObj: this.messageObj,
})}
}>
Yes
</button>
</div>
</mwc-dialog>
`
}
}
@ -900,10 +1105,8 @@ class ChatMenu extends LitElement {
this.setForwardProperties(stringifyMessageObject)
} catch (error) {
console.log({error})
}
}
render() {
return html`
<div class="container">
@ -919,7 +1122,6 @@ class ChatMenu extends LitElement {
this.setToggledMessage(this.originalMessage)
this.emojiPicker.togglePicker(e.target)
} catch (error) {
console.log({error})
}
}}
@ -930,6 +1132,10 @@ class ChatMenu extends LitElement {
class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`}
data-text="${translate("blockpage.bcchange14")}"
@click="${() => {
if (this.version === '0') {
this.versionErrorSnack()
return
}
this.messageForwardFunc()
}}">
<vaadin-icon icon="vaadin:arrow-forward" slot="icon"></vaadin-icon>
@ -954,7 +1160,7 @@ class ChatMenu extends LitElement {
this.versionErrorSnack()
return
}
this.setRepliedToMessageObj(this.originalMessage);
this.setRepliedToMessageObj({...this.originalMessage, version: this.version});
}}">
<vaadin-icon icon="vaadin:reply" slot="icon"></vaadin-icon>
</div>

View File

@ -4,6 +4,8 @@ import { get, translate } from 'lit-translate';
import { EmojiPicker } from 'emoji-picker-js';
import { Epml } from '../../../epml.js';
import '@material/mwc-icon'
import '@material/mwc-checkbox'
// import { addAutoLoadImageChat } from "../../../../qortal-ui-core/src/redux/app/app-actions.js";
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent });
class ChatTextEditor extends LitElement {
@ -13,6 +15,8 @@ class ChatTextEditor extends LitElement {
isLoadingMessages: { type: Boolean },
_sendMessage: { attribute: false },
placeholder: { type: String },
attachment: { type: Object },
insertFile: { attribute: false },
imageFile: { type: Object },
insertImage: { attribute: false },
iframeHeight: { type: Number },
@ -31,7 +35,8 @@ class ChatTextEditor extends LitElement {
toggleEnableChatEnter: {attribute: false},
isEnabledChatEnter: {type: Boolean},
openGifModal: { type: Boolean },
setOpenGifModal: { attribute: false }
setOpenGifModal: { attribute: false },
chatId: {type: String}
}
}
@ -48,6 +53,11 @@ class ChatTextEditor extends LitElement {
overflow: hidden;
}
* {
--mdc-checkbox-unchecked-color: var(--black);
}
.chatbar-container {
width: 100%;
display: flex;
@ -261,6 +271,13 @@ class ChatTextEditor extends LitElement {
transition: all 0.2s;
display: none;
}
.removeBg {
background: none;
}
.chatbar-button-single label {
font-size: 13px;
}
.chatbar-button-single:hover {
filter: brightness(120%);
@ -299,7 +316,7 @@ class ChatTextEditor extends LitElement {
.ProseMirror {
width: 100%;
box-sizing: border-box;
word-break: break-all;
word-break: break-word;
}
.ProseMirror mark {
@ -341,7 +358,10 @@ class ChatTextEditor extends LitElement {
.hide-styling {
display: none;
}
mwc-checkbox::shadow .mdc-checkbox::after, mwc-checkbox::shadow .mdc-checkbox::before {
background-color:var(--mdc-theme-primary)
}
--mdc-checkbox-unchecked-color
`
}
@ -359,66 +379,93 @@ class ChatTextEditor extends LitElement {
}
render() {
return html`
<div
class=${["chatbar-container", "chatbar-buttons", this.iframeId !=="_chatEditorDOM" && 'hide-styling'].join(" ")}
style="align-items: center;">
class=${["chatbar-container", "chatbar-buttons", this.iframeId !=="_chatEditorDOM" && 'hide-styling'].join(" ")}
style="align-items: center;justify-content:space-between">
<div style="display: flex;align-items: center">
<button
@click=${() => this.editor.chain().focus().toggleBold().run()}
?disabled=${
this.editor &&
!this.editor.can()
.chain()
.focus()
.toggleBold()
.run()
}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('bold') ? 'is-active' : ''].join(" ")}>
<span class="material-symbols-outlined">&#xe238;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleItalic().run()}
?disabled=${ this.editor &&
!this.editor.can()
.chain()
.focus()
.toggleItalic()
.run()
}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('italic') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe23f;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleUnderline().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('underline') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe249;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleHighlight().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('highlight') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf82b;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleCodeBlock().run()}
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf84d;</span>
</button>
<button
@click=${()=> this.toggleEnableChatEnter() }
style="height: 26px; box-sizing: border-box;"
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
${this.isEnabledChatEnter ? html`
${translate("chatpage.cchange63")}
` : html`
${translate("chatpage.cchange64")}
`}
</button>
@click=${() => this.editor.chain().focus().toggleBold().run()}
?disabled=${
this.editor &&
!this.editor.can()
.chain()
.focus()
.toggleBold()
.run()
}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('bold') ? 'is-active' : ''].join(" ")}
>
<!-- <mwc-icon >format_bold</mwc-icon> -->
<span class="material-symbols-outlined">&#xe238;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleItalic().run()}
?disabled=${ this.editor &&
!this.editor.can()
.chain()
.focus()
.toggleItalic()
.run()
}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('italic') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe23f;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleUnderline().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('underline') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xe249;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleHighlight().run()}
class=${["chatbar-button-single", (this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('highlight') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf82b;</span>
</button>
<button
@click=${() => this.editor.chain().focus().toggleCodeBlock().run()}
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
<span class="material-symbols-outlined">&#xf84d;</span>
</button>
<button
@click=${()=> this.toggleEnableChatEnter() }
style="height: 26px; box-sizing: border-box;"
class=${["chatbar-button-single",(this.editedMessageObj || this.repliedToMessageObj || this.openGifModal) && 'show-chatbar-buttons', this.editor && this.editor.isActive('codeBlock') ? 'is-active' : ''].join(' ')}
>
${this.isEnabledChatEnter ? html`
${translate("chatpage.cchange63")}
` : html`
${translate("chatpage.cchange64")}
`}
</button>
</div>
${this.iframeId === "_chatEditorDOM" ? html`
<div
style="height: 26px; box-sizing: border-box"
class=${["chatbar-button-single", "removeBg"].join(' ')}
>
<label
for="qChatShowAutoMsg"
@click=${() => this.shadowRoot.getElementById('qChatShowAutoMsg').click()}
>${translate('chatpage.cchange69')}</label>
<mwc-checkbox style="margin-right: -15px;" id="qChatShowAutoMsg" @click=${e => {
console.log(e.target.checked)
if(e.target.checked){
window.parent.reduxStore.dispatch( window.parent.reduxAction.removeAutoLoadImageChat(this.chatId))
return
}
window.parent.reduxStore.dispatch( window.parent.reduxAction.addAutoLoadImageChat(this.chatId))
}} ?checked=${(window.parent.reduxStore.getState().app?.autoLoadImageChats || []).includes(this.chatId)}></mwc-checkbox>
</div>
` : ''}
</div>
<div
class=${["chatbar-container", (this.iframeId === "newChat" || this.iframeId === "privateMessage") ? "chatbar-caption" : ""].join(" ")}
@ -438,16 +485,19 @@ class ChatTextEditor extends LitElement {
</vaadin-icon>
<div class="file-picker-input-container">
<input
@change="${e => {
this.insertImage(e.target.files[0]);
const filePickerInput = this.shadowRoot.getElementById('file-picker')
if(filePickerInput){
filePickerInput.value = ""
}
}
}"
id="file-picker"
class="file-picker-input" type="file" name="myImage" accept="image/*" />
@change="${e => {
this.insertFile(e.target.files[0]);
const filePickerInput = this.shadowRoot.getElementById('file-picker');
if (filePickerInput) {
filePickerInput.value = "";
}
}
}"
id="file-picker"
class="file-picker-input"
type="file"
name="myImage"
accept="image/*, .doc, .docx, .pdf, .zip, .pdf, .txt, .odt, .ods, .xls, .xlsx, .ppt, .pptx" />
</div>
</div>
<textarea style="color: var(--black);" tabindex='1' ?autofocus=${true} ?disabled=${this.isLoading || this.isLoadingMessages} id="messageBox" rows="1"></textarea>
@ -495,7 +545,7 @@ class ChatTextEditor extends LitElement {
html`
<div
style="margin-bottom: 10px;
${this.iframeId === 'newChat'
${(this.iframeId === 'newChat' || this.iframeId === "newAttachmentChat")
? 'display: none;'
: 'display: flex;'}">
${this.isLoading === false ? html`
@ -518,8 +568,8 @@ class ChatTextEditor extends LitElement {
${this.chatMessageSize >= 750 ?
html`
<div class="message-size-container" style=${this.imageFile && "margin-top: 10px;"}>
<div class="message-size" style="${this.chatMessageSize > 1000 && 'color: #bd1515'}">
${`Your message size is of ${this.chatMessageSize} bytes out of a maximum of 1000`}
<div class="message-size" style="${this.chatMessageSize > 4000 && 'color: #bd1515'}">
${`Your message size is of ${this.chatMessageSize} bytes out of a maximum of 4000`}
</div>
</div>
` :
@ -607,9 +657,9 @@ class ChatTextEditor extends LitElement {
}
sendMessageFunc(props) {
if(this.editor.isEmpty) return
if(this.editor.isEmpty && this.iframeId !== 'newChat') return
this.getMessageSize(this.editor.getJSON())
if (this.chatMessageSize > 1000 ) {
if (this.chatMessageSize > 4000 ) {
parentEpml.request('showSnackBar', get("chatpage.cchange29"));
return;
}
@ -657,7 +707,20 @@ class ChatTextEditor extends LitElement {
repliedTo: '',
version: 2
};
} else {
} else if (this.attachment && this.iframeId === 'newAttachmentChat') {
messageObject = {
messageText: trimmedMessage,
attachments: [{
service: "QCHAT_ATTACHMENT",
name: '123456789123456789123456789',
identifier: '123456',
attachmentName: "123456789123456789123456789",
attachmentSize: "123456"
}],
repliedTo: '',
version: 2
};
} else {
messageObject = {
messageText: trimmedMessage,
images: [''],
@ -678,4 +741,4 @@ class ChatTextEditor extends LitElement {
}
window.customElements.define("chat-text-editor", ChatTextEditor)
window.customElements.define("chat-text-editor", ChatTextEditor)

View File

@ -63,6 +63,7 @@ class LevelFounder extends LitElement {
.message-data {
display: flex;
justify-content: center;
align-items: center;
gap: 5px;
}
@ -158,12 +159,12 @@ class LevelFounder extends LitElement {
renderLevel() {
let adresslevel = this.memberInfo.level;
return html `
return adresslevel ? html `
<img id="level-img" src=${`/img/badges/level-${adresslevel}.png`} alt=${`badge-${adresslevel}`} class="message-data-level" />
<paper-tooltip class="level-img-tooltip" for="level-img" position="top" >
${translate("mintingpage.mchange27")} ${adresslevel}
</paper-tooltip>
`
` : ''
}
_textMenu(event) {

View File

@ -19,7 +19,7 @@ class TimeAgo extends LitElement {
updated(changedProps) {
changedProps.forEach((OldProp, name) => {
if (name === 'timeIso') {
if (name === 'timeIso' || name === 'timestamp') {
this.renderTime(this.timestamp)
}
});
@ -35,7 +35,6 @@ class TimeAgo extends LitElement {
}
render() {
return html`
<time-ago datetime=${this.timeIso} format=${this.format}> </time-ago>
`

View File

@ -85,7 +85,7 @@ export class UserInfo extends LitElement {
${!this.isImageLoaded && this.selectedHead && !this.selectedHead.name ?
html`
<div class="avatar-container">
<img src="/img/qortal-chat-logo.png" alt="avatar" />
<img src="/img/incognito.png" alt="avatar" />
</div>`
: ""}
<div class="user-info-header">

View File

@ -22,6 +22,8 @@ import '@vaadin/icon'
import '@vaadin/icons'
import '@vaadin/grid'
import '@vaadin/grid/vaadin-grid-filter-column.js'
import '@vaadin/grid/vaadin-grid-sort-column.js'
import '@vaadin/text-field'
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
@ -33,6 +35,7 @@ class GroupManagement extends LitElement {
privateGroups: { type: Array },
joinedGroups: { type: Array },
groupInvites: { type: Array },
filteredItems: { type: Array },
privateGroupSearch: { type: Array },
newMembersList: { type: Array },
newAdminsList: { type: Array },
@ -111,6 +114,9 @@ class GroupManagement extends LitElement {
--_lumo-grid-secondary-border-color: var(--border2);
}
[part="input-field"] {
background-color: #fff;
}
#group-management-page {
background: var(--white);
padding: 12px 24px;
@ -437,6 +443,7 @@ class GroupManagement extends LitElement {
this.privateGroups = []
this.joinedGroups = []
this.groupInvites = []
this.filteredItems = []
this.privateGroupSearch = []
this.newMembersList = []
this.newAdminsList = []
@ -1314,8 +1321,8 @@ class GroupManagement extends LitElement {
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">${translate("grouppage.gchange3")}</h3>
<vaadin-grid theme="large" id="joinedGroupsGrid" ?hidden="${this.isEmptyArray(this.joinedGroups)}" .items="${this.joinedGroups}" aria-label="Joined Groups" all-rows-visible>
<vaadin-grid-column width="8rem" flex-grow="0" header="${translate("grouppage.gchange54")}" path="memberCount"></vaadin-grid-column>
<vaadin-grid-filter-column header="${translate("grouppage.gchange4")}" path="groupName"></vaadin-grid-filter-column>
<vaadin-grid-filter-column header="${translate("grouppage.gchange5")}" path="description"></vaadin-grid-filter-column>
<vaadin-grid-column header="${translate("grouppage.gchange4")}" path="groupName"></vaadin-grid-column>
<vaadin-grid-column header="${translate("grouppage.gchange5")}" path="description"></vaadin-grid-column>
<vaadin-grid-column width="11rem" flex-grow="0" header="${translate("grouppage.gchange6")}" .renderer=${(root, column, data) => {
render(html`${this.renderRole(data.item)}`, root)
}}></vaadin-grid-column>
@ -1359,11 +1366,25 @@ class GroupManagement extends LitElement {
<div class="divCard">
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">${translate("grouppage.gchange9")}</h3>
<vaadin-grid theme="large" id="publicGroupsGrid" ?hidden="${this.isEmptyArray(this.publicGroups)}" .items="${this.publicGroups}" aria-label="Public Open Groups" all-rows-visible>
<vaadin-grid-column width="8rem" flex-grow="0" header="${translate("grouppage.gchange54")}" path="memberCount"></vaadin-grid-column>
<vaadin-grid-filter-column header="${translate("grouppage.gchange4")}" path="groupName"></vaadin-grid-filter-column>
<vaadin-grid-filter-column header="${translate("grouppage.gchange5")}" path="description"></vaadin-grid-filter-column>
<vaadin-grid-filter-column header="${translate("grouppage.gchange10")}" path="owner"></vaadin-grid-filter-column>
<vaadin-text-field
placeholder="${translate("datapage.dchange4")}"
style="width: 25%; margin-bottom: 20px;"
clear-button-visible
@value-changed="${(e) => {
this.filteredItems = []
const searchTerm = (e.target.value || '').trim()
const keys = ['groupName', 'description', 'owner']
const filtered = this.publicGroups.filter((search) => keys.some((key) => search[key].toLowerCase().includes(searchTerm.toLowerCase())))
this.filteredItems = filtered
}}"
>
<vaadin-icon slot="prefix" icon="vaadin:search"></vaadin-icon>
</vaadin-text-field><br>
<vaadin-grid theme="large" id="publicGroupsGrid" .items="${this.filteredItems}" aria-label="Public Open Groups" all-rows-visible>
<vaadin-grid-sort-column width="8rem" flex-grow="0" header="${translate("grouppage.gchange54")}" path="memberCount"></vaadin-grid-sort-column>
<vaadin-grid-column header="${translate("grouppage.gchange4")}" path="groupName"></vaadin-grid-column>
<vaadin-grid-column header="${translate("grouppage.gchange5")}" path="description"></vaadin-grid-column>
<vaadin-grid-column header="${translate("grouppage.gchange10")}" path="owner"></vaadin-grid-column>
<vaadin-grid-column width="11rem" flex-grow="0" header="${translate("grouppage.gchange7")}" .renderer=${(root, column, data) => {
render(html`<mwc-button @click=${() => this.joinGroup(data.item)}><mwc-icon>queue</mwc-icon>&nbsp;${translate("grouppage.gchange51")}</mwc-button>`, root)
}}></vaadin-grid-column>
@ -1773,7 +1794,8 @@ class GroupManagement extends LitElement {
this.publicGroups = results
this.privateGroups = _privateGroups
this.joinedGroups = _joinedGroups
setTimeout(getOpen_JoinedGroups, 60000)
this.filteredItems = this.publicGroups
setTimeout(getOpen_JoinedGroups, 600000)
}
window.addEventListener("contextmenu", (event) => {
@ -1820,7 +1842,6 @@ class GroupManagement extends LitElement {
setTimeout(getGroupInvites, 1)
configLoaded = true
}
console.log('parse', JSON.parse(c))
this.config = JSON.parse(c)
})
parentEpml.subscribe('copy_menu_switch', async value => {
@ -3640,4 +3661,4 @@ class GroupManagement extends LitElement {
}
}
window.customElements.define('group-management', GroupManagement)
window.customElements.define('group-management', GroupManagement)

View File

@ -51,11 +51,11 @@ export const qchatStyles = css`
}
.people-list .blockedusers {
z-index: 1;
position: absolute;
bottom: 0;
width: 20vw;
background: var(--white);
border-top: 1px solid var(--border);
border-right: 3px #ddd solid;
display: flex;
justify-content: space-between;
@ -476,4 +476,4 @@ export const qchatStyles = css`
color: #04aa2e;
font-size: 13px;
}
`
`

View File

@ -1,9 +1,14 @@
import { LitElement, html, css } from 'lit';
import { render } from 'lit/html.js';
import { passiveSupport } from 'passive-events-support/src/utils'
passiveSupport({
events: ['touchstart']
})
import { Epml } from '../../../../epml.js';
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';
import {repeat} from 'lit/directives/repeat.js';
registerTranslateConfig({
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
@ -24,8 +29,9 @@ import '@vaadin/grid'
import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline';
import Placeholder from '@tiptap/extension-placeholder'
import { Editor, Extension } from '@tiptap/core'
import Highlight from '@tiptap/extension-highlight'
import { Editor, Extension } from '@tiptap/core'
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
class Chat extends LitElement {
@ -51,7 +57,7 @@ class Chat extends LitElement {
userFoundModalOpen: { type: Boolean },
userSelected: { type: Object },
editor: {type: Object},
groupInvites: { type: Array }
groupInvites: { type: Array },
}
}
@ -114,6 +120,7 @@ class Chat extends LitElement {
}
async connectedCallback() {
super.connectedCallback();
await this.getUpdateCompleteTextEditor();
@ -143,15 +150,36 @@ class Chat extends LitElement {
}})
]
})
this.unsubscribeStore = window.parent.reduxStore.subscribe(() => {
try {
if(window.parent.location && window.parent.location.search){
const queryString = window.parent.location.search;
const params = new URLSearchParams(queryString);
const chat = params.get("chat")
if(chat && chat !== this.activeChatHeadUrl){
let url = window.parent.location.href;
let newUrl = url.split("?")[0];
window.parent.history.pushState({}, "", newUrl);
this.setActiveChatHeadUrl(chat)
}
}
} catch (error) {
console.error(error)
}
});
}
disconnectedCallback() {
super.disconnectedCallback();
this.editor.destroy()
this.editor.destroy();
this.unsubscribeStore();
}
updatePlaceholder(editor, text){
editor.extensionManager.extensions.forEach((extension) => {
if (extension.name === "placeholder") {
@ -211,7 +239,7 @@ class Chat extends LitElement {
</div>
<div class="chat-history">
${window.parent.location.pathname !== "/app/q-chat" || this.activeChatHeadUrl ? html`${this.renderChatPage(this.chatId)}` : html`${this.renderChatWelcomePage()}`}
${this.activeChatHeadUrl ? html`${this.renderChatPage()}` : html`${this.renderChatWelcomePage()}`}
</div>
</div>
<!-- Start Chatting Dialog -->
@ -363,6 +391,8 @@ class Chat extends LitElement {
`
}
async firstUpdated() {
this.changeLanguage();
this.changeTheme();
@ -389,26 +419,7 @@ class Chat extends LitElement {
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 = () => {
let tempUrl = document.location.href
let splitedUrl = decodeURI(tempUrl).split('?')
let urlData = splitedUrl[1]
if (urlData !== undefined) {
this.chatId = urlData
}
}
const runFunctionsAfterPageLoad = () => {
// Functions to exec after render while waiting for page info...
@ -497,8 +508,11 @@ class Chat extends LitElement {
})
})
parentEpml.imReady()
}
setOpenPrivateMessage(props) {
this.openPrivateMessage = props.open;
this.shadowRoot.getElementById("sendTo").value = props.name
@ -526,7 +540,6 @@ class Chat extends LitElement {
}
this.userFoundModalOpen = true;
} catch (error) {
console.error(error);
let err4string = get("chatpage.cchange35");
parentEpml.request('showSnackBar', `${err4string}`)
}
@ -767,7 +780,6 @@ class Chat extends LitElement {
})
this.groupInvites = pendingGroupInvites;
} catch (error) {
console.error(error);
let err4string = get("chatpage.cchange61");
parentEpml.request('showSnackBar', `${err4string}`)
}
@ -846,17 +858,14 @@ class Chat extends LitElement {
}
renderChatHead(chatHeadArr) {
let tempUrl = document.location.href
let splitedUrl = decodeURI(tempUrl).split('?')
// let activeChatHeadUrl = splitedUrl[1] === undefined ? '' : splitedUrl[1]
return chatHeadArr.map(eachChatHead => {
return html`<chat-head activeChatHeadUrl=${this.activeChatHeadUrl} .setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)} chatInfo=${JSON.stringify(eachChatHead)}></chat-head>`
})
}
renderChatPage(chatId) {
renderChatPage() {
// Check for the chat ID from and render chat messages
// Else render Welcome to Q-CHat
@ -876,9 +885,10 @@ class Chat extends LitElement {
}
setChatHeads(chatObj) {
let groupList = chatObj.groups.map(group => group.groupId === 0 ? { groupId: group.groupId, url: `group/${group.groupId}`, groupName: "Qortal General Chat", timestamp: group.timestamp === undefined ? 2 : group.timestamp } : { ...group, timestamp: group.timestamp === undefined ? 1 : group.timestamp, url: `group/${group.groupId}` })
let directList = chatObj.direct.map(dc => {
const chatObjGroups = Array.isArray(chatObj.groups) ? chatObj.groups : [];
const chatObjDirect = Array.isArray(chatObj.direct) ? chatObj.direct : [];
let groupList = chatObjGroups.map(group => group.groupId === 0 ? { groupId: group.groupId, url: `group/${group.groupId}`, groupName: "Qortal General Chat", timestamp: group.timestamp === undefined ? 2 : group.timestamp, sender: group.sender } : { ...group, timestamp: group.timestamp === undefined ? 1 : group.timestamp, url: `group/${group.groupId}` })
let directList = chatObjDirect.map(dc => {
return { ...dc, url: `direct/${dc.address}` }
})
const compareNames = (a, b) => {
@ -971,4 +981,4 @@ class Chat extends LitElement {
}
}
window.customElements.define('q-chat', Chat)
window.customElements.define('q-chat', Chat)

View File

@ -243,7 +243,7 @@ class Websites extends LitElement {
<mwc-tab label="${translate("websitespage.schange2")}" icon="desktop_windows" @click="${(e) => this.displayTabContent('followed')}"></mwc-tab>
<mwc-tab label="${translate("websitespage.schange3")}" icon="block" @click="${(e) => this.displayTabContent('blocked')}"></mwc-tab>
</mwc-tab-bar>
<z id="tabs-1-content">
<div id="tabs-1-content">
<div id="tab-browse-content">
<div style="min-height:48px; display: flex; padding-bottom: 6px; margin: 2px;">
<h2 style="margin: 0; flex: 1; padding-top: .5em; display: inline;">${translate("websitespage.schange1")}</h2>
@ -703,7 +703,7 @@ class Websites extends LitElement {
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 url = `${nodeUrl}/arbitrary/THUMBNAIL/${name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}`
return html`<a class="visitSite" href="browser/index.html?name=${name}&service=${this.service}"><img src="${url}" onerror="this.src='/img/qortal-chat-logo.png';"></a>`
return html`<a class="visitSite" href="browser/index.html?name=${name}&service=${this.service}"><img src="${url}" onerror="this.src='/img/incognito.png';"></a>`
}
renderRelayModeText() {

View File

@ -534,7 +534,7 @@ class SponsorshipList extends LitElement {
${sponsorship?.name ? html`
<img src=${sponsorship.url}
class="avatar-img"
onerror="this.src='/img/qortal-chat-logo.png'"
onerror="this.src='/img/incognito.png'"
/>
` : ''}
${sponsorship?.name || sponsorship.address}

View File

@ -12,11 +12,13 @@ import '@material/mwc-textfield'
import '@material/mwc-icon'
import '@material/mwc-icon-button'
import '@material/mwc-dialog'
import '@material/mwc-fab'
import '@material/mwc-tab-bar'
import '@material/mwc-tab'
import '@material/mwc-list/mwc-list-item'
import '@material/mwc-select'
import '@polymer/iron-icons/iron-icons.js'
import '@polymer/paper-dialog/paper-dialog.js'
import '@polymer/paper-icon-button/paper-icon-button.js'
import '@polymer/paper-spinner/paper-spinner-lite.js'
import '@vaadin/grid'
@ -542,22 +544,47 @@ class TradeBotPortal extends LitElement {
opacity: 0;
}
}
paper-dialog.info {
width: 75%;
max-width: 75vw;
height: 50%;
max-height: 50vh;
background-color: var(--white);
color: var(--black);
border: 1px solid var(--black);
border-radius: 15px;
line-height: 1.6;
overflow-y: auto;
}
.actions {
display:flex;
justify-content: space-between;
padding: 0 1em;
margin: 12px 0 -6px 0;
}
.close-icon {
font-size: 36px;
}
.close-icon:hover {
cursor: pointer;
opacity: .6;
}
@media (min-width: 701px) {
* {
}
#trade-bot-portal {
display: grid;
grid-template-columns:3fr 4fr 3fr;
grid-template-columns: 2fr 4fr 2fr;
grid-auto-rows: max(80px);
column-gap: 0.5em;
row-gap: 0.4em;
justify-items: stretch;
align-items: stretch;
margin-bottom: 10px;
margin-bottom: 20px;
}
#first-trade-section {
display: grid;
grid-template-columns:1fr 4fr 1fr;
grid-template-columns: 1fr 4fr 1fr;
grid-auto-rows: max(250px);
column-gap: 0.5em;
row-gap: 0.4em;
@ -567,7 +594,7 @@ class TradeBotPortal extends LitElement {
}
#second-trade-section {
display: grid;
grid-template-columns:1fr 4fr 1fr;
grid-template-columns: 1fr 4fr 1fr;
grid-auto-rows: max(250px);
column-gap: 0.5em;
row-gap: 0.4em;
@ -860,7 +887,7 @@ class TradeBotPortal extends LitElement {
return html`
<div class="my-open-orders">
<div class="box">
<header><span>${translate("tradepage.tchange36")}</span></header>
<header><span>${translate("info.inf15")}</span></header>
<div class="border-wrapper">
<div class="loadingContainer" id="loadingHistoricTrades" style="display:${this.isLoadingMyOpenOrders ? 'block' : 'none'}"><div class="loading"></div><span style="color: var(--black);">${translate("login.loading")}</span></div>
<vaadin-grid multi-sort="true" theme="compact column-borders row-stripes wrap-cell-content" id="myOrdersGrid" aria-label="My Orders" .items="${this.listedCoins.get(this.selectedCoin).myOrders}">
@ -1037,7 +1064,7 @@ class TradeBotPortal extends LitElement {
return html`
<div class="trade-bot-container">
<div class="box-bot">
<header><span>${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT</span></header>
<header><span>${translate("info.inf16")} QORT ${translate("info.inf14")} ${this.listedCoins.get(this.selectedCoin).coinCode}</span></header>
<div class="border-wrapper">
<vaadin-grid theme="compact column-borders row-stripes wrap-cell-content" id="tradeBotBTCGrid" aria-label="Auto Buy BTC" ?hidden="${this.isEmptyArray(this.tradeBotBtcBook)}" .items="${this.tradeBotBtcBook}">
<vaadin-grid-column
@ -1103,7 +1130,7 @@ class TradeBotPortal extends LitElement {
return html`
<div class="trade-bot-container">
<div class="box-bot">
<header><span>${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT</span></header>
<header><span>${translate("info.inf16")} QORT ${translate("info.inf14")} ${this.listedCoins.get(this.selectedCoin).coinCode}</span></header>
<div class="border-wrapper">
<vaadin-grid theme="compact column-borders row-stripes wrap-cell-content" id="tradeBotLTCGrid" aria-label="Auto Buy LTC" ?hidden="${this.isEmptyArray(this.tradeBotLtcBook)}" .items="${this.tradeBotLtcBook}">
<vaadin-grid-column
@ -1169,7 +1196,7 @@ class TradeBotPortal extends LitElement {
return html`
<div class="trade-bot-container">
<div class="box-bot">
<header><span>${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT</span></header>
<header><span>${translate("info.inf16")} QORT ${translate("info.inf14")} ${this.listedCoins.get(this.selectedCoin).coinCode}</span></header>
<div class="border-wrapper">
<vaadin-grid theme="compact column-borders row-stripes wrap-cell-content" id="tradeBotDOGEGrid" aria-label="Auto Buy DOGE" ?hidden="${this.isEmptyArray(this.tradeBotDogeBook)}" .items="${this.tradeBotDogeBook}">
<vaadin-grid-column
@ -1235,7 +1262,7 @@ class TradeBotPortal extends LitElement {
return html`
<div class="trade-bot-container">
<div class="box-bot">
<header><span>${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT</span></header>
<header><span>${translate("info.inf16")} QORT ${translate("info.inf14")} ${this.listedCoins.get(this.selectedCoin).coinCode}</span></header>
<div class="border-wrapper">
<vaadin-grid theme="compact column-borders row-stripes wrap-cell-content" id="tradeBotDGBGrid" aria-label="Auto Buy DGB" ?hidden="${this.isEmptyArray(this.tradeBotDgbBook)}" .items="${this.tradeBotDgbBook}">
<vaadin-grid-column
@ -1301,7 +1328,7 @@ class TradeBotPortal extends LitElement {
return html`
<div class="trade-bot-container">
<div class="box-bot">
<header><span>${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT</span></header>
<header><span>${translate("info.inf16")} QORT ${translate("info.inf14")} ${this.listedCoins.get(this.selectedCoin).coinCode}</span></header>
<div class="border-wrapper">
<vaadin-grid theme="compact column-borders row-stripes wrap-cell-content" id="tradeBotRVNGrid" aria-label="Auto Buy RVN" ?hidden="${this.isEmptyArray(this.tradeBotRvnBook)}" .items="${this.tradeBotRvnBook}">
<vaadin-grid-column
@ -1367,7 +1394,7 @@ class TradeBotPortal extends LitElement {
return html`
<div class="trade-bot-container">
<div class="box-bot">
<header><span>${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT</span></header>
<header><span>${translate("info.inf16")} QORT ${translate("info.inf14")} ${this.listedCoins.get(this.selectedCoin).coinCode}</span></header>
<div class="border-wrapper">
<vaadin-grid theme="compact column-borders row-stripes wrap-cell-content" id="tradeBotARRRGrid" aria-label="Auto Buy ARRR" ?hidden="${this.isEmptyArray(this.tradeBotArrrBook)}" .items="${this.tradeBotArrrBook}">
<vaadin-grid-column
@ -1435,7 +1462,7 @@ class TradeBotPortal extends LitElement {
<div id="trade-bot-portal">
<div></div>
<div style="min-height: 50px; display: flex; padding-bottom: 10px; padding-top: 10px; margin: auto;">
<h2 style="margin: 0 0 15px 0; line-height: 50px; display: inline;">${translate("tradepage.tchange39")} - &nbsp;</h2>
<h2 style="margin: 0 0 15px 0; line-height: 50px; display: inline;">${translate("info.inf13")} QORT ${translate("info.inf14")} ${this.listedCoins.get(this.selectedCoin).coinCode} - &nbsp;</h2>
<mwc-select outlined id="coinSelectionMenu" label="${translate("tradepage.tchange2")}">
<mwc-list-item value="BITCOIN"><span class="coinName btc" style="color: var(--black);">BTC / QORT</span></mwc-list-item>
<mwc-list-item value="LITECOIN" selected><span class="coinName ltc" style="color: var(--black);">LTC / QORT</span></mwc-list-item>
@ -1444,6 +1471,9 @@ class TradeBotPortal extends LitElement {
<mwc-list-item value="RAVENCOIN"><span class="coinName rvn" style="color: var(--black);">RVN / QORT</span></mwc-list-item>
<mwc-list-item value="PIRATECHAIN"><span class="coinName arrr" style="color: var(--black);">ARRR / QORT</span></mwc-list-item>
</mwc-select>
<div style="padding-left: 20px; padding-top: 5px;">
<mwc-fab mini icon="info" title="${translate("info.inf7")}" @click=${() => this.shadowRoot.getElementById('buyInfoDialog').open()}></mwc-fab>
</div>
</div>
<div></div>
</div>
@ -1924,6 +1954,20 @@ class TradeBotPortal extends LitElement {
</div>
<mwc-button slot="primaryAction" dialogAction="cancel" class="cancel">${translate("general.close")}</mwc-button>
</mwc-dialog>
<paper-dialog id="buyInfoDialog" class="info" modal>
<div class="actions">
<h3></h3>
<mwc-icon class="close-icon" @click=${() => this.shadowRoot.getElementById('buyInfoDialog').close()} title="${translate("info.inf8")}">highlight_off</mwc-icon>
</div>
<div class="container">
<h1 style="color: #03a9f4;">${translate("info.inf7")}</h1>
<h2>${translate("info.inf9")}</h2>
<h2>${translate("info.inf10")}</h2>
<h2>${translate("info.inf11")}</h2>
<h2>${translate("info.inf12")}</h2>
</div>
</paper-dialog>
`
}

View File

@ -12,11 +12,13 @@ import '@material/mwc-textfield'
import '@material/mwc-icon'
import '@material/mwc-icon-button'
import '@material/mwc-dialog'
import '@material/mwc-fab'
import '@material/mwc-tab-bar'
import '@material/mwc-tab'
import '@material/mwc-list/mwc-list-item'
import '@material/mwc-select'
import '@polymer/iron-icons/iron-icons.js'
import '@polymer/paper-dialog/paper-dialog.js'
import '@polymer/paper-icon-button/paper-icon-button.js'
import '@polymer/paper-spinner/paper-spinner-lite.js'
import '@vaadin/grid'
@ -549,6 +551,31 @@ class TradePortal extends LitElement {
opacity: 0;
}
}
paper-dialog.info {
width: 75%;
max-width: 75vw;
height: 35%;
max-height: 35vh;
background-color: var(--white);
color: var(--black);
border: 1px solid var(--black);
border-radius: 15px;
line-height: 1.6;
overflow-y: auto;
}
.actions {
display: flex;
justify-content: space-between;
padding: 0 1em;
margin: 12px 0 -6px 0;
}
.close-icon {
font-size: 36px;
}
.close-icon:hover {
cursor: pointer;
opacity: .6;
}
@media (min-width: 701px) {
* {
}
@ -887,8 +914,8 @@ class TradePortal extends LitElement {
<div class="open-market-container">
<div class="box">
<mwc-tab-bar id="tabs-1" activeIndex="0">
<mwc-tab id="tab-buy" label="${translate("tradepage.tchange18")}" @click="${(e) => this.displayTabContent('buy')}"></mwc-tab>
<mwc-tab id="tab-sell" label="${translate("tradepage.tchange19")}" @click="${(e) => this.displayTabContent('sell')}"></mwc-tab>
<mwc-tab id="tab-buy" label="${translate("tradepage.tchange18")} QORT" @click="${(e) => this.displayTabContent('buy')}"></mwc-tab>
<mwc-tab id="tab-sell" label="${translate("tradepage.tchange19")} QORT" @click="${(e) => this.displayTabContent('sell')}"></mwc-tab>
</mwc-tab-bar>
<z id="tabs-1-content">
<div id="tab-buy-content">
@ -1168,9 +1195,13 @@ class TradePortal extends LitElement {
<mwc-list-item value="RAVENCOIN"><span class="coinName rvn" style="color: var(--black);">QORT / RVN</span></mwc-list-item>
<mwc-list-item value="PIRATECHAIN"><span class="coinName arrr" style="color: var(--black);">QORT / ARRR</span></mwc-list-item>
</mwc-select>
<div style="padding-left: 25px; padding-top: 15px;">
<div style="padding-left: 20px; padding-top: 5px;">
<mwc-fab mini icon="info" title="${translate("info.inf1")}" @click=${() => this.shadowRoot.getElementById('tradeInfoDialog').open()}></mwc-fab>
</div>
<div style="padding-left: 20px; padding-top: 15px;">
${this.chartShowCoin()}
</div>
</div>
<div id="trade-portal">
<div id="first-trade-section">
@ -1221,6 +1252,19 @@ class TradePortal extends LitElement {
</div>
<mwc-button slot="primaryAction" dialogAction="cancel" class="cancel">${translate("general.close")}</mwc-button>
</mwc-dialog>
<paper-dialog id="tradeInfoDialog" class="info" modal>
<div class="actions">
<h3></h3>
<mwc-icon class="close-icon" @click=${() => this.shadowRoot.getElementById('tradeInfoDialog').close()} title="${translate("info.inf2")}">highlight_off</mwc-icon>
</div>
<div class="container">
<h1 style="color: #03a9f4; text-align: center;">${translate("info.inf1")}</h1>
<h1 style="text-align: center;">${translate("info.inf3")} ${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("info.inf4")}</h1>
<h1 style="text-align: center;">${translate("info.inf5")} ${this.listedCoins.get(this.selectedCoin).coinCode}</h1>
<h1 style="text-align: center;">${translate("info.inf6")}</h1>
</div>
</paper-dialog>
`
}

View File

@ -62,6 +62,7 @@ class MultiWallet extends LitElement {
arrrMemo: { type: String },
errorMessage: { type: String },
arrrWalletAddress: { type: String },
unusedWalletAddress: { type: String },
successMessage: { type: String },
sendMoneyLoading: { type: Boolean },
btnDisable: { type: Boolean },
@ -357,8 +358,8 @@ class MultiWallet extends LitElement {
.wallet-address {
display: flex;
align-items: center;
font-size: 18px;
color: var(--black);
font-size: 18px;
color: var(--black);
margin: 4px 0 20px;
}
@ -366,7 +367,7 @@ class MultiWallet extends LitElement {
display: inline-block;
font-weight: 600;
font-size: 32px;
color: var(--black);
color: var(--black);
}
#transactions {
@ -505,6 +506,13 @@ class MultiWallet extends LitElement {
position: relative;
}
.unused-address-dialog {
min-height: 150px;
min-width: 550px;
box-sizing: border-box;
position: relative;
}
.btn-clear-success {
--mdc-icon-button-size: 32px;
color: red;
@ -561,6 +569,12 @@ class MultiWallet extends LitElement {
width: 185px;
}
.unused-pos {
margin-top: -44px;
margin-left: 410px;
width: 185px;
}
@media (max-width: 863px) {
.wallet {
width: 100%;
@ -673,6 +687,7 @@ class MultiWallet extends LitElement {
this.arrrRecipient = ''
this.arrrMemo = ''
this.arrrWalletAddress = ''
this.unusedWalletAddress = ''
this.errorMessage = ''
this.successMessage = ''
this.myElementId = ''
@ -799,7 +814,7 @@ class MultiWallet extends LitElement {
${translate("walletpage.wchange2")}
<div class="wallet-address" ?hidden="${this.getSelectedWalletAddress().length < 1}">
<span>${this.getSelectedWalletAddress()}</span>
<button-icon-copy
<button-icon-copy
title="${translate("walletpage.wchange3")}"
onSuccessMessage="${translate("walletpage.wchange4")}"
onErrorMessage="${translate("walletpage.wchange39")}"
@ -822,6 +837,9 @@ class MultiWallet extends LitElement {
<div class="book-pos" ?hidden="${this.getSelectedWalletAddress().length < 1}">
${this.renderAddressbookButton()}
</div>
<div class="unused-pos" ?hidden="${this.getSelectedWalletAddress().length < 1}">
${this.renderUnusedAddressButton()}
</div>
<div class="qrcode-pos" ?hidden="${this.getSelectedWalletAddress().length < 1}">
<qortal-qrcode-generator data="${this.getSelectedWalletAddress()}" mode="octet" format="html" auto></qortal-qrcode-generator>
</div>
@ -849,7 +867,7 @@ class MultiWallet extends LitElement {
<span class="title"> ${translate("walletpage.wchange10")} </span>
<br />
<div style="display: inline;">
${this.renderSQB()}
${this.selectedTransaction.type === 'DEPLOY_AT' ? html`${this.renderCAB()}` : html`${this.renderSQB()}`}
</div>
<br />
${!this.selectedTransaction.amount ? '' : html`
@ -1968,6 +1986,171 @@ class MultiWallet extends LitElement {
</mwc-button>
</mwc-dialog>
<mwc-dialog id="btcUnusedAddressDialog" scrimClickAction="" escapeKeyAction="">
<div class="unused-address-dialog">
<div style="text-align: center;">
<img src="/img/btc.png" width="32" height="32">
<h2>BTC</h2>
<hr />
</div>
<p>
<span style="font-weight: bold;">${this.wallets.get(this._selectedWallet).unusedWalletAddress}</span>
<button-icon-copy
title="${translate("walletpage.wchange3")}"
onSuccessMessage="${translate("walletpage.wchange4")}"
onErrorMessage="${translate("walletpage.wchange39")}"
textToCopy=${this.wallets.get(this._selectedWallet).unusedWalletAddress}
buttonSize="24px"
iconSize="16px"
color="var(--copybutton)"
offsetLeft="4px"
>
</button-icon-copy>
<br />
<span>${translate("walletpage.wchange38")}</span>
</p>
</div>
<mwc-button
slot="primaryAction"
dialogAction="cancel"
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="ltcUnusedAddressDialog" scrimClickAction="" escapeKeyAction="">
<div class="unused-address-dialog">
<div style="text-align: center;">
<img src="/img/ltc.png" width="32" height="32">
<h2>LTC</h2>
<hr />
</div>
<p>
<span style="font-weight: bold;">${this.wallets.get(this._selectedWallet).unusedWalletAddress}</span>
<button-icon-copy
title="${translate("walletpage.wchange3")}"
onSuccessMessage="${translate("walletpage.wchange4")}"
onErrorMessage="${translate("walletpage.wchange39")}"
textToCopy=${this.wallets.get(this._selectedWallet).unusedWalletAddress}
buttonSize="24px"
iconSize="16px"
color="var(--copybutton)"
offsetLeft="4px"
>
</button-icon-copy>
<br />
<span>${translate("walletpage.wchange38")}</span>
</p>
</div>
<mwc-button
slot="primaryAction"
dialogAction="cancel"
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="dogeUnusedAddressDialog" scrimClickAction="" escapeKeyAction="">
<div class="unused-address-dialog">
<div style="text-align: center;">
<img src="/img/doge.png" width="32" height="32">
<h2>DOGE</h2>
<hr />
</div>
<p>
<span style="font-weight: bold;">${this.wallets.get(this._selectedWallet).unusedWalletAddress}</span>
<button-icon-copy
title="${translate("walletpage.wchange3")}"
onSuccessMessage="${translate("walletpage.wchange4")}"
onErrorMessage="${translate("walletpage.wchange39")}"
textToCopy=${this.wallets.get(this._selectedWallet).unusedWalletAddress}
buttonSize="24px"
iconSize="16px"
color="var(--copybutton)"
offsetLeft="4px"
>
</button-icon-copy>
<br />
<span>${translate("walletpage.wchange38")}</span>
</p>
</div>
<mwc-button
slot="primaryAction"
dialogAction="cancel"
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="dgbUnusedAddressDialog" scrimClickAction="" escapeKeyAction="">
<div class="unused-address-dialog">
<div style="text-align: center;">
<img src="/img/dgb.png" width="32" height="32">
<h2>DGB</h2>
<hr />
</div>
<p>
<span style="font-weight: bold;">${this.wallets.get(this._selectedWallet).unusedWalletAddress}</span>
<button-icon-copy
title="${translate("walletpage.wchange3")}"
onSuccessMessage="${translate("walletpage.wchange4")}"
onErrorMessage="${translate("walletpage.wchange39")}"
textToCopy=${this.wallets.get(this._selectedWallet).unusedWalletAddress}
buttonSize="24px"
iconSize="16px"
color="var(--copybutton)"
offsetLeft="4px"
>
</button-icon-copy>
<br />
<span>${translate("walletpage.wchange38")}</span>
</p>
</div>
<mwc-button
slot="primaryAction"
dialogAction="cancel"
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="rvnUnusedAddressDialog" scrimClickAction="" escapeKeyAction="">
<div class="unused-address-dialog">
<div style="text-align: center;">
<img src="/img/rvn.png" width="32" height="32">
<h2>RVN</h2>
<hr />
</div>
<p>
<span style="font-weight: bold;">${this.wallets.get(this._selectedWallet).unusedWalletAddress}</span>
<button-icon-copy
title="${translate("walletpage.wchange3")}"
onSuccessMessage="${translate("walletpage.wchange4")}"
onErrorMessage="${translate("walletpage.wchange39")}"
textToCopy=${this.wallets.get(this._selectedWallet).unusedWalletAddress}
buttonSize="24px"
iconSize="16px"
color="var(--copybutton)"
offsetLeft="4px"
>
</button-icon-copy>
<br />
<span>${translate("walletpage.wchange38")}</span>
</p>
</div>
<mwc-button
slot="primaryAction"
dialogAction="cancel"
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="addQortAddressDialog" scrimClickAction="" escapeKeyAction="">
<div style="text-align:center">
<img src="/img/qort.png" width="32" height="32">
@ -3843,6 +4026,23 @@ class MultiWallet extends LitElement {
}
}
renderCAB() {
return html`
<span>${this.selectedTransaction.aTAddress}</span>
<button-icon-copy
title="${translate("blockpage.bcchange8")}"
onSuccessMessage="${translate("walletpage.wchange4")}"
onErrorMessage="${translate("walletpage.wchange39")}"
textToCopy=${this.selectedTransaction.aTAddress}
buttonSize="24px"
iconSize="16px"
color="var(--copybutton)"
offsetLeft="4px"
>
</button-icon-copy>
`
}
renderFetchText() {
return html`${translate("walletpage.wchange1")}`
}
@ -4098,7 +4298,7 @@ class MultiWallet extends LitElement {
const getName = async (recipient)=> {
try {
const getNames = await parentEpml.request("apiCall", {
type: "api",
url: `/names/address/${recipient}`,
@ -4484,12 +4684,12 @@ class MultiWallet extends LitElement {
this.balance = this.wallets.get(this._selectedWallet).balance
}
}
})
})
const txsQort = await parentEpml.request('apiCall', {
url: `/transactions/search?address=${this.wallets.get('qort').wallet.address}&confirmationStatus=CONFIRMED&reverse=true`,
})
url: `/transactions/search?address=${this.wallets.get('qort').wallet.address}&confirmationStatus=CONFIRMED&reverse=true&txType=PAYMENT&txType=REGISTER_NAME&txType=UPDATE_NAME&txType=SELL_NAME&txType=CANCEL_SELL_NAME&txType=BUY_NAME&txType=CREATE_POLL&txType=VOTE_ON_POLL&txType=ISSUE_ASSET&txType=TRANSFER_ASSET&txType=CREATE_ASSET_ORDER&txType=CANCEL_ASSET_ORDER&txType=MULTI_PAYMENT&txType=DEPLOY_AT&txType=MESSAGE&txType=AIRDROP&txType=AT&txType=CREATE_GROUP&txType=UPDATE_GROUP&txType=ADD_GROUP_ADMIN&txType=REMOVE_GROUP_ADMIN&txType=GROUP_BAN&txType=CANCEL_GROUP_BAN&txType=GROUP_KICK&txType=GROUP_INVITE&txType=CANCEL_GROUP_INVITE&txType=JOIN_GROUP&txType=LEAVE_GROUP&txType=GROUP_APPROVAL&txType=SET_GROUP&txType=UPDATE_ASSET&txType=ACCOUNT_FLAGS&txType=ENABLE_FORGING&txType=REWARD_SHARE&txType=ACCOUNT_LEVEL&txType=TRANSFER_PRIVS&txType=PRESENCE`,
})
const pendingTxsQort = await parentEpml.request('apiCall', {
url: `/transactions/unconfirmed?creator=${this.wallets.get('qort').wallet.base58PublicKey}&reverse=true&txType=PAYMENT&txType=REGISTER_NAME&txType=UPDATE_NAME&txType=SELL_NAME&txType=CANCEL_SELL_NAME&txType=BUY_NAME&txType=CREATE_POLL&txType=VOTE_ON_POLL&txType=ARBITRARY&txType=ISSUE_ASSET&txType=TRANSFER_ASSET&txType=CREATE_ASSET_ORDER&txType=CANCEL_ASSET_ORDER&txType=MULTI_PAYMENT&txType=DEPLOY_AT&txType=MESSAGE&txType=PUBLICIZE&txType=AIRDROP&txType=AT&txType=CREATE_GROUP&txType=UPDATE_GROUP&txType=ADD_GROUP_ADMIN&txType=REMOVE_GROUP_ADMIN&txType=GROUP_BAN&txType=CANCEL_GROUP_BAN&txType=GROUP_KICK&txType=GROUP_INVITE&txType=CANCEL_GROUP_INVITE&txType=JOIN_GROUP&txType=LEAVE_GROUP&txType=GROUP_APPROVAL&txType=SET_GROUP&txType=UPDATE_ASSET&txType=ACCOUNT_FLAGS&txType=ENABLE_FORGING&txType=REWARD_SHARE&txType=ACCOUNT_LEVEL&txType=TRANSFER_PRIVS&txType=PRESENCE`,
url: `/transactions/unconfirmed?creator=${this.wallets.get('qort').wallet.base58PublicKey}&reverse=true&txType=PAYMENT&txType=REGISTER_NAME&txType=UPDATE_NAME&txType=SELL_NAME&txType=CANCEL_SELL_NAME&txType=BUY_NAME&txType=CREATE_POLL&txType=VOTE_ON_POLL&txType=ISSUE_ASSET&txType=TRANSFER_ASSET&txType=CREATE_ASSET_ORDER&txType=CANCEL_ASSET_ORDER&txType=MULTI_PAYMENT&txType=DEPLOY_AT&txType=MESSAGE&txType=AIRDROP&txType=AT&txType=CREATE_GROUP&txType=UPDATE_GROUP&txType=ADD_GROUP_ADMIN&txType=REMOVE_GROUP_ADMIN&txType=GROUP_BAN&txType=CANCEL_GROUP_BAN&txType=GROUP_KICK&txType=GROUP_INVITE&txType=CANCEL_GROUP_INVITE&txType=JOIN_GROUP&txType=LEAVE_GROUP&txType=GROUP_APPROVAL&txType=SET_GROUP&txType=UPDATE_ASSET&txType=ACCOUNT_FLAGS&txType=ENABLE_FORGING&txType=REWARD_SHARE&txType=ACCOUNT_LEVEL&txType=TRANSFER_PRIVS&txType=PRESENCE`,
})
if (this._selectedWallet == coin) {
this.wallets.get(coin).transactions = pendingTxsQort.concat(txsQort)
@ -4621,6 +4821,70 @@ class MultiWallet extends LitElement {
}
}
async getUnusedAddress(coin) {
this.wallets.get(this._selectedWallet).unusedWalletAddress = ''
let _url = ``
let _body = null
switch (coin) {
case 'qort':
case 'arrr':
_url = ``
_body = null
break
case 'btc':
case 'ltc':
case 'doge':
case 'dgb':
case 'rvn':
const walletName = `${coin}Wallet`
_url = `/crosschain/${coin}/unusedaddress?apiKey=${this.getApiKey()}`
_body = `${window.parent.reduxStore.getState().app.selectedAddress[walletName].derivedMasterPublicKey}`
break
default:
break
}
if (_body === null) {
this.unusedAddressString = ""
} else {
await parentEpml.request('apiCall', {
url: _url,
method: 'POST',
body: _body,
}).then((res) => {
this.wallets.get(this._selectedWallet).unusedWalletAddress = res
this.unusedAddressString = this.wallets.get(this._selectedWallet).unusedWalletAddress
this.openUnusedAddressDialog(coin)
})
}
}
openUnusedAddressDialog(coin) {
switch (coin) {
case 'qort':
case 'arrr':
break
case 'btc':
this.shadowRoot.querySelector("#btcUnusedAddressDialog").show();
break
case 'ltc':
this.shadowRoot.querySelector("#ltcUnusedAddressDialog").show();
break
case 'doge':
this.shadowRoot.querySelector("#dogeUnusedAddressDialog").show();
break
case 'dgb':
this.shadowRoot.querySelector("#dgbUnusedAddressDialog").show();
break
case 'rvn':
this.shadowRoot.querySelector("#rvnUnusedAddressDialog").show();
break
default:
break
}
}
renderSendButton() {
if ( this._selectedWallet === "qort" ) {
return html`<vaadin-button theme="primary medium" style="width: 100%;" @click=${() => this.openSendQort()}><vaadin-icon icon="vaadin:coin-piles" slot="prefix"></vaadin-icon> ${translate("walletpage.wchange17")} QORT</vaadin-button>`
@ -4661,6 +4925,22 @@ class MultiWallet extends LitElement {
}
}
renderUnusedAddressButton() {
switch (this._selectedWallet) {
case "qort":
case "arrr":
return html`<vaadin-button disabled theme="primary medium" style="width: 100%;" @click=${() => this.getUnusedAddress(this._selectedWallet)}><vaadin-icon icon="vaadin:magic" slot="prefix"></vaadin-icon> ${translate("walletpage.wchange58")}</vaadin-button>`
case "btc":
case "ltc":
case "doge":
case "dgb":
case "rvn":
return html`<vaadin-button theme="primary medium" style="width: 100%;" @click=${() => this.getUnusedAddress(this._selectedWallet)}><vaadin-icon icon="vaadin:magic" slot="prefix"></vaadin-icon> ${translate("walletpage.wchange58")}</vaadin-button>`
default:
return html``
}
}
renderExportAddressbookButton() {
if ( this._selectedWallet === "qort" ) {
return html`<vaadin-button theme="primary small" style="width: 100%;" @click=${() => this.exportQortAddressbook()}><vaadin-icon icon="vaadin:cloud-download" slot="prefix"></vaadin-icon> ${translate("walletpage.wchange54")}</vaadin-button>`
@ -4897,7 +5177,12 @@ class MultiWallet extends LitElement {
path="creatorAddress"
>
</vaadin-grid-column>
<vaadin-grid-column auto-width header="${translate("walletpage.wchange10")}" path="recipient"></vaadin-grid-column>
<vaadin-grid-column auto-width header="${translate("walletpage.wchange10")}"
.renderer=${(root, column, data) => {
render(html`${data.item.type === 'DEPLOY_AT' ? html`${data.item.aTAddress}` : html`${data.item.recipient}`}`, root)
}}
>
</vaadin-grid-column>
<vaadin-grid-column auto-width header="${translate("walletpage.wchange36")}" path="fee"></vaadin-grid-column>
<vaadin-grid-column auto-width header="${translate("walletpage.wchange11")}" path="amount"></vaadin-grid-column>
<vaadin-grid-column auto-width

View File

@ -21,14 +21,16 @@ export const replaceMessagesEdited = async ({
let responseItem = { ...response[0] }
const decodeResponseItem = decodeMessageFunc(responseItem, isReceipient, _publicKey)
delete decodeResponseItem.timestamp
msgItem = {
...msg,
...decodeResponseItem,
senderName: msg.senderName,
sender: msg.sender,
editedTimestamp: response[0].timestamp,
}
}
} catch (error) {
console.log(error)
}
return msgItem
@ -39,7 +41,6 @@ export const replaceMessagesEdited = async ({
try {
parsedMessageObj = JSON.parse(msg.decodedMessage)
} catch (error) {
console.log('error')
return msg
}
let msgItem = msg
@ -49,39 +50,52 @@ export const replaceMessagesEdited = async ({
msgQuery = `&txGroupId=${msg.txGroupId}`
}
if (parsedMessageObj.repliedTo) {
const originalReply = await parentEpml.request("apiCall", {
type: "api",
url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}`,
})
const response = await parentEpml.request("apiCall", {
type: "api",
url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}`,
})
if (
originalReply &&
Array.isArray(originalReply) &&
originalReply.length !== 0 &&
response &&
Array.isArray(response) &&
response.length !== 0
) {
const decodeOriginalReply = decodeMessageFunc(originalReply[0], isReceipient, _publicKey)
const decodeUpdatedReply = decodeMessageFunc(response[0], isReceipient, _publicKey)
const formattedRepliedToData = {
...decodeUpdatedReply,
senderName: decodeOriginalReply.senderName,
sender: decodeOriginalReply.sender,
}
msgItem = {
...msg,
repliedToData: decodeMessageFunc(response[0], isReceipient, _publicKey),
repliedToData: formattedRepliedToData,
}
} else {
const response2 = await parentEpml.request("apiCall", {
type: "api",
url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}`,
})
if (
response2 &&
Array.isArray(response2) &&
response2.length !== 0
originalReply &&
Array.isArray(originalReply) &&
originalReply.length !== 0
) {
msgItem = {
...msg,
repliedToData: decodeMessageFunc(response2[0], isReceipient, _publicKey),
repliedToData: decodeMessageFunc(originalReply[0], isReceipient, _publicKey),
}
}
}
}
} catch (error) {
console.log(error)
}
return msgItem

View File

@ -0,0 +1,4 @@
export function roundToNearestDecimal(num) {
const mb = num / 1000000;
return Math.round(mb * 10) / 10;
}

View File

@ -21,6 +21,9 @@ if ! which curl; then sudo apt-get --yes install curl; fi
# Install apt repository source list if it does not exist
if ! grep ^ /etc/apt/sources.list /etc/apt/sources.list.d/* | grep qortal.list; then
curl -fsSL https://update.qortal.online/repo/qortal.gpg | sudo tee /usr/share/keyrings/qortal.gpg
echo 'deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/qortal.gpg] https://update.qortal.online/repo/ ./ ' > /etc/apt/sources.list.d/qortal.list
curl -sS https://update.qortal.online/repo/qortal.gpg | sudo apt-key add -
sudo rm -rf /usr/share/keyrings/qortal.gpg
sudo apt-key export 7FB37C97 | sudo gpg --dearmour -o /usr/share/keyrings/qortal.gpg
sudo rm -rf /etc/apt/sources.list.d/qortal.list
echo 'deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/qortal.gpg] https://update.qortal.online/repo/ ./ ' > /etc/apt/sources.list.d/qortal.list
fi