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 ~/.profile ``` (For Debian based distro) <br/>
``` source ~/.bashrc ``` (For Fedora / CentOS) <br/> ``` source ~/.bashrc ``` (For Fedora / CentOS) <br/>
``` nvm ls-remote ``` (Fetch list of available versions) <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 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 ``` 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_7": "Актуализацията е готова за инсталиране",
"electron_translate_8": "Беше изтеглена нова версия на Qortal UI.", "electron_translate_8": "Беше изтеглена нова версия на Qortal UI.",
"electron_translate_9": "Щракнете върху ИНСТАЛИРАНЕ СЕГА, за да приложите актуализация, МОЖЕ БИ ПО-КЪСНО, за да не актуализирате потребителския интерфейс.", "electron_translate_9": "Щракнете върху ИНСТАЛИРАНЕ СЕГА, за да приложите актуализация, МОЖЕ БИ ПО-КЪСНО, за да не актуализирате потребителския интерфейс.",
"electron_translate_10": "Грешка при актуализиране...", "electron_translate_10": "Временна неуспешна актуализация, ще опитаме отново по-късно",
"electron_translate_11": "ИЗТЕГЛЕТЕ АКТУАЛИЗАЦИЯТА", "electron_translate_11": "ИЗТЕГЛЕТЕ АКТУАЛИЗАЦИЯТА",
"electron_translate_12": "Ще бъде изтеглено ⌛️ във фонов режим!" "electron_translate_12": "Ще бъде изтеглено ⌛️ във фонов режим!"
} }

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Update zur Installation bereit", "electron_translate_7": "Update zur Installation bereit",
"electron_translate_8": "Eine neue Qortal-UI-Version wurde heruntergeladen.", "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_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_11": "UPDATE HERUNTERLADEN",
"electron_translate_12": "Das Update wird im Hintergrund heruntergeladen ⌛️!" "electron_translate_12": "Das Update wird im Hintergrund heruntergeladen ⌛️!"
} }

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Update ready to install", "electron_translate_7": "Update ready to install",
"electron_translate_8": "A new Qortal UI version has been downloaded.", "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_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_11": "DOWNLOAD UPDATE",
"electron_translate_12": "It will be downloaded ⌛️ in the background!" "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_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_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_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_11": "DESCARGAR ACTUALIZACIÓN",
"electron_translate_12": "¡Se descargará ⌛️ en segundo plano!" "electron_translate_12": "¡Se descargará ⌛️ en segundo plano!"
} }

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Mise à jour prête à installer", "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_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_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_11": "TÉLÉCHARGER LA MISE À JOUR",
"electron_translate_12": "Il sera téléchargé ⌛️ en arrière-plan !" "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_7": "Aggiornamento pronto per l'installazione",
"electron_translate_8": "È stata scaricata una nuova versione dell'interfaccia utente di Qortal.", "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_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_11": "SCARICA AGGIORNAMENTO",
"electron_translate_12": "Verrà scaricato ⌛️ in background!" "electron_translate_12": "Verrà scaricato ⌛️ in background!"
} }

View File

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

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Update klaar om te installeren", "electron_translate_7": "Update klaar om te installeren",
"electron_translate_8": "Er is een nieuwe Qortal UI-versie gedownload.", "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_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_11": "UPDATE DOWNLOADEN",
"electron_translate_12": "Het wordt ⌛️ op de achtergrond gedownload!" "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_7": "Atualização pronta para instalar",
"electron_translate_8": "Uma nova versão Qortal UI foi baixada.", "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_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_11": "BAIXAR ATUALIZAÇÃO",
"electron_translate_12": "Será baixado ⌛️ em segundo plano!" "electron_translate_12": "Será baixado ⌛️ em segundo plano!"
} }

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Atualização pronta para instalar", "electron_translate_7": "Atualização pronta para instalar",
"electron_translate_8": "Uma nova versão Qortal UI foi baixada.", "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_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_11": "BAIXAR ATUALIZAÇÃO",
"electron_translate_12": "Será baixado ⌛️ em segundo plano!" "electron_translate_12": "Será baixado ⌛️ em segundo plano!"
} }

View File

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

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Güncelleme yüklenmeye hazır", "electron_translate_7": "Güncelleme yüklenmeye hazır",
"electron_translate_8": "Yeni bir Qortal UI sürümü indirildi.", "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_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_11": "GÜNCELLEMEYİ İNDİRİN",
"electron_translate_12": "Arka planda ⌛️ indirilecek!" "electron_translate_12": "Arka planda ⌛️ indirilecek!"
} }

View File

@ -10,7 +10,7 @@
"electron_translate_7": "Update ready to install", "electron_translate_7": "Update ready to install",
"electron_translate_8": "A new Qortal UI version has been downloaded.", "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_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_11": "DOWNLOAD UPDATE",
"electron_translate_12": "It will be downloaded ⌛️ in the background!" "electron_translate_12": "It will be downloaded ⌛️ in the background!"
} }

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

@ -9,7 +9,8 @@ html {
--copybutton: #707584; --copybutton: #707584;
--chat-group: #080808; --chat-group: #080808;
--chat-bubble: #9f9f9f0a; --chat-bubble: #9f9f9f0a;
--chat-bubble-bg: #f3f3f3; --chat-bubble-bg: #e6e6e6;
--chat-bubble-myBg: #d1ddf2;
--chat-bubble-msg-color: #080808; --chat-bubble-msg-color: #080808;
--reaction-bubble-outline: #6b6969; --reaction-bubble-outline: #6b6969;
--chat-menu-bg: #ffffff; --chat-menu-bg: #ffffff;
@ -73,6 +74,7 @@ html[theme="dark"] {
--chat-group: #ffffff; --chat-group: #ffffff;
--chat-bubble: #9694941a; --chat-bubble: #9694941a;
--chat-bubble-bg: #2d3749; --chat-bubble-bg: #2d3749;
--chat-bubble-myBg: #40444d;
--chat-bubble-msg-color: #ffffff; --chat-bubble-msg-color: #ffffff;
--reaction-bubble-outline: #ffffff; --reaction-bubble-outline: #ffffff;
--chat-menu-bg: #32394c; --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", "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", "description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet",
"keywords": [ "keywords": [
"QORT", "QORT",
@ -17,9 +17,9 @@
"author": "QORTAL <admin@qortal.org>", "author": "QORTAL <admin@qortal.org>",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@hapi/hapi": "21.2.1", "@hapi/hapi": "21.3.0",
"@hapi/inert": "7.0.0", "@hapi/inert": "7.0.1",
"sass": "1.57.1" "sass": "1.58.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.20.12", "@babel/core": "7.20.12",
@ -59,27 +59,28 @@
"@rollup/plugin-node-resolve": "15.0.1", "@rollup/plugin-node-resolve": "15.0.1",
"@rollup/plugin-replace": "5.0.2", "@rollup/plugin-replace": "5.0.2",
"@rollup/plugin-terser": "0.4.0", "@rollup/plugin-terser": "0.4.0",
"@vaadin/button": "23.3.6", "@vaadin/button": "23.3.7",
"@vaadin/grid": "23.3.6", "@vaadin/grid": "23.3.7",
"@vaadin/icons": "23.3.6", "@vaadin/icons": "23.3.7",
"@vaadin/password-field": "23.3.6", "@vaadin/password-field": "23.3.7",
"@vaadin/tooltip": "23.3.6", "@vaadin/tooltip": "23.3.7",
"asmcrypto.js": "2.3.2", "asmcrypto.js": "2.3.2",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"epml": "0.3.3", "epml": "0.3.3",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"lit": "2.6.1", "lit": "2.6.1",
"lit-translate": "2.0.1", "lit-translate": "2.0.1",
"localforage": "1.10.0",
"pwa-helpers": "0.9.1", "pwa-helpers": "0.9.1",
"redux": "4.2.0", "redux": "4.2.1",
"redux-thunk": "2.4.2", "redux-thunk": "2.4.2",
"rollup": "3.12.0", "rollup": "3.15.0",
"rollup-plugin-node-globals": "1.4.0", "rollup-plugin-node-globals": "1.4.0",
"rollup-plugin-progress": "1.1.2", "rollup-plugin-progress": "1.1.2",
"rollup-plugin-scss": "3.0.0", "rollup-plugin-scss": "3.0.0",
"rollup-plugin-web-worker-loader": "1.6.1" "rollup-plugin-web-worker-loader": "1.6.1"
}, },
"engines": { "engines": {
"node": ">=16.17.1" "node": ">=18.12.1"
} }
} }

View File

@ -18,6 +18,7 @@ class AppInfo extends connect(store)(LitElement) {
coreInfo: { type: Array }, coreInfo: { type: Array },
nodeConfig: { type: Object }, nodeConfig: { type: Object },
pageUrl: { type: String }, pageUrl: { type: String },
publicizeAddress: { type: String },
theme: { type: String, reflect: true } theme: { type: String, reflect: true }
} }
} }
@ -95,6 +96,7 @@ class AppInfo extends connect(store)(LitElement) {
this.coreInfo = [] this.coreInfo = []
this.nodeStatus = {} this.nodeStatus = {}
this.pageUrl = '' this.pageUrl = ''
this.publicizeAddress = ''
this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light'
this.publicKeyisOnChainConfirmation = false this.publicKeyisOnChainConfirmation = false
this.interval 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) { async confirmPublicKeyOnChain(address) {
const _computePow2 = async (chatBytes) => { const _computePow2 = async (chatBytes) => {
const difficulty = 14; const difficulty = 14
const path = window.parent.location.origin + '/memory-pow/memory-pow.wasm.full' const path = window.parent.location.origin + '/memory-pow/memory-pow.wasm.full'
const worker = new WebWorker(); const worker = new WebWorker();
let nonce = null let nonce = null
let chatBytesArray = null let chatBytesArray = null
await new Promise((res, rej) => { await new Promise((res, rej) => {
worker.postMessage({chatBytes, path, difficulty}); worker.postMessage({chatBytes, path, difficulty})
worker.onmessage = e => { worker.onmessage = e => {
worker.terminate() worker.terminate()
chatBytesArray = e.data.chatBytesArray chatBytesArray = e.data.chatBytesArray
nonce = e.data.nonce nonce = e.data.nonce
res() res()
} }
}) })
let _response = await routes.sign_chat({ let _response = await routes.sign_chat({
data: { data: {
@ -137,93 +161,74 @@ class AppInfo extends connect(store)(LitElement) {
chatBytesArray: chatBytesArray, chatBytesArray: chatBytesArray,
chatNonce: nonce chatNonce: nonce
}, },
})
return _response
}
}); let stop = false
return _response const checkPublicKey = async () => {
}; if (!stop) {
stop = true
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 = ''
try { try {
data = await res.text(); if(localStorage.getItem(this.publicizeAddress) === 'true') {
} catch (error) { clearInterval(this.interval)
data = { return
error: 'error'
} }
} const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
if(data === 'false' && this.nodeInfo.isSynchronizing !== true){ const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
let _reference = new Uint8Array(64); const url = `${nodeUrl}/addresses/publickey/${address}`
window.crypto.getRandomValues(_reference); const res = await fetch(url)
let reference = window.parent.Base58.encode(_reference); let data = ''
const chatRes = await routes.chat({ try {
data: { data = await res.text()
type: 19, } catch (error) {
nonce: store.getState().app.selectedAddress.nonce, data = {
params: { error: 'error'
lastReference: reference, }
proofOfWorkNonce: 0, }
fee: 0, if(data === 'false' && this.nodeInfo.isSynchronizing !== true) {
timestamp: Date.now(), 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 { try {
const powRes = await _computePow2(chatRes) const powRes = await _computePow2(chatRes)
if(powRes === true){ if(powRes === true) {
clearInterval(this.interval) clearInterval(this.interval)
localStorage.removeItem(this.publicizeAddress)
this.publicKeyisOnChainConfirmation = true localStorage.setItem(this.publicizeAddress, 'true')
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }
} }
if (!data.error && data !== 'false' && data) { if (!data.error && data !== 'false' && data) {
clearInterval(this.interval) clearInterval(this.interval)
localStorage.removeItem(this.publicizeAddress)
localStorage.setItem(this.publicizeAddress, 'true')
}
this.publicKeyisOnChainConfirmation = true } catch (error) {
} }
stop = false
} catch (error) { }
} }
stop = false this.interval = setInterval(checkPublicKey, 5000);
}
};
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)
} }
async getNodeInfo() { async getNodeInfo() {

View File

@ -4,6 +4,11 @@ import { store } from '../store.js'
import { Epml } from '../epml.js' import { Epml } from '../epml.js'
import { addTradeBotRoutes } from '../tradebot/addTradeBotRoutes.js' import { addTradeBotRoutes } from '../tradebot/addTradeBotRoutes.js'
import { get, translate, translateUnsafeHTML } from 'lit-translate' 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-icon-button/paper-icon-button.js'
import '@polymer/paper-progress/paper-progress.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.js'
import '../functional-components/side-menu-item.js' import '../functional-components/side-menu-item.js'
import './start-minting.js' import './start-minting.js'
import { setChatLastSeen } from '../redux/app/app-actions.js'
const parentEpml = new Epml({type: 'WINDOW', source: window.parent}) const parentEpml = new Epml({type: 'WINDOW', source: window.parent})
@ -207,7 +213,7 @@ class AppView extends connect(store)(LitElement) {
#balanceheader { #balanceheader {
flex: 0 0 24px; flex: 0 0 24px;
padding: 12px 12px 20px 12px; padding: 12px 12px 45px 12px;
border-bottom: 1px solid var(--border); border-bottom: 1px solid var(--border);
background: var(--sidetopbar); background: var(--sidetopbar);
} }
@ -330,6 +336,15 @@ class AppView extends connect(store)(LitElement) {
background-color: rgb(148, 146, 146); background-color: rgb(148, 146, 146);
cursor: pointer; 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> </side-menu>
</div> </div>
</div> </div>
<button class="balanceButton" @click="${() => this.shBalanceTicker()}">${translate("grouppage.gchange59")}</button>
<div id="theTicker" style="display: none; margin-bottom: 20px;">
<div id="balanceheader"> <div id="balanceheader">
<span class="balanceheadertext"> <span class="balanceheadertext">
${this.getAllBalancesLoading ? html` ${this.getAllBalancesLoading ? html`
@ -441,6 +458,7 @@ class AppView extends connect(store)(LitElement) {
</div> </div>
${this.getAllBalancesLoading ? html`<paper-progress indeterminate style="width: 100%; margin: 4px;"></paper-progress>` : ''} ${this.getAllBalancesLoading ? html`<paper-progress indeterminate style="width: 100%; margin: 4px;"></paper-progress>` : ''}
${this.balanceTicker} ${this.balanceTicker}
</div>
<app-info></app-info> <app-info></app-info>
</div> </div>
</app-header-layout> </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 getOpenTradesBTC()
await appDelay(1000) await appDelay(1000)
await getOpenTradesLTC() await getOpenTradesLTC()
@ -1397,6 +1426,16 @@ class AppView extends connect(store)(LitElement) {
await getOpenTradesRVN() await getOpenTradesRVN()
await appDelay(1000) await appDelay(1000)
await getOpenTradesARRR() 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() { async getNodeType() {

View File

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

View File

@ -95,12 +95,12 @@ class AccountView extends connect(store)(LitElement) {
getAvatar() { getAvatar() {
let numberBlocks = (this.accountInfo.addressInfo.blocksMinted + this.accountInfo.addressInfo.blocksMintedAdjustment); let numberBlocks = (this.accountInfo.addressInfo.blocksMinted + this.accountInfo.addressInfo.blocksMintedAdjustment);
if (Number.isNaN(numberBlocks) || numberBlocks == "" || numberBlocks === null) { 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 { } else {
const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] const avatarNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]
const avatarUrl = avatarNode.protocol + '://' + avatarNode.domain + ':' + avatarNode.port 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()}` 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> </div>
</paper-dialog> </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 class="card-explorer-container">
<div id="first-explorer-section"> <div id="first-explorer-section">
${this.boughtBTCTemplate()} ${this.boughtBTCTemplate()}
@ -1191,7 +1191,7 @@ class UserInfoView extends connect(store)(LitElement) {
</div> </div>
</paper-dialog> </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 class="card-explorer-container">
<div id="first-explorer-section"> <div id="first-explorer-section">
${this.soldBTCTemplate()} ${this.soldBTCTemplate()}

View File

@ -2,7 +2,6 @@ import { store } from '../../store.js'
import { doPageUrl } from '../../redux/app/app-actions.js' import { doPageUrl } from '../../redux/app/app-actions.js'
export const newMessage = (data) => { export const newMessage = (data) => {
const alert = playSound(data.sound) const alert = playSound(data.sound)
// Should I show notification ? // Should I show notification ?
@ -18,7 +17,7 @@ export const newMessage = (data) => {
} }
notify.onclick = (e) => { notify.onclick = (e) => {
const pageUrl = `/app/q-chat/${data.req.url}` const pageUrl = `/app/q-chat/?chat=${data.req.url}`
store.dispatch(doPageUrl(pageUrl)) store.dispatch(doPageUrl(pageUrl))
} }
} else { } else {
@ -26,7 +25,7 @@ export const newMessage = (data) => {
const notify = new Notification(data.title, data.options) const notify = new Notification(data.title, data.options)
notify.onclick = (e) => { notify.onclick = (e) => {
const pageUrl = `/app/q-chat/${data.req.url}` const pageUrl = `/app/q-chat/?chat=${data.req.url}`
store.dispatch(doPageUrl(pageUrl)) 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 NODE_CONFIG_STREAM_NAME = 'node_config'
const COPY_MENU_SWITCH = 'copy_menu_switch' const COPY_MENU_SWITCH = 'copy_menu_switch'
const FRAME_PASTE_MENU_SWITCH = 'frame_paste_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 loggedInStream = new EpmlStream(LOGIN_STREAM_NAME, () => store.getState().app.loggedIn)
export const configStream = new EpmlStream(CONFIG_STREAM_NAME, () => store.getState().config) 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 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 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 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 = { let oldState = {
@ -46,6 +48,9 @@ store.subscribe(() => {
if (oldState.app.framePasteMenuSwitch !== state.app.framePasteMenuSwitch) { if (oldState.app.framePasteMenuSwitch !== state.app.framePasteMenuSwitch) {
framePasteMenuSwitchStream.emit(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) { if (oldState.app.selectedAddress !== state.app.selectedAddress) {
selectedAddressStream.emit({ selectedAddressStream.emit({

View File

@ -1,5 +1,5 @@
// Core App Actions here... // 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) => { export const doUpdateBlockInfo = (blockObj) => {
return (dispatch, getState) => { return (dispatch, getState) => {
@ -105,3 +105,31 @@ const framePasteMenuSwitch = (payload) => {
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 COPY_MENU_SWITCH = 'COPY_MENU_SWITCH'
export const PASTE_MENU_SWITCH = 'PASTE_MENU_SWITCH' export const PASTE_MENU_SWITCH = 'PASTE_MENU_SWITCH'
export const FRAME_PASTE_MENU_SWITCH = 'FRAME_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. // 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 { initWorkersReducer } from './reducers/init-workers.js'
import { loginReducer } from './reducers/login-reducer.js' import { loginReducer } from './reducers/login-reducer.js'
import { setNode, addNode } from './reducers/manage-node.js' import { setNode, addNode } from './reducers/manage-node.js'
import localForage from "localforage";
const chatLastSeen = localForage.createInstance({
name: "chat-last-seen",
});
const INITIAL_STATE = { const INITIAL_STATE = {
loggedIn: false, loggedIn: false,
@ -42,7 +48,9 @@ const INITIAL_STATE = {
framePasteMenuSwitch: { framePasteMenuSwitch: {
isOpen: false, isOpen: false,
elementId: '' elementId: ''
} },
autoLoadImageChats: loadStateFromLocalStorage('autoLoadImageChats') || [],
chatLastSeen: []
} }
export default (state = INITIAL_STATE, action) => { export default (state = INITIAL_STATE, action) => {
@ -146,6 +154,60 @@ export default (state = INITIAL_STATE, action) => {
...state, ...state,
framePasteMenuSwitch: action.payload 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: default:
return state return state
} }

View File

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

View File

@ -159,7 +159,7 @@ const ADDRESS_VERSION = 58
const PROXY_URL = "/proxy/" const PROXY_URL = "/proxy/"
// Chat reference timestamp // 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 // 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]) 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 PaymentTransaction from './PaymentTransaction.js'
import RegisterNameTransaction from './names/RegisterNameTransaction.js' import RegisterNameTransaction from './names/RegisterNameTransaction.js'
import UpdateNameTransaction from './names/UpdateNameTransaction.js'
import SellNameTransacion from './names/SellNameTransacion.js' import SellNameTransacion from './names/SellNameTransacion.js'
import CancelSellNameTransacion from './names/CancelSellNameTransacion.js' import CancelSellNameTransacion from './names/CancelSellNameTransacion.js'
import BuyNameTransacion from './names/BuyNameTransacion.js' import BuyNameTransacion from './names/BuyNameTransacion.js'
@ -25,6 +26,7 @@ import TransferPrivsTransaction from './TransferPrivsTransaction.js'
export const transactionTypes = { export const transactionTypes = {
2: PaymentTransaction, 2: PaymentTransaction,
3: RegisterNameTransaction, 3: RegisterNameTransaction,
4: UpdateNameTransaction,
5: SellNameTransacion, 5: SellNameTransacion,
6: CancelSellNameTransacion, 6: CancelSellNameTransacion,
7: BuyNameTransacion, 7: BuyNameTransacion,

View File

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

View File

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

View File

@ -1,11 +1,15 @@
import { LitElement, html, css } from 'lit' import { LitElement, html, css } from 'lit'
import { render } from 'lit/html.js' import { render } from 'lit/html.js'
import { Epml } from '../../../epml.js' import { Epml } from '../../../epml.js'
import localForage from "localforage";
import { translate} from 'lit-translate';
import '@material/mwc-icon' import '@material/mwc-icon'
const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
const chatLastSeen = localForage.createInstance({
name: "chat-last-seen",
});
class ChatHead extends LitElement { class ChatHead extends LitElement {
static get properties() { static get properties() {
return { return {
@ -15,7 +19,8 @@ class ChatHead extends LitElement {
iconName: { type: String }, iconName: { type: String },
activeChatHeadUrl: { type: String }, activeChatHeadUrl: { type: String },
isImageLoaded: { type: Boolean }, isImageLoaded: { type: Boolean },
setActiveChatHeadUrl: {attribute: false} setActiveChatHeadUrl: {attribute: false},
lastReadMessageTimestamp: {type: Number}
} }
} }
@ -24,9 +29,13 @@ class ChatHead extends LitElement {
li { li {
width: 100%; width: 100%;
padding: 7px 5px 7px 5px; padding: 10px 5px 10px 5px;
cursor: pointer; cursor: pointer;
width: 100%; width: 100%;
box-sizing: border-box;
display: flex;
align-items: flex-start;
} }
li:hover { li:hover {
@ -44,12 +53,21 @@ class ChatHead extends LitElement {
color: var(--chat-group); color: var(--chat-group);
} }
.about {
margin-top: 8px;
}
.about { .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 { .status {
@ -64,6 +82,13 @@ class ChatHead extends LitElement {
clear: both; clear: both;
height: 0; 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.activeChatHeadUrl = ''
this.isImageLoaded = false this.isImageLoaded = false
this.imageFetches = 0 this.imageFetches = 0
this.lastReadMessageTimestamp = 0
this.loggedInAddress = window.parent.reduxStore.getState().app.selectedAddress.address
} }
createImage(imageUrl) { createImage(imageUrl) {
const imageHTMLRes = new Image(); const imageHTMLRes = new Image();
imageHTMLRes.src = imageUrl; imageHTMLRes.src = imageUrl;
imageHTMLRes.style= "width:40px; height:40px; float: left; border-radius:50%"; imageHTMLRes.style= "width:40px; height:40px; float: left; border-radius:50%";
@ -99,7 +126,7 @@ class ChatHead extends LitElement {
setTimeout(() => { setTimeout(() => {
this.imageFetches = this.imageFetches + 1; this.imageFetches = this.imageFetches + 1;
imageHTMLRes.src = imageUrl; imageHTMLRes.src = imageUrl;
}, 500); }, 750);
} else { } else {
@ -109,32 +136,61 @@ class ChatHead extends LitElement {
return imageHTMLRes; return imageHTMLRes;
} }
render() { render() {
let avatarImg = ''; let avatarImg = '';
let backupAvatarImg = '' let backupAvatarImg = ''
let isUnread = false
if(this.chatInfo.name){ if(this.chatInfo.name){
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`; const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.chatInfo.name}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`;
avatarImg= this.createImage(avatarUrl) 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` return html`
<li @click=${() => this.getUrl(this.chatInfo.url)} class="clearfix ${this.activeChatHeadUrl === this.chatInfo.url ? 'active' : ''}"> <li @click=${() => this.getUrl(this.chatInfo.url)} class="clearfix ${this.activeChatHeadUrl === this.chatInfo.url ? 'active' : ''}">
${this.isImageLoaded ? html`${avatarImg}` : html`` } ${this.isImageLoaded ? html`${avatarImg}` : html`` }
${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`<mwc-icon class="img-icon">account_circle</mwc-icon>` : html`` } ${!this.isImageLoaded && !this.chatInfo.name && !this.chatInfo.groupName ? html`<mwc-icon class="img-icon">account_circle</mwc-icon>` : 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.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; float: left; border-radius:50%; background: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadBgActive)' : 'var(--chatHeadBg)' }; color: ${this.activeChatHeadUrl === this.chatInfo.url ? 'var(--chatHeadTextActive)' : 'var(--chatHeadText)' }; font-weight:bold; display: flex; justify-content: center; align-items: center; text-transform: capitalize">${this.chatInfo.groupName.charAt(0)}</div>`: ''} ${!this.isImageLoaded && this.chatInfo.groupName ? html`<div style="width: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="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>
<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> </li>
` `
} }
firstUpdated() { async firstUpdated() {
let configLoaded = false let configLoaded = false
this.lastReadMessageTimestamp = await chatLastSeen.getItem(this.chatInfo.url) || 0
parentEpml.ready().then(() => { parentEpml.ready().then(() => {
parentEpml.subscribe('selected_address', async selectedAddress => { parentEpml.subscribe('selected_address', async selectedAddress => {
this.selectedAddress = {} this.selectedAddress = {}
@ -142,6 +198,15 @@ class ChatHead extends LitElement {
if (!selectedAddress || Object.entries(selectedAddress).length === 0) return if (!selectedAddress || Object.entries(selectedAddress).length === 0) return
this.selectedAddress = selectedAddress 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 => { parentEpml.subscribe('config', c => {
if (!configLoaded) { if (!configLoaded) {
configLoaded = true configLoaded = true
@ -156,7 +221,18 @@ class ChatHead extends LitElement {
if(changedProperties.has('activeChatHeadUrl')){ if(changedProperties.has('activeChatHeadUrl')){
return true return true
} }
if(changedProperties.has('lastReadMessageTimestamp')){
return true
}
if(changedProperties.has('chatInfo')){ if(changedProperties.has('chatInfo')){
const prevChatInfo = changedProperties.get('chatInfo')
if(prevChatInfo.address !== this.chatInfo.address){
this.isImageLoaded = false
this.requestUpdate()
}
return true 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 { .message-data-my-name {
color: #cf21e8; color: #05be0e;
text-shadow: 0 0 3px #cf21e8; font-weight: bold;
} }
.message-data-time { .message-data-time {
color: #888888; color: #888888;
font-size: 13px; font-size: 13px;
user-select: none; user-select: none;
float: right; display: flex;
padding-left: 15px; justify-content: space-between;
text-align: right; width: 100%;
padding-top: 2px;
} }
.message-data-time-hidden { .message-data-time-hidden {
@ -96,10 +97,10 @@ export const chatStyles = css`
color: #888888; color: #888888;
font-size: 13px; font-size: 13px;
user-select: none; user-select: none;
float: right; display: flex;
padding-left: 15px; justify-content: space-between;
padding-bottom: 3px; width: 100%;
text-align: right; padding-top: 2px;
} }
.message-user-info { .message-user-info {
@ -138,6 +139,11 @@ export const chatStyles = css`
min-width: 150px; min-width: 150px;
} }
.message-myBg {
background-color: var(--chat-bubble-myBg) !important;
}
.message-triangle { .message-triangle {
position: relative; position: relative;
} }
@ -154,10 +160,30 @@ export const chatStyles = css`
border-color: transparent transparent var(--chat-bubble-bg) transparent; 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 { .message-reactions {
background-color: transparent; background-color: transparent;
width: calc(100% - 54px); width: calc(100% - 54px);
margin-left: 54px; margin-left: 54px;
display: flex;
flex-flow: row wrap;
justify-content: left;
gap: 8px;
} }
.original-message { .original-message {
@ -186,9 +212,7 @@ export const chatStyles = css`
} }
.original-message-sender { .original-message-sender {
margin: 0 0 5px 0;
color: var(--mdc-theme-primary); color: var(--mdc-theme-primary);
cursor: pointer;
} }
.replied-message { .replied-message {
@ -199,6 +223,7 @@ export const chatStyles = css`
max-width: 300px; max-width: 300px;
max-height: 40px; max-height: 40px;
} }
.replied-message p { .replied-message p {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
@ -393,11 +418,11 @@ export const chatStyles = css`
} }
.reactions-bg { .reactions-bg {
position: relative;
background-color: #d5d5d5; background-color: #d5d5d5;
border-radius: 10px; border-radius: 10px;
padding: 5px; padding: 5px;
color: black; color: black;
margin-right: 10px;
transition: all 0.1s ease-in-out; transition: all 0.1s ease-in-out;
border: 0.5px solid transparent; border: 0.5px solid transparent;
cursor: pointer; cursor: pointer;
@ -413,7 +438,7 @@ export const chatStyles = css`
.message-data-level { .message-data-level {
height: 21px; height: 21px;
width: 21px; width: auto;
overflow: hidden; overflow: hidden;
} }
@ -422,6 +447,10 @@ export const chatStyles = css`
height: 40vh; height: 40vh;
} }
.hideImg {
visibility: hidden;
}
.image-deleted-msg { .image-deleted-msg {
font-family: Roboto, sans-serif; font-family: Roboto, sans-serif;
font-size: 14px; font-size: 14px;
@ -617,6 +646,7 @@ export const chatStyles = css`
white-space: pre-wrap; white-space: pre-wrap;
margin: 0px; margin: 0px;
} }
.replied-message pre code { .replied-message pre code {
color: inherit; color: inherit;
padding: 0; padding: 0;
@ -624,12 +654,10 @@ export const chatStyles = css`
font-size: 0.8rem; font-size: 0.8rem;
} }
.replied-message img { .replied-message img {
width: 1.7em; width: 1.7em;
height: 1.5em; height: 1.5em;
margin: 0px; margin: 0px;
} }
.replied-message blockquote { .replied-message blockquote {
@ -643,6 +671,86 @@ export const chatStyles = css`
margin: 2rem 0; 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{ .blink-bg{
border-radius: 8px; border-radius: 8px;
animation: blinkingBackground 3s; animation: blinkingBackground 3s;
@ -652,4 +760,46 @@ export const chatStyles = css`
100% { background-color:rgba(var(--menuactivergb), 0)} 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 { chatStyles } from './ChatScroller-css.js'
import { Epml } from "../../../epml"; import { Epml } from "../../../epml";
import { cropAddress } from "../../utils/cropAddress"; import { cropAddress } from "../../utils/cropAddress";
import { roundToNearestDecimal } from '../../utils/roundToNearestDecimal.js';
import './LevelFounder.js'; import './LevelFounder.js';
import './NameMenu.js'; import './NameMenu.js';
import './ChatModals.js'; import './ChatModals.js';
@ -13,11 +14,14 @@ import './WrapperModal';
import "./UserInfo/UserInfo"; import "./UserInfo/UserInfo";
import '@vaadin/icons'; import '@vaadin/icons';
import '@vaadin/icon'; import '@vaadin/icon';
import '@vaadin/tooltip';
import '@material/mwc-button'; import '@material/mwc-button';
import '@material/mwc-dialog'; import '@material/mwc-dialog';
import '@material/mwc-icon'; import '@material/mwc-icon';
import { EmojiPicker } from 'emoji-picker-js'; 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 StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline'; import Underline from '@tiptap/extension-underline';
import Highlight from '@tiptap/extension-highlight' import Highlight from '@tiptap/extension-highlight'
@ -52,7 +56,8 @@ class ChatScroller extends LitElement {
userName: { type: String }, userName: { type: String },
selectedHead: { type: Object }, selectedHead: { type: Object },
goToRepliedMessage: { attribute: false }, 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.hideMessages = JSON.parse(localStorage.getItem("MessageBlockedAddresses") || "[]")
this.openTipUser = false; this.openTipUser = false;
this.openUserInfo = false; this.openUserInfo = false;
this.listSeenMessages= []
}
addSeenMessage(val){
this.listSeenMessages.push(val)
} }
render() { render() {
@ -76,7 +86,6 @@ class ChatScroller extends LitElement {
let timestamp; let timestamp;
let sender; let sender;
let repliedToData; let repliedToData;
let firstMessageInChat; let firstMessageInChat;
if (index === 0) { if (index === 0) {
@ -139,11 +148,14 @@ class ChatScroller extends LitElement {
.setUserName=${(val) => this.setUserName(val)} .setUserName=${(val) => this.setUserName(val)}
id=${message.reference} id=${message.reference}
.goToRepliedMessage=${this.goToRepliedMessage} .goToRepliedMessage=${this.goToRepliedMessage}
.addSeenMessage=${(val)=> this.addSeenMessage(val)}
.listSeenMessages=${this.listSeenMessages}
chatId=${this.chatId}
> >
</message-template>` </message-template>`
) )
})} })}
<div id='downObserver'></div> <div style=${"height: 1px;"} id='downObserver'></div>
</ul> </ul>
` `
} }
@ -273,6 +285,7 @@ class MessageTemplate extends LitElement {
openDialogImage: { type: Boolean }, openDialogImage: { type: Boolean },
openDialogGif: { type: Boolean }, openDialogGif: { type: Boolean },
openDeleteImage: { type: Boolean }, openDeleteImage: { type: Boolean },
openDeleteAttachment: { type: Boolean },
isImageLoaded: { type: Boolean }, isImageLoaded: { type: Boolean },
isGifLoaded: { type: Boolean }, isGifLoaded: { type: Boolean },
isFirstMessage: { type: Boolean }, isFirstMessage: { type: Boolean },
@ -287,6 +300,9 @@ class MessageTemplate extends LitElement {
setUserName: { attribute: false }, setUserName: { attribute: false },
openTipUser:{ type: Boolean }, openTipUser:{ type: Boolean },
goToRepliedMessage: { attribute: false }, goToRepliedMessage: { attribute: false },
listSeenMessages: { type: Array },
addSeenMessage: { attribute: false },
chatId: { type: String },
} }
} }
@ -306,7 +322,7 @@ class MessageTemplate extends LitElement {
this.isFirstMessage = false this.isFirstMessage = false
this.isSingleMessageInGroup = false this.isSingleMessageInGroup = false
this.isLastMessageInGroup = false this.isLastMessageInGroup = false
this.viewImage = false this.viewImage = false
} }
static styles = [chatStyles] 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() { render() {
const hidemsg = this.hideMessages; const hidemsg = this.hideMessages;
let message = ""; let message = "";
@ -346,8 +392,11 @@ class MessageTemplate extends LitElement {
let image = null; let image = null;
let gif = null; let gif = null;
let isImageDeleted = false; let isImageDeleted = false;
let isAttachmentDeleted = false;
let version = 0; let version = 0;
let isForwarded = false let isForwarded = false
let isEdited = false
let attachment = null;
try { try {
const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage); const parsedMessageObj = JSON.parse(this.messageObj.decodedMessage);
if (parsedMessageObj.version.toString() === '2' && parsedMessageObj.messageText) { if (parsedMessageObj.version.toString() === '2' && parsedMessageObj.messageText) {
@ -365,9 +414,14 @@ class MessageTemplate extends LitElement {
message = parsedMessageObj.messageText; message = parsedMessageObj.messageText;
repliedToData = this.messageObj.repliedToData; repliedToData = this.messageObj.repliedToData;
isImageDeleted = parsedMessageObj.isImageDeleted; isImageDeleted = parsedMessageObj.isImageDeleted;
isAttachmentDeleted = parsedMessageObj.isAttachmentDeleted;
reactions = parsedMessageObj.reactions || []; reactions = parsedMessageObj.reactions || [];
version = parsedMessageObj.version version = parsedMessageObj.version;
isForwarded = parsedMessageObj.type === 'forward' 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) { if (parsedMessageObj.images && Array.isArray(parsedMessageObj.images) && parsedMessageObj.images.length > 0) {
image = parsedMessageObj.images[0]; image = parsedMessageObj.images[0];
} }
@ -389,15 +443,16 @@ class MessageTemplate extends LitElement {
let levelFounder = ''; let levelFounder = '';
let hideit = hidemsg.includes(this.messageObj.sender); let hideit = hidemsg.includes(this.messageObj.sender);
let forwarded = '' let forwarded = ''
let edited = ''
levelFounder = html`<level-founder checkleveladdress="${this.messageObj.sender}"></level-founder>`; levelFounder = html`<level-founder checkleveladdress="${this.messageObj.sender}"></level-founder>`;
if (this.messageObj.senderName) { if (this.messageObj.senderName) {
const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node];
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
const avatarUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.messageObj.senderName}/qortal_avatar?async=true&apiKey=${myNode.apiKey}`; 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 { } 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) => { const createImage = (imageUrl) => {
@ -415,15 +470,12 @@ class MessageTemplate extends LitElement {
setTimeout(() => { setTimeout(() => {
this.imageFetches = this.imageFetches + 1; this.imageFetches = this.imageFetches + 1;
imageHTMLRes.src = imageUrl; imageHTMLRes.src = imageUrl;
}, 500); }, 2000);
} else { } else {
imageHTMLRes.src = '/img/chain.png'; setTimeout(() => {
imageHTMLRes.style= "max-width:45vh; max-height:20vh; border-radius: 5px; filter: opacity(0.5)"; this.imageFetches = this.imageFetches + 1;
imageHTMLRes.onclick= () => { imageHTMLRes.src = imageUrl;
}, 6000);
}
this.isImageLoaded = true
} }
}; };
return imageHTMLRes; return imageHTMLRes;
@ -460,7 +512,7 @@ class MessageTemplate extends LitElement {
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port; const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port;
imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`; imageUrl = `${nodeUrl}/arbitrary/${image.service}/${image.name}/${image.identifier}?async=true&apiKey=${myNode.apiKey}`;
if(this.viewImage || this.myAddress === this.messageObj.sender){ if (this.viewImage || this.myAddress === this.messageObj.sender) {
imageHTML = createImage(imageUrl); imageHTML = createImage(imageUrl);
imageHTMLDialog = createImage(imageUrl) imageHTMLDialog = createImage(imageUrl)
imageHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px"; imageHTMLDialog.style= "height: auto; max-height: 80vh; width: auto; max-width: 80vw; object-fit: contain; border-radius: 5px";
@ -489,12 +541,17 @@ class MessageTemplate extends LitElement {
</span> </span>
`; `;
edited = html`
<span class="edited-message-style">
${translate("chatpage.cchange68")}
</span>
`;
if (repliedToData) { if (repliedToData) {
try { try {
const parsedMsg = JSON.parse(repliedToData.decodedMessage); const parsedMsg = JSON.parse(repliedToData.decodedMessage);
repliedToData.decodedMessage = parsedMsg; repliedToData.decodedMessage = parsedMsg;
} catch (error) { } catch (error) {
console.error(error);
} }
} }
@ -533,9 +590,12 @@ class MessageTemplate extends LitElement {
`} `}
<div <div
class="${`message-subcontainer2 class="${`message-subcontainer2
${((this.isFirstMessage === true && this.isSingleMessageInGroup === false) || ${this.myAddress === this.messageObj.sender && "message-myBg" }
(this.isSingleMessageInGroup === true && this.isLastMessageInGroup === true)) && ${(((this.isFirstMessage === true && this.isSingleMessageInGroup === false) ||
'message-triangle'}`}" (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} style="${(this.isSingleMessageInGroup === true && this.isLastMessageInGroup === false) ? 'margin-bottom: 0;' : null}
${(this.isFirstMessage === false && this.isSingleMessageInGroup === true && this.isLastMessageInGroup === false) ${(this.isFirstMessage === false && this.isSingleMessageInGroup === true && this.isLastMessageInGroup === false)
? 'border-radius: 8px 25px 25px 8px;' ? 'border-radius: 8px 25px 25px 8px;'
@ -580,58 +640,66 @@ class MessageTemplate extends LitElement {
${repliedToData && html` ${repliedToData && html`
<div class="original-message" <div class="original-message"
@click=${()=> { @click=${()=> {
this.goToRepliedMessage(repliedToData) this.goToRepliedMessage(repliedToData, this.messageObj)
}}> }}>
<p <p
style=${"cursor: pointer; margin: 0 0 5px 0;"}
class=${this.myAddress !== repliedToData.sender
class="original-message-sender"> ? "original-message-sender"
: "message-data-my-name"}>
${repliedToData.senderName ?? cropAddress(repliedToData.sender)} ${repliedToData.senderName ?? cropAddress(repliedToData.sender)}
</p> </p>
<p class="replied-message"> <p class="replied-message">
${version.toString() === '1' ? html`
${repliedToData.decodedMessage.messageText}
${version.toString() === '1' ? html` ` : ''}
${repliedToData.decodedMessage.messageText} ${version.toString() === '2' ? html`
` : ''} ${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText, [
${version.toString() === '2' ? html` StarterKit,
${unsafeHTML(generateHTML(repliedToData.decodedMessage.messageText, [ Underline,
StarterKit, Highlight
Underline, // other extensions …
Highlight ]))}
// other extensions … `
]))} : ''}
` : ''}
<!-- ${repliedToData.decodedMessage.messageText} -->
</p> </p>
</div> </div>
`} `}
${image && !isImageDeleted && !this.viewImage && this.myAddress !== this.messageObj.sender ? html` ${image && !isImageDeleted && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
<div <div
@click=${()=> { @click=${()=> {
this.viewImage = true this.viewImage = true
this.addSeenMessage(this.messageObj.reference)
}} }}
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')}
style=${this.isFirstMessage && "margin-top: 10px;"}> style=${this.isFirstMessage && "margin-top: 10px;"}>
<div style="display:flex;width:100%;height:100%;justify-content:center;align-items:center;cursor:pointer;color:var(--black)"> <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>
</div> </div>
` : html``} ` : 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` ${image && !isImageDeleted && (this.viewImage || this.myAddress === this.messageObj.sender) ? html`
<div <div
class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : ''].join(' ')} class=${[`image-container`, !this.isImageLoaded ? 'defaultSize' : '', !this.isImageLoaded ? 'hideImg': ''].join(' ')}
style=${this.isFirstMessage && "margin-top: 10px;"}> style=${this.isFirstMessage && "margin-top: 10px;"}>
${imageHTML}<vaadin-icon ${imageHTML}
${this.myAddress === this.messageObj.sender ? html`
<vaadin-icon
@click=${() => { @click=${() => {
this.openDeleteImage = true; this.openDeleteImage = true;
this.chatE
}} }}
class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon> class="image-delete-icon" icon="vaadin:close" slot="icon"></vaadin-icon>
` : ''}
</div> </div>
` : image && isImageDeleted ? html` ` : image && isImageDeleted ? html`
<p class="image-deleted-msg">This image has been deleted</p> <p class="image-deleted-msg">${translate("chatpage.cchange80")}</p>
` : html``} ` : html``}
${gif && !this.viewImage && this.myAddress !== this.messageObj.sender ? html` ${gif && !this.viewImage && this.myAddress !== this.messageObj.sender ? html`
<div <div
@ -652,6 +720,51 @@ class MessageTemplate extends LitElement {
${gifHTML} ${gifHTML}
</div> </div>
` : html``} ` : 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 <div
id="messageContent" id="messageContent"
class="message" class="message"
@ -662,7 +775,14 @@ class MessageTemplate extends LitElement {
${version.toString() === '1' ? html` ${version.toString() === '1' ? html`
${unsafeHTML(this.emojiPicker.parse(replacedMessage))} ${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.isSingleMessageInGroup === true &&
this.isLastMessageInGroup === true) || this.isLastMessageInGroup === true) ||
(this.isFirstMessage === true && (this.isFirstMessage === true &&
@ -671,6 +791,14 @@ class MessageTemplate extends LitElement {
? 'message-data-time' ? 'message-data-time'
: 'message-data-time-hidden' : 'message-data-time-hidden'
}"> }">
${isEdited ?
html`
<span>
${edited}
</span>
`
: ''
}
<message-time timestamp=${this.messageObj.timestamp}></message-time> <message-time timestamp=${this.messageObj.timestamp}></message-time>
</div> </div>
</div> </div>
@ -705,17 +833,66 @@ class MessageTemplate extends LitElement {
</div> </div>
<div class="message-reactions" style="${reactions.length > 0 && <div class="message-reactions" style="${reactions.length > 0 &&
'margin-top: 10px; margin-bottom: 5px;'}"> 'margin-top: 10px; margin-bottom: 5px;'}">
${reactions.map((reaction)=> { ${reactions.map((reaction, index)=> {
return html` return html`
<span <span
@click=${() => this.sendMessage({ @click=${() => this.sendMessage({
type: 'reaction', type: 'reaction',
editedMessageObj: this.messageObj, editedMessageObj: this.messageObj,
reaction: reaction.type, reaction: reaction.type,
})} })}
class="reactions-bg"> id=${`reactions-${index}`}
${reaction.type} ${reaction.qty} class="reactions-bg">
</span>` ${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>
</div> </div>
@ -782,7 +959,7 @@ class MessageTemplate extends LitElement {
this.openDeleteImage = false; this.openDeleteImage = false;
}}> }}>
<div class="delete-image-msg"> <div class="delete-image-msg">
<p>Are you sure you want to delete this image?</p> <p>${translate("chatpage.cchange78")}</p>
</div> </div>
<div class="modal-button-row" @click=${() => this.openDeleteImage = false}> <div class="modal-button-row" @click=${() => this.openDeleteImage = false}>
<button class="modal-button-red"> <button class="modal-button-red">
@ -800,6 +977,34 @@ class MessageTemplate extends LitElement {
</button> </button>
</div> </div>
</mwc-dialog> </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) this.setForwardProperties(stringifyMessageObject)
} catch (error) { } catch (error) {
console.log({error})
} }
} }
render() { render() {
return html` return html`
<div class="container"> <div class="container">
@ -919,7 +1122,6 @@ class ChatMenu extends LitElement {
this.setToggledMessage(this.originalMessage) this.setToggledMessage(this.originalMessage)
this.emojiPicker.togglePicker(e.target) this.emojiPicker.togglePicker(e.target)
} catch (error) { } catch (error) {
console.log({error})
} }
}} }}
@ -930,6 +1132,10 @@ class ChatMenu extends LitElement {
class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`} class=${`menu-icon ${!this.firstMessageInChat ? "tooltip" : ""}`}
data-text="${translate("blockpage.bcchange14")}" data-text="${translate("blockpage.bcchange14")}"
@click="${() => { @click="${() => {
if (this.version === '0') {
this.versionErrorSnack()
return
}
this.messageForwardFunc() this.messageForwardFunc()
}}"> }}">
<vaadin-icon icon="vaadin:arrow-forward" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:arrow-forward" slot="icon"></vaadin-icon>
@ -954,7 +1160,7 @@ class ChatMenu extends LitElement {
this.versionErrorSnack() this.versionErrorSnack()
return return
} }
this.setRepliedToMessageObj(this.originalMessage); this.setRepliedToMessageObj({...this.originalMessage, version: this.version});
}}"> }}">
<vaadin-icon icon="vaadin:reply" slot="icon"></vaadin-icon> <vaadin-icon icon="vaadin:reply" slot="icon"></vaadin-icon>
</div> </div>

View File

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

View File

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

View File

@ -19,7 +19,7 @@ class TimeAgo extends LitElement {
updated(changedProps) { updated(changedProps) {
changedProps.forEach((OldProp, name) => { changedProps.forEach((OldProp, name) => {
if (name === 'timeIso') { if (name === 'timeIso' || name === 'timestamp') {
this.renderTime(this.timestamp) this.renderTime(this.timestamp)
} }
}); });
@ -35,7 +35,6 @@ class TimeAgo extends LitElement {
} }
render() { render() {
return html` return html`
<time-ago datetime=${this.timeIso} format=${this.format}> </time-ago> <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 ? ${!this.isImageLoaded && this.selectedHead && !this.selectedHead.name ?
html` html`
<div class="avatar-container"> <div class="avatar-container">
<img src="/img/qortal-chat-logo.png" alt="avatar" /> <img src="/img/incognito.png" alt="avatar" />
</div>` </div>`
: ""} : ""}
<div class="user-info-header"> <div class="user-info-header">

View File

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

View File

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

View File

@ -1,9 +1,14 @@
import { LitElement, html, css } from 'lit'; import { LitElement, html, css } from 'lit';
import { render } from 'lit/html.js'; import { render } from 'lit/html.js';
import { passiveSupport } from 'passive-events-support/src/utils'
passiveSupport({
events: ['touchstart']
})
import { Epml } from '../../../../epml.js'; import { Epml } from '../../../../epml.js';
import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate'; import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate';
import { qchatStyles } from './q-chat-css.src.js' import { qchatStyles } from './q-chat-css.src.js'
import WebWorker from 'web-worker:./computePowWorker.src.js'; import WebWorker from 'web-worker:./computePowWorker.src.js';
import {repeat} from 'lit/directives/repeat.js';
registerTranslateConfig({ registerTranslateConfig({
loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) loader: lang => fetch(`/language/${lang}.json`).then(res => res.json())
@ -24,8 +29,9 @@ import '@vaadin/grid'
import StarterKit from '@tiptap/starter-kit' import StarterKit from '@tiptap/starter-kit'
import Underline from '@tiptap/extension-underline'; import Underline from '@tiptap/extension-underline';
import Placeholder from '@tiptap/extension-placeholder' 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 }) const parentEpml = new Epml({ type: 'WINDOW', source: window.parent })
class Chat extends LitElement { class Chat extends LitElement {
@ -51,7 +57,7 @@ class Chat extends LitElement {
userFoundModalOpen: { type: Boolean }, userFoundModalOpen: { type: Boolean },
userSelected: { type: Object }, userSelected: { type: Object },
editor: {type: Object}, editor: {type: Object},
groupInvites: { type: Array } groupInvites: { type: Array },
} }
} }
@ -114,6 +120,7 @@ class Chat extends LitElement {
} }
async connectedCallback() { async connectedCallback() {
super.connectedCallback(); super.connectedCallback();
await this.getUpdateCompleteTextEditor(); await this.getUpdateCompleteTextEditor();
@ -144,14 +151,35 @@ 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() { disconnectedCallback() {
super.disconnectedCallback(); super.disconnectedCallback();
this.editor.destroy() this.editor.destroy();
this.unsubscribeStore();
} }
updatePlaceholder(editor, text){ updatePlaceholder(editor, text){
editor.extensionManager.extensions.forEach((extension) => { editor.extensionManager.extensions.forEach((extension) => {
if (extension.name === "placeholder") { if (extension.name === "placeholder") {
@ -211,7 +239,7 @@ class Chat extends LitElement {
</div> </div>
<div class="chat-history"> <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>
</div> </div>
<!-- Start Chatting Dialog --> <!-- Start Chatting Dialog -->
@ -363,6 +391,8 @@ class Chat extends LitElement {
` `
} }
async firstUpdated() { async firstUpdated() {
this.changeLanguage(); this.changeLanguage();
this.changeTheme(); this.changeTheme();
@ -389,26 +419,7 @@ class Chat extends LitElement {
this.shadowRoot.getElementById('messageBox').addEventListener('keydown', stopKeyEventPropagation); this.shadowRoot.getElementById('messageBox').addEventListener('keydown', stopKeyEventPropagation);
// let typingTimer;
// let doneTypingInterval = 3000;
// //on keyup, start the countdown
// nameInput.addEventListener('keyup', () => {
// clearTimeout(typingTimer);
// if (nameInput.value) {
// console.log("typing started!");
// typingTimer = setTimeout(this.userSearch, doneTypingInterval);
// }
// });
const getDataFromURL = () => {
let tempUrl = document.location.href
let splitedUrl = decodeURI(tempUrl).split('?')
let urlData = splitedUrl[1]
if (urlData !== undefined) {
this.chatId = urlData
}
}
const runFunctionsAfterPageLoad = () => { const runFunctionsAfterPageLoad = () => {
// Functions to exec after render while waiting for page info... // Functions to exec after render while waiting for page info...
@ -497,8 +508,11 @@ class Chat extends LitElement {
}) })
}) })
parentEpml.imReady() parentEpml.imReady()
} }
setOpenPrivateMessage(props) { setOpenPrivateMessage(props) {
this.openPrivateMessage = props.open; this.openPrivateMessage = props.open;
this.shadowRoot.getElementById("sendTo").value = props.name this.shadowRoot.getElementById("sendTo").value = props.name
@ -526,7 +540,6 @@ class Chat extends LitElement {
} }
this.userFoundModalOpen = true; this.userFoundModalOpen = true;
} catch (error) { } catch (error) {
console.error(error);
let err4string = get("chatpage.cchange35"); let err4string = get("chatpage.cchange35");
parentEpml.request('showSnackBar', `${err4string}`) parentEpml.request('showSnackBar', `${err4string}`)
} }
@ -767,7 +780,6 @@ class Chat extends LitElement {
}) })
this.groupInvites = pendingGroupInvites; this.groupInvites = pendingGroupInvites;
} catch (error) { } catch (error) {
console.error(error);
let err4string = get("chatpage.cchange61"); let err4string = get("chatpage.cchange61");
parentEpml.request('showSnackBar', `${err4string}`) parentEpml.request('showSnackBar', `${err4string}`)
} }
@ -847,16 +859,13 @@ class Chat extends LitElement {
renderChatHead(chatHeadArr) { renderChatHead(chatHeadArr) {
let tempUrl = document.location.href
let splitedUrl = decodeURI(tempUrl).split('?')
// let activeChatHeadUrl = splitedUrl[1] === undefined ? '' : splitedUrl[1]
return chatHeadArr.map(eachChatHead => { return chatHeadArr.map(eachChatHead => {
return html`<chat-head activeChatHeadUrl=${this.activeChatHeadUrl} .setActiveChatHeadUrl=${(val)=> this.setActiveChatHeadUrl(val)} chatInfo=${JSON.stringify(eachChatHead)}></chat-head>` 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 // Check for the chat ID from and render chat messages
// Else render Welcome to Q-CHat // Else render Welcome to Q-CHat
@ -876,9 +885,10 @@ class Chat extends LitElement {
} }
setChatHeads(chatObj) { setChatHeads(chatObj) {
const chatObjGroups = Array.isArray(chatObj.groups) ? chatObj.groups : [];
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}` }) const chatObjDirect = Array.isArray(chatObj.direct) ? chatObj.direct : [];
let directList = chatObj.direct.map(dc => { 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}` } return { ...dc, url: `direct/${dc.address}` }
}) })
const compareNames = (a, b) => { const compareNames = (a, b) => {

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.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 label="${translate("websitespage.schange3")}" icon="block" @click="${(e) => this.displayTabContent('blocked')}"></mwc-tab>
</mwc-tab-bar> </mwc-tab-bar>
<z id="tabs-1-content"> <div id="tabs-1-content">
<div id="tab-browse-content"> <div id="tab-browse-content">
<div style="min-height:48px; display: flex; padding-bottom: 6px; margin: 2px;"> <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> <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 myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]
const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port
const url = `${nodeUrl}/arbitrary/THUMBNAIL/${name}/qortal_avatar?async=true&apiKey=${this.getApiKey()}` 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() { renderRelayModeText() {

View File

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

View File

@ -12,11 +12,13 @@ import '@material/mwc-textfield'
import '@material/mwc-icon' import '@material/mwc-icon'
import '@material/mwc-icon-button' import '@material/mwc-icon-button'
import '@material/mwc-dialog' import '@material/mwc-dialog'
import '@material/mwc-fab'
import '@material/mwc-tab-bar' import '@material/mwc-tab-bar'
import '@material/mwc-tab' import '@material/mwc-tab'
import '@material/mwc-list/mwc-list-item' import '@material/mwc-list/mwc-list-item'
import '@material/mwc-select' import '@material/mwc-select'
import '@polymer/iron-icons/iron-icons.js' 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-icon-button/paper-icon-button.js'
import '@polymer/paper-spinner/paper-spinner-lite.js' import '@polymer/paper-spinner/paper-spinner-lite.js'
import '@vaadin/grid' import '@vaadin/grid'
@ -542,22 +544,47 @@ class TradeBotPortal extends LitElement {
opacity: 0; 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) { @media (min-width: 701px) {
* { * {
} }
#trade-bot-portal { #trade-bot-portal {
display: grid; display: grid;
grid-template-columns:3fr 4fr 3fr; grid-template-columns: 2fr 4fr 2fr;
grid-auto-rows: max(80px); grid-auto-rows: max(80px);
column-gap: 0.5em; column-gap: 0.5em;
row-gap: 0.4em; row-gap: 0.4em;
justify-items: stretch; justify-items: stretch;
align-items: stretch; align-items: stretch;
margin-bottom: 10px; margin-bottom: 20px;
} }
#first-trade-section { #first-trade-section {
display: grid; display: grid;
grid-template-columns:1fr 4fr 1fr; grid-template-columns: 1fr 4fr 1fr;
grid-auto-rows: max(250px); grid-auto-rows: max(250px);
column-gap: 0.5em; column-gap: 0.5em;
row-gap: 0.4em; row-gap: 0.4em;
@ -567,7 +594,7 @@ class TradeBotPortal extends LitElement {
} }
#second-trade-section { #second-trade-section {
display: grid; display: grid;
grid-template-columns:1fr 4fr 1fr; grid-template-columns: 1fr 4fr 1fr;
grid-auto-rows: max(250px); grid-auto-rows: max(250px);
column-gap: 0.5em; column-gap: 0.5em;
row-gap: 0.4em; row-gap: 0.4em;
@ -860,7 +887,7 @@ class TradeBotPortal extends LitElement {
return html` return html`
<div class="my-open-orders"> <div class="my-open-orders">
<div class="box"> <div class="box">
<header><span>${translate("tradepage.tchange36")}</span></header> <header><span>${translate("info.inf15")}</span></header>
<div class="border-wrapper"> <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> <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}"> <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` return html`
<div class="trade-bot-container"> <div class="trade-bot-container">
<div class="box-bot"> <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"> <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 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 <vaadin-grid-column
@ -1103,7 +1130,7 @@ class TradeBotPortal extends LitElement {
return html` return html`
<div class="trade-bot-container"> <div class="trade-bot-container">
<div class="box-bot"> <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"> <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 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 <vaadin-grid-column
@ -1169,7 +1196,7 @@ class TradeBotPortal extends LitElement {
return html` return html`
<div class="trade-bot-container"> <div class="trade-bot-container">
<div class="box-bot"> <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"> <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 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 <vaadin-grid-column
@ -1235,7 +1262,7 @@ class TradeBotPortal extends LitElement {
return html` return html`
<div class="trade-bot-container"> <div class="trade-bot-container">
<div class="box-bot"> <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"> <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 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 <vaadin-grid-column
@ -1301,7 +1328,7 @@ class TradeBotPortal extends LitElement {
return html` return html`
<div class="trade-bot-container"> <div class="trade-bot-container">
<div class="box-bot"> <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"> <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 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 <vaadin-grid-column
@ -1367,7 +1394,7 @@ class TradeBotPortal extends LitElement {
return html` return html`
<div class="trade-bot-container"> <div class="trade-bot-container">
<div class="box-bot"> <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"> <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 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 <vaadin-grid-column
@ -1435,7 +1462,7 @@ class TradeBotPortal extends LitElement {
<div id="trade-bot-portal"> <div id="trade-bot-portal">
<div></div> <div></div>
<div style="min-height: 50px; display: flex; padding-bottom: 10px; padding-top: 10px; margin: auto;"> <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-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="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> <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="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-list-item value="PIRATECHAIN"><span class="coinName arrr" style="color: var(--black);">ARRR / QORT</span></mwc-list-item>
</mwc-select> </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> <div></div>
</div> </div>
@ -1924,6 +1954,20 @@ class TradeBotPortal extends LitElement {
</div> </div>
<mwc-button slot="primaryAction" dialogAction="cancel" class="cancel">${translate("general.close")}</mwc-button> <mwc-button slot="primaryAction" dialogAction="cancel" class="cancel">${translate("general.close")}</mwc-button>
</mwc-dialog> </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'
import '@material/mwc-icon-button' import '@material/mwc-icon-button'
import '@material/mwc-dialog' import '@material/mwc-dialog'
import '@material/mwc-fab'
import '@material/mwc-tab-bar' import '@material/mwc-tab-bar'
import '@material/mwc-tab' import '@material/mwc-tab'
import '@material/mwc-list/mwc-list-item' import '@material/mwc-list/mwc-list-item'
import '@material/mwc-select' import '@material/mwc-select'
import '@polymer/iron-icons/iron-icons.js' 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-icon-button/paper-icon-button.js'
import '@polymer/paper-spinner/paper-spinner-lite.js' import '@polymer/paper-spinner/paper-spinner-lite.js'
import '@vaadin/grid' import '@vaadin/grid'
@ -549,6 +551,31 @@ class TradePortal extends LitElement {
opacity: 0; 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) { @media (min-width: 701px) {
* { * {
} }
@ -887,8 +914,8 @@ class TradePortal extends LitElement {
<div class="open-market-container"> <div class="open-market-container">
<div class="box"> <div class="box">
<mwc-tab-bar id="tabs-1" activeIndex="0"> <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-buy" label="${translate("tradepage.tchange18")} QORT" @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-sell" label="${translate("tradepage.tchange19")} QORT" @click="${(e) => this.displayTabContent('sell')}"></mwc-tab>
</mwc-tab-bar> </mwc-tab-bar>
<z id="tabs-1-content"> <z id="tabs-1-content">
<div id="tab-buy-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="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-list-item value="PIRATECHAIN"><span class="coinName arrr" style="color: var(--black);">QORT / ARRR</span></mwc-list-item>
</mwc-select> </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()} ${this.chartShowCoin()}
</div> </div>
</div> </div>
<div id="trade-portal"> <div id="trade-portal">
<div id="first-trade-section"> <div id="first-trade-section">
@ -1221,6 +1252,19 @@ class TradePortal extends LitElement {
</div> </div>
<mwc-button slot="primaryAction" dialogAction="cancel" class="cancel">${translate("general.close")}</mwc-button> <mwc-button slot="primaryAction" dialogAction="cancel" class="cancel">${translate("general.close")}</mwc-button>
</mwc-dialog> </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 }, arrrMemo: { type: String },
errorMessage: { type: String }, errorMessage: { type: String },
arrrWalletAddress: { type: String }, arrrWalletAddress: { type: String },
unusedWalletAddress: { type: String },
successMessage: { type: String }, successMessage: { type: String },
sendMoneyLoading: { type: Boolean }, sendMoneyLoading: { type: Boolean },
btnDisable: { type: Boolean }, btnDisable: { type: Boolean },
@ -505,6 +506,13 @@ class MultiWallet extends LitElement {
position: relative; position: relative;
} }
.unused-address-dialog {
min-height: 150px;
min-width: 550px;
box-sizing: border-box;
position: relative;
}
.btn-clear-success { .btn-clear-success {
--mdc-icon-button-size: 32px; --mdc-icon-button-size: 32px;
color: red; color: red;
@ -561,6 +569,12 @@ class MultiWallet extends LitElement {
width: 185px; width: 185px;
} }
.unused-pos {
margin-top: -44px;
margin-left: 410px;
width: 185px;
}
@media (max-width: 863px) { @media (max-width: 863px) {
.wallet { .wallet {
width: 100%; width: 100%;
@ -673,6 +687,7 @@ class MultiWallet extends LitElement {
this.arrrRecipient = '' this.arrrRecipient = ''
this.arrrMemo = '' this.arrrMemo = ''
this.arrrWalletAddress = '' this.arrrWalletAddress = ''
this.unusedWalletAddress = ''
this.errorMessage = '' this.errorMessage = ''
this.successMessage = '' this.successMessage = ''
this.myElementId = '' this.myElementId = ''
@ -822,6 +837,9 @@ class MultiWallet extends LitElement {
<div class="book-pos" ?hidden="${this.getSelectedWalletAddress().length < 1}"> <div class="book-pos" ?hidden="${this.getSelectedWalletAddress().length < 1}">
${this.renderAddressbookButton()} ${this.renderAddressbookButton()}
</div> </div>
<div class="unused-pos" ?hidden="${this.getSelectedWalletAddress().length < 1}">
${this.renderUnusedAddressButton()}
</div>
<div class="qrcode-pos" ?hidden="${this.getSelectedWalletAddress().length < 1}"> <div class="qrcode-pos" ?hidden="${this.getSelectedWalletAddress().length < 1}">
<qortal-qrcode-generator data="${this.getSelectedWalletAddress()}" mode="octet" format="html" auto></qortal-qrcode-generator> <qortal-qrcode-generator data="${this.getSelectedWalletAddress()}" mode="octet" format="html" auto></qortal-qrcode-generator>
</div> </div>
@ -849,7 +867,7 @@ class MultiWallet extends LitElement {
<span class="title"> ${translate("walletpage.wchange10")} </span> <span class="title"> ${translate("walletpage.wchange10")} </span>
<br /> <br />
<div style="display: inline;"> <div style="display: inline;">
${this.renderSQB()} ${this.selectedTransaction.type === 'DEPLOY_AT' ? html`${this.renderCAB()}` : html`${this.renderSQB()}`}
</div> </div>
<br /> <br />
${!this.selectedTransaction.amount ? '' : html` ${!this.selectedTransaction.amount ? '' : html`
@ -1968,6 +1986,171 @@ class MultiWallet extends LitElement {
</mwc-button> </mwc-button>
</mwc-dialog> </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=""> <mwc-dialog id="addQortAddressDialog" scrimClickAction="" escapeKeyAction="">
<div style="text-align:center"> <div style="text-align:center">
<img src="/img/qort.png" width="32" height="32"> <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() { renderFetchText() {
return html`${translate("walletpage.wchange1")}` return html`${translate("walletpage.wchange1")}`
} }
@ -4486,10 +4686,10 @@ class MultiWallet extends LitElement {
} }
}) })
const txsQort = await parentEpml.request('apiCall', { 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', { 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) { if (this._selectedWallet == coin) {
this.wallets.get(coin).transactions = pendingTxsQort.concat(txsQort) 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() { renderSendButton() {
if ( this._selectedWallet === "qort" ) { 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>` 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() { renderExportAddressbookButton() {
if ( this._selectedWallet === "qort" ) { 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>` 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" path="creatorAddress"
> >
</vaadin-grid-column> </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.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 header="${translate("walletpage.wchange11")}" path="amount"></vaadin-grid-column>
<vaadin-grid-column auto-width <vaadin-grid-column auto-width

View File

@ -21,14 +21,16 @@ export const replaceMessagesEdited = async ({
let responseItem = { ...response[0] } let responseItem = { ...response[0] }
const decodeResponseItem = decodeMessageFunc(responseItem, isReceipient, _publicKey) const decodeResponseItem = decodeMessageFunc(responseItem, isReceipient, _publicKey)
delete decodeResponseItem.timestamp delete decodeResponseItem.timestamp
msgItem = { msgItem = {
...msg, ...msg,
...decodeResponseItem, ...decodeResponseItem,
senderName: msg.senderName,
sender: msg.sender,
editedTimestamp: response[0].timestamp, editedTimestamp: response[0].timestamp,
} }
} }
} catch (error) { } catch (error) {
console.log(error)
} }
return msgItem return msgItem
@ -39,7 +41,6 @@ export const replaceMessagesEdited = async ({
try { try {
parsedMessageObj = JSON.parse(msg.decodedMessage) parsedMessageObj = JSON.parse(msg.decodedMessage)
} catch (error) { } catch (error) {
console.log('error')
return msg return msg
} }
let msgItem = msg let msgItem = msg
@ -49,39 +50,52 @@ export const replaceMessagesEdited = async ({
msgQuery = `&txGroupId=${msg.txGroupId}` msgQuery = `&txGroupId=${msg.txGroupId}`
} }
if (parsedMessageObj.repliedTo) { 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", { const response = await parentEpml.request("apiCall", {
type: "api", type: "api",
url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}`, url: `/chat/messages?chatreference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}`,
}) })
if ( if (
originalReply &&
Array.isArray(originalReply) &&
originalReply.length !== 0 &&
response && response &&
Array.isArray(response) && Array.isArray(response) &&
response.length !== 0 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 = { msgItem = {
...msg, ...msg,
repliedToData: decodeMessageFunc(response[0], isReceipient, _publicKey), repliedToData: formattedRepliedToData,
} }
} else { } else {
const response2 = await parentEpml.request("apiCall", {
type: "api",
url: `/chat/messages?reference=${parsedMessageObj.repliedTo}&reverse=true${msgQuery}`,
})
if ( if (
response2 && originalReply &&
Array.isArray(response2) && Array.isArray(originalReply) &&
response2.length !== 0 originalReply.length !== 0
) { ) {
msgItem = { msgItem = {
...msg, ...msg,
repliedToData: decodeMessageFunc(response2[0], isReceipient, _publicKey), repliedToData: decodeMessageFunc(originalReply[0], isReceipient, _publicKey),
} }
} }
} }
} }
} catch (error) { } catch (error) {
console.log(error)
} }
return msgItem 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 # 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 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 curl -sS https://update.qortal.online/repo/qortal.gpg | sudo apt-key add -
echo 'deb [arch=amd64,arm64 signed-by=/usr/share/keyrings/qortal.gpg] https://update.qortal.online/repo/ ./ ' > /etc/apt/sources.list.d/qortal.list 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 fi