From 0dfe8a0bdcb06533a95de2071dcc0e853f14f1a5 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Wed, 4 Jan 2023 12:46:38 +0100 Subject: [PATCH 01/14] Fix upload avatar --- .../plugins/core/name-registration/name-registration.src.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js b/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js index 944d670b..037d9d85 100644 --- a/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js +++ b/qortal-ui-plugins/plugins/core/name-registration/name-registration.src.js @@ -280,7 +280,7 @@ class NameRegistration extends LitElement { } async uploadAvatar(nameObj) { - let name = nameObj.name + let name = encodeURIComponent(nameObj.name) window.location.href = `../qdn/publish/index.html?service=THUMBNAIL&identifier=qortal_avatar&name=${name}&uploadType=file&category=Avatar&showName=false&showService=false&showIdentifier=false` } From fba20229be4dc1fe4a25bdde136828d250545381 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Wed, 4 Jan 2023 13:52:47 +0100 Subject: [PATCH 02/14] Add search private group --- qortal-ui-core/language/de.json | 6 +- qortal-ui-core/language/es.json | 6 +- qortal-ui-core/language/fr.json | 6 +- qortal-ui-core/language/hindi.json | 6 +- qortal-ui-core/language/hr.json | 6 +- qortal-ui-core/language/hu.json | 6 +- qortal-ui-core/language/it.json | 6 +- qortal-ui-core/language/ko.json | 6 +- qortal-ui-core/language/no.json | 6 +- qortal-ui-core/language/pl.json | 6 +- qortal-ui-core/language/pt.json | 6 +- qortal-ui-core/language/ro.json | 6 +- qortal-ui-core/language/rs.json | 6 +- qortal-ui-core/language/ru.json | 6 +- qortal-ui-core/language/us.json | 6 +- qortal-ui-core/language/zhc.json | 6 +- qortal-ui-core/language/zht.json | 6 +- .../group-management/group-management.src.js | 121 +++++++++++++++++- 18 files changed, 202 insertions(+), 21 deletions(-) diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index 449987b0..25f0843f 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -606,7 +606,11 @@ "gchange51":"Beitreten", "gchange52":"Administrator", "gchange53":"Mitglied", - "gchange54":"Mitglieder" + "gchange54":"Mitglieder", + "gchange55":"Private Gruppe suchen", + "gchange56":"Zu suchender Gruppenname", + "gchange57":"Privater Gruppenname nicht gefunden", + "gchange58":"Beachten Sie, dass der Gruppenname genau übereinstimmen muss." }, "puzzlepage":{ "pchange1":"Rätsel", diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index 19e7c00f..a36e4009 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -606,7 +606,11 @@ "gchange51":"Unirse", "gchange52":"Aministrador", "gchange53":"Miembro", - "gchange54":"Miembros" + "gchange54":"Miembros", + "gchange55":"Buscar grupo privado", + "gchange56":"Nombre del grupo a buscar", + "gchange57":"Nombre de grupo privado no encontrado", + "gchange58":"Tenga en cuenta que el nombre del grupo debe coincidir exactamente." }, "puzzlepage":{ "pchange1":"Rompecabezas", diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index 9cc2d17c..80239121 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -606,7 +606,11 @@ "gchange51":"Rejoindre", "gchange52":"Admin", "gchange53":"Membre", - "gchange54":"Membres" + "gchange54":"Membres", + "gchange55":"Rechercher un groupe privé", + "gchange56":"Nom du groupe à rechercher", + "gchange57":"Nom de groupe privé introuvable", + "gchange58":"Notez que le nom du groupe doit correspondre exactement." }, "puzzlepage":{ "pchange1":"Puzzles", diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index 77e0de5a..8ffa0cbf 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -607,7 +607,11 @@ "gchange51":"शामिल हों", "gchange52":"व्यवस्थापक", "gchange53":"सदस्य", - "gchange54":"सदस्यों" + "gchange54":"सदस्यों", + "gchange55":"निजी समूह खोजें", + "gchange56":"खोजने के लिए समूह का नाम", + "gchange57":"निजी समूह का नाम नहीं मिला", + "gchange58":"ध्यान दें कि समूह का नाम सटीक मेल खाना चाहिए।" }, "puzzlepage":{ "pchange1":"पहेलि", diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index 0c3013c1..3ab42894 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -606,7 +606,11 @@ "gchange51":"Pridruži", "gchange52":"Admin", "gchange53":"Član", - "gchange54":"Članovi" + "gchange54":"Članovi", + "gchange55":"Traži privatnu grupu", + "gchange56":"Naziv grupe za pretraživanje", + "gchange57":"Ime privatne grupe nije pronađeno", + "gchange58":"Imajte na umu da se naziv grupe mora točno podudarati." }, "puzzlepage":{ "pchange1":"Zagonetke", diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index 5bebf438..259da25b 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -606,7 +606,11 @@ "gchange51":"Csatlakoz", "gchange52":"Kormányozo", "gchange53":"Tag", - "gchange54":"Tagok" + "gchange54":"Tagok", + "gchange55":"Keresés privát csoportban", + "gchange56":"A keresendő csoport neve", + "gchange57":"A privát csoport neve nem található", + "gchange58":"Ne feledje, hogy a csoport nevének pontosan meg kell egyeznie." }, "puzzlepage":{ "pchange1":"Rejtvények", diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index 1592451a..b68f67ec 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -606,7 +606,11 @@ "gchange51":"Unisciti", "gchange52":"Amministratore", "gchange53":"Membro", - "gchange54":"Membri" + "gchange54":"Membri", + "gchange55":"Cerca gruppo privato", + "gchange56":"Nome gruppo da cercare", + "gchange57":"Nome gruppo privato non trovato", + "gchange58":"Nota che il nome del gruppo deve corrispondere esattamente." }, "puzzlepage":{ "pchange1":"Puzzle", diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index c36bd93b..687b8123 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -606,7 +606,11 @@ "gchange51":"가입", "gchange52":"관리자", "gchange53":"회원", - "gchange54":"회원들" + "gchange54":"회원들", + "gchange55":"비공개 그룹 검색", + "gchange56":"검색할 그룹 이름", + "gchange57":"비공개 그룹 이름을 찾을 수 없음", + "gchange58":"그룹 이름이 정확히 일치해야 합니다." }, "puzzlepage":{ "pchange1":"퍼즐", diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index 9a16ace5..f84ae9cf 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -606,7 +606,11 @@ "gchange51":"Bli med", "gchange52":"Admin", "gchange53":"Medlem", - "gchange54":"Medlemmer" + "gchange54":"Medlemmer", + "gchange55":"Søk i privat gruppe", + "gchange56":"Gruppenavn å søke", + "gchange57":"Privat gruppenavn ikke funnet", + "gchange58":"Merk at gruppenavnet må samsvare nøyaktig." }, "puzzlepage":{ "pchange1":"Puzzles", diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index e57f725a..9d807000 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -606,7 +606,11 @@ "gchange51":"Dołącz", "gchange52":"Administrator", "gchange53":"Członek", - "gchange54":"Członkowie" + "gchange54":"Członkowie", + "gchange55":"Wyszukaj grupę prywatną", + "gchange56":"Nazwa grupy do wyszukania", + "gchange57":"Nie znaleziono nazwy grupy prywatnej", + "gchange58":"Zauważ, że nazwa grupy musi dokładnie pasować." }, "puzzlepage":{ "pchange1":"Zagadki", diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index 5f56fe4c..0bd11bef 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -606,7 +606,11 @@ "gchange51":"Entrar", "gchange52":"Administrador", "gchange53":"Membro", - "gchange54":"Membros" + "gchange54":"Membros", + "gchange55":"Pesquisar Grupo Privado", + "gchange56":"Nome do grupo para pesquisar", + "gchange57":"Nome do grupo privado não encontrado", + "gchange58":"Observe que o nome do grupo deve corresponder exatamente." }, "puzzlepage":{ "pchange1":"Enigmas", diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index d65da8f2..17463ce7 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -606,7 +606,11 @@ "gchange51":"Inscriere", "gchange52":"Admin", "gchange53":"Membru", - "gchange54":"Membrii" + "gchange54":"Membrii", + "gchange55":"Căutați grup privat", + "gchange56":"Numele grupului de căutat", + "gchange57":"Numele grupului privat nu a fost găsit", + "gchange58":"Rețineți că numele grupului trebuie să se potrivească exact." }, "puzzlepage":{ "pchange1":"Puzzle-uri", diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index 1cad90c9..bd076946 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -606,7 +606,11 @@ "gchange51":"Uđite", "gchange52":"Administrator", "gchange53":"Član", - "gchange54":"Članovi" + "gchange54":"Članovi", + "gchange55":"Pretraži privatnu grupu", + "gchange56":"Ime grupe za pretragu", + "gchange57":"Ime privatne grupe nije pronađeno", + "gchange58":"Imajte na umu da ime grupe mora potpuno da se podudara." }, "puzzlepage":{ "pchange1":"Slagalice", diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index 1865f095..12623f6e 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -606,7 +606,11 @@ "gchange51":"Присоединиться", "gchange52":"Администратор", "gchange53":"Член", - "gchange54":"Члены" + "gchange54":"Члены", + "gchange55":"Поиск в закрытой группе", + "gchange56":"Имя группы для поиска", + "gchange57":"Имя частной группы не найдено", + "gchange58":"Обратите внимание, что название группы должно точно совпадать." }, "puzzlepage":{ "pchange1":"Головоломки", diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index a0bddb50..1a918ae4 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -606,7 +606,11 @@ "gchange51":"Join", "gchange52":"Admin", "gchange53":"Member", - "gchange54":"Members" + "gchange54":"Members", + "gchange55":"Search Private Group", + "gchange56":"Group Name To Search", + "gchange57":"Private Group Name Not Found", + "gchange58":"Note that group name must exact match." }, "puzzlepage":{ "pchange1":"Puzzles", diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index b8052dfd..8b474d34 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -606,7 +606,11 @@ "gchange51":"加入", "gchange52":"管理员", "gchange53":"成员", - "gchange54":"成员数量" + "gchange54":"成员数量", + "gchange55":"搜索私人群组", + "gchange56":"要搜索的组名", + "gchange57":"未找到私人组名", + "gchange58":"注意组名必须完全匹配。" }, "puzzlepage":{ "pchange1":"益智游戏", diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index 113b8b14..374ea797 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -606,7 +606,11 @@ "gchange51":"加入", "gchange52":"管理員", "gchange53":"成員", - "gchange54":"成員數量" + "gchange54":"成員數量", + "gchange55":"搜索私人群組", + "gchange56":"要搜索的組名", + "gchange57":"未找到私人組名", + "gchange58":"注意組名必須完全匹配。" }, "puzzlepage":{ "pchange1":"益智游戲", diff --git a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js index 0221abe2..db52b313 100644 --- a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js +++ b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js @@ -30,8 +30,10 @@ class GroupManagement extends LitElement { return { loading: { type: Boolean }, publicGroups: { type: Array }, + privateGroups: { type: Array }, joinedGroups: { type: Array }, groupInvites: { type: Array }, + privateGroupSearch: { type: Array }, newMembersList: { type: Array }, newAdminsList: { type: Array }, newBannedList: { type: Array }, @@ -77,6 +79,7 @@ class GroupManagement extends LitElement { toInviteMemberToGroup: { type: String }, toCancelInviteMemberName: { type: String }, toCancelInviteMemberAddress: { type: String }, + searchGroupName: { type: String }, errorMessage: { type: String }, successMessage: { type: String } } @@ -410,6 +413,13 @@ class GroupManagement extends LitElement { background: var(--white); color: var(--black); } + + #search { + width: 100%; + display: flex; + margin: auto; + align-items: center; + } ` } @@ -418,8 +428,10 @@ class GroupManagement extends LitElement { this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' this.selectedAddress = {} this.publicGroups = [] + this.privateGroups = [] this.joinedGroups = [] this.groupInvites = [] + this.privateGroupSearch = [] this.newMembersList = [] this.newAdminsList = [] this.newBannedList = [] @@ -458,6 +470,7 @@ class GroupManagement extends LitElement { this.toInviteMemberToGroup = '' this.toCancelInviteMemberName = '' this.toCancelInviteMemberAddress = '' + this.searchGroupName = '' this.errorMessage = '' this.successMessage = '' this.selectedView = { id: 'group-members', name: 'Group Members' } @@ -1136,6 +1149,45 @@ class GroupManagement extends LitElement { this.shadowRoot.querySelector('#createGroupDialog').show()}>add${translate("grouppage.gchange2")} +
+

${translate("grouppage.gchange55")}

+
+ + + + { + if (data.item.isOpen === true) { + render(html`${translate("managegroup.mg44")}`, root) + } else { + render(html`${translate("managegroup.mg45")}`, root) + } + }}> + + + { + render(html` this.joinGroup(data.item)}>queue ${translate("grouppage.gchange51")}`, root) + }}> + +
+

${translate("grouppage.gchange3")}

@@ -1150,7 +1202,9 @@ class GroupManagement extends LitElement { }}> ${this.isEmptyArray(this.joinedGroups) ? html` - ${translate("grouppage.gchange8")} +
+ ${translate("grouppage.gchange8")} +
`: ''}
@@ -1175,7 +1229,9 @@ class GroupManagement extends LitElement { }}> ${this.isEmptyArray(this.groupInvites) ? html` - ${translate("managegroup.mg35")} +
+ ${translate("managegroup.mg35")} +
`: ''} @@ -1477,6 +1533,22 @@ class GroupManagement extends LitElement { ${translate("general.close")} + + +
+ warning +

${translate("grouppage.gchange57")}

+

${translate("grouppage.gchange58")}

+
+ + this.closePrivateGroupErrorDialog()} + class="red" + > + ${translate("general.close")} + +
` } @@ -1504,6 +1576,14 @@ class GroupManagement extends LitElement { return myGs } + const getPrivateGroups = async () => { + let privateG = await parentEpml.request('apiCall', { + url: `/groups?limit=0&reverse=true` + }) + let myPgs = privateG.filter(myP => myP.isOpen === false) + return myPgs + } + const getJoinedGroups = async () => { let joinedG = await parentEpml.request('apiCall', { url: `/groups/member/${this.selectedAddress.address}` @@ -1563,13 +1643,15 @@ class GroupManagement extends LitElement { const getOpen_JoinedGroups = async () => { let _joinedGroups = await getJoinedGroups() let _publicGroups = await getOpenPublicGroups() + let _privateGroups = await getPrivateGroups() let results = _publicGroups.filter(myOpenGroup => { let value = _joinedGroups.some(myJoinedGroup => myOpenGroup.groupId === myJoinedGroup.groupId) return !value }); this.publicGroups = results + this.privateGroups = _privateGroups this.joinedGroups = _joinedGroups - setTimeout(getOpen_JoinedGroups, 30000) + setTimeout(getOpen_JoinedGroups, 60000) } window.addEventListener("contextmenu", (event) => { @@ -1722,6 +1804,36 @@ class GroupManagement extends LitElement { } } + searchGroupListener(e) { + if (e.key === 'Enter') { + this.doGroupSearch(e) + } + } + + doGroupSearch(e) { + this.renderSearchResult() + } + + renderSearchResult() { + this.privateGroupSearch = [] + let searchGroupName = this.shadowRoot.getElementById('searchGroupName').value + if (searchGroupName.length === 0) { + let err1string = get("websitespage.schange34") + parentEpml.request('showSnackBar', `${err1string}`) + } else { + this.privateGroupSearch = this.privateGroups.filter(myS => myS.groupName === searchGroupName) + if (this.privateGroupSearch.length === 0) { + this.shadowRoot.querySelector('#privateGroupErrorDialog').show() + } + } + } + + closePrivateGroupErrorDialog() { + this.shadowRoot.querySelector('#privateGroupErrorDialog').close() + this.shadowRoot.getElementById('searchGroupName').value = '' + this.privateGroupSearch = [] + } + renderBanButton(groupObj) { return html` this.openCreateBanMemberDialog(groupObj)}>create ${translate("managegroup.mg6")}` } @@ -1939,6 +2051,7 @@ class GroupManagement extends LitElement { closeFieldErrorDialog() { this.shadowRoot.querySelector('#fieldErrorDialog').close() } + async unitCreateFee() { const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port @@ -3178,4 +3291,4 @@ class GroupManagement extends LitElement { } } -window.customElements.define('group-management', GroupManagement) +window.customElements.define('group-management', GroupManagement) \ No newline at end of file From 5b8a9a4e09c09b48f51bfd0fb2cfed4252328310 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Wed, 4 Jan 2023 16:54:33 +0100 Subject: [PATCH 03/14] Update reload balances --- qortal-ui-core/language/de.json | 3 ++- qortal-ui-core/language/es.json | 3 ++- qortal-ui-core/language/fr.json | 3 ++- qortal-ui-core/language/hindi.json | 3 ++- qortal-ui-core/language/hr.json | 3 ++- qortal-ui-core/language/hu.json | 3 ++- qortal-ui-core/language/it.json | 3 ++- qortal-ui-core/language/ko.json | 3 ++- qortal-ui-core/language/no.json | 3 ++- qortal-ui-core/language/pl.json | 3 ++- qortal-ui-core/language/pt.json | 3 ++- qortal-ui-core/language/ro.json | 3 ++- qortal-ui-core/language/rs.json | 3 ++- qortal-ui-core/language/ru.json | 3 ++- qortal-ui-core/language/us.json | 3 ++- qortal-ui-core/language/zhc.json | 3 ++- qortal-ui-core/language/zht.json | 3 ++- qortal-ui-core/src/components/app-view.js | 30 +++++++++++++++++------ 18 files changed, 56 insertions(+), 25 deletions(-) diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index 25f0843f..e197a97e 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -155,7 +155,8 @@ "continue":"Fortsetzen", "save":"Speichern", "balance":"Guthaben", - "balances":"IHR WALLET-GUTHABEN" + "balances":"IHR WALLET-GUTHABEN", + "update":"AKTUALISIERE WALLET-GUTHABEN" }, "startminting":{ "smchange1":"Prägekonten können nicht abgerufen werden", diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index a36e4009..0312fa3f 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -155,7 +155,8 @@ "continue":"Continuar", "save":"Guardar", "balance":"Saldo", - "balances":"LOS SALDOS DE TU BILLETERA" + "balances":"LOS SALDOS DE TU BILLETERA", + "update":"ACTUALIZAR SALDOS DE CARTERA" }, "startminting":{ "smchange1":"No se pueden obtener cuentas de acuñación", diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index 80239121..d677edd0 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -155,7 +155,8 @@ "continue":"Continuer", "save":"Sauvegarder", "balance":"Solde", - "balances":"VOS SOLDES DE PORTEFEUILLE" + "balances":"VOS SOLDES DE PORTEFEUILLE", + "update":"METTRE À JOUR LES SOLDES DES PORTEFEUILLES" }, "startminting":{ "smchange1":"Impossible de récupérer les comptes de frappe", diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index 8ffa0cbf..a35856f4 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -156,7 +156,8 @@ "continue":"जारी रखें", "save":"सहेजें", "balance":"संतुलन", - "balances":"आपका वॉलेट बैलेंस" + "balances":"आपका वॉलेट बैलेंस", + "update":"अपडेट वॉलेट बैलेंस" }, "startminting":{ "smchange1":"खनन खाते नहीं लाए जा सकते", diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index 3ab42894..8478c342 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -155,7 +155,8 @@ "continue":"Nastavi", "save":"Spremi", "balance":"Kreditna", - "balances":"VAŠ NOVČANIK JE NA SALJU" + "balances":"VAŠ NOVČANIK JE NA SALJU", + "update":"AŽURIRAJTE STANJE NOVČANIKA" }, "startminting":{ "smchange1":"Nije moguće dohvatiti račune za kovanje", diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index 259da25b..6fdc4f54 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -155,7 +155,8 @@ "continue":"Folytatódik/folytatáshoz", "save":"Mentéshez", "balance":"Hitel", - "balances":"A PÉNZTÁRCSA EGYENLEGEK" + "balances":"A PÉNZTÁRCSA EGYENLEGEK", + "update":"FRISSÍTSE A PÉNZTÁRCSA-EGYENLEGEKET" }, "startminting":{ "smchange1":"Nem lehet lekérni a pénzverési számlákat", diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index b68f67ec..7cea8646 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -155,7 +155,8 @@ "continue":"Continua", "save":"Salva", "balance":"Saldo", - "balances":"IL TUO SALDO DEL PORTAFOGLIO" + "balances":"I SALDI DEL TUO PORTAFOGLIO", + "update":"AGGIORNA I SALDI DEL PORTAFOGLIO" }, "startminting":{ "smchange1":"Impossibile recuperare i conti di conio", diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index 687b8123..edd76c68 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -155,7 +155,8 @@ "continue":"계속하다", "save":"저장", "balance":"균형", - "balances":"지갑 잔액" + "balances":"지갑 잔액", + "update":"월렛 잔액 업데이트" }, "startminting":{ "smchange1":"발행 계정을 가져올 수 없습니다", diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index f84ae9cf..24e94b7d 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -155,7 +155,8 @@ "continue":"Fortsett", "save":"Lagre", "balance":"Saldo", - "balances":"DIN WALLET-SALDO" + "balances":"DIN WALLET-SALDO", + "update":"OPPDATERT WALLET-SALDOER" }, "startminting":{ "smchange1":"Kan ikke hente myntingkontoer", diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index 9d807000..cd4f76c3 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -155,7 +155,8 @@ "continue":"Kontynuuj", "save":"Zapisz", "balance":"Saldo", - "balances":"SALDO TWOJEGO PORTFELA" + "balances":"SALDO TWOJEGO PORTFELA", + "update":"AKTUALIZUJ SALDA W PORTFELU" }, "startminting":{ "smchange1":"Nie można pobrać kont menniczych", diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index 0bd11bef..bce9759d 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -155,7 +155,8 @@ "continue":"Continuar", "save":"Salvar", "balance":"Saldo", - "balances":"SEUS SALDOS DE CARTEIRA" + "balances":"SEUS SALDOS DE CARTEIRA", + "update":"ATUALIZAR SALDOS DA CARTEIRA" }, "startminting":{ "smchange1":"Não é possível buscar contas de cunhagem", diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index 17463ce7..97861bf6 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -155,7 +155,8 @@ "continue":"Continua", "save":"Salveza", "balance":"Credit", - "balances":"SOLDELE PORTOTELULUI DVS" + "balances":"SOLDELE PORTOTELULUI DVS", + "update":"ACTUALIZAȚI SOLDELE PORTOTELULUI" }, "startminting":{ "smchange1":"Nu se pot prelua conturile de batere", diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index bd076946..0670c642 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -155,7 +155,8 @@ "continue":"Nastavite", "save":"Sačuvajte", "balance":"Kredit", - "balances":"VAŠI STANJE U NOVČANIKU" + "balances":"VAŠI STANJE U NOVČANIKU", + "update":"AŽURIRAJTE STANJE NOVČANIKA" }, "startminting":{ "smchange1":"Nije moguće preuzeti naloge za kovanje", diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index 12623f6e..1828a4af 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -155,7 +155,8 @@ "continue":"Продолжить", "save":"Сохранить", "balance":"кредит", - "balances":"БАЛАНС ВАШЕГО КОШЕЛЬКА" + "balances":"БАЛАНС ВАШЕГО КОШЕЛЬКА", + "update":"ОБНОВИТЬ БАЛАНС КОШЕЛЬКА" }, "startminting":{ "smchange1":"Не удается получить учетные записи минтинга", diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 1a918ae4..c50aa9ec 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -155,7 +155,8 @@ "continue":"Continue", "save":"Save", "balance":"Balance", - "balances":"YOUR WALLET BALANCES" + "balances":"YOUR WALLET BALANCES", + "update":"UPDATE WALLET BALANCES" }, "startminting":{ "smchange1":"Cannot fetch minting accounts", diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index 8b474d34..c034caf0 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -155,7 +155,8 @@ "continue":"继续", "save":"保存", "balance":"信用", - "balances":"您的钱包余额" + "balances":"您的钱包余额", + "update":"更新钱包余额" }, "startminting":{ "smchange1":"无法获取铸币帐户", diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index 374ea797..94e0c663 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -155,7 +155,8 @@ "continue":"繼續", "save":"保存", "balance":"信用", - "balances":"您的錢包餘額" + "balances":"您的錢包餘額", + "update":"更新錢包餘額" }, "startminting":{ "smchange1":"無法獲取鑄幣帳戶", diff --git a/qortal-ui-core/src/components/app-view.js b/qortal-ui-core/src/components/app-view.js index dbc8c9e0..200c0c8f 100644 --- a/qortal-ui-core/src/components/app-view.js +++ b/qortal-ui-core/src/components/app-view.js @@ -6,6 +6,7 @@ import { addTradeBotRoutes } from '../tradebot/addTradeBotRoutes.js' import { get, translate, translateUnsafeHTML } from 'lit-translate' import '@polymer/paper-icon-button/paper-icon-button.js' +import '@polymer/paper-progress/paper-progress.js' import '@polymer/iron-icons/iron-icons.js' import '@polymer/app-layout/app-layout.js' import '@polymer/paper-ripple' @@ -38,6 +39,7 @@ class AppView extends connect(store)(LitElement) { theme: { type: String, reflect: true }, addressInfo: { type: Object }, searchContentString: { type: String }, + getAllBalancesLoading: { type: Boolean }, botQortWallet: { type: String }, botBtcWallet: { type: String }, botLtcWallet: { type: String }, @@ -154,6 +156,10 @@ class AppView extends connect(store)(LitElement) { border-top: var(--border); } + paper-progress { + --paper-progress-active-color: var(--mdc-theme-primary); + } + .s-menu { list-style: none; padding: 0px 0px; @@ -207,7 +213,7 @@ class AppView extends connect(store)(LitElement) { .balanceheadertext { position: absolute; margin: auto; - font-size: 16px; + font-size: 14px; font-weight: 400; width: 250px; display: inline; @@ -323,11 +329,12 @@ class AppView extends connect(store)(LitElement) { constructor() { super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' this.searchContentString = '' this.urls = []; this.nodeType = '' this.addressInfo = {} - this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.getAllBalancesLoading = false this.botQortWallet = '' this.botBtcWallet = '' this.botLtcWallet = '' @@ -414,13 +421,18 @@ class AppView extends connect(store)(LitElement) {
- ${translate("general.balances")} - - - - + ${this.getAllBalancesLoading ? html` + ${translate("general.update")} + ` : html` + ${translate("general.balances")} + + + + + `}
+ ${this.getAllBalancesLoading ? html`` : ''} ${this.balanceTicker} @@ -1505,6 +1517,7 @@ class AppView extends connect(store)(LitElement) { } async getAllBalances() { + this.getAllBalancesLoading = true await this.updateQortWalletBalance() await this.updateBtcWalletBalance() await this.updateLtcWalletBalance() @@ -1513,6 +1526,7 @@ class AppView extends connect(store)(LitElement) { await this.updateRvnWalletBalance() await this.fetchArrrWalletAddress() await this.updateArrrWalletBalance() + this.getAllBalancesLoading = false } async renderBalances() { @@ -2132,4 +2146,4 @@ class AppView extends connect(store)(LitElement) { } } -window.customElements.define('app-view', AppView) +window.customElements.define('app-view', AppView) \ No newline at end of file From a9e9c8b3d499e086d8710ba2a311da5f39bd6523 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Wed, 4 Jan 2023 21:07:45 +0100 Subject: [PATCH 04/14] Fix user info --- .../src/components/user-info-view/user-info-view.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/qortal-ui-core/src/components/user-info-view/user-info-view.js b/qortal-ui-core/src/components/user-info-view/user-info-view.js index ada00969..453b6ccf 100644 --- a/qortal-ui-core/src/components/user-info-view/user-info-view.js +++ b/qortal-ui-core/src/components/user-info-view/user-info-view.js @@ -6,8 +6,6 @@ import { doLogout } from '../../redux/app/app-actions.js' import { get, translate, translateUnsafeHTML } from 'lit-translate' import '@polymer/paper-dialog/paper-dialog.js' -import '@polymer/neon-animation/animations/scale-up-animation.js'; -import '@polymer/neon-animation/animations/fade-out-animation.js'; import '@material/mwc-button' import '@material/mwc-icon' import '@vaadin/grid' @@ -1173,7 +1171,7 @@ class UserInfoView extends connect(store)(LitElement) { - +
${this.boughtBTCTemplate()} @@ -1193,7 +1191,7 @@ class UserInfoView extends connect(store)(LitElement) {
- +
${this.soldBTCTemplate()} @@ -1213,7 +1211,7 @@ class UserInfoView extends connect(store)(LitElement) {
- +

${translate("walletpage.wchange5")}


@@ -1773,7 +1771,7 @@ class UserInfoView extends connect(store)(LitElement) { openTrades() { this.shadowRoot.getElementById('userTrades').open() - this.shadowRoot.getElementById('userInfoDialog').close() + this.shadowRoot.getElementById('userFullInfoDialog').close() } async openUserBoughtDialog() { From 811957d900684f13853c766b216a398745c47a28 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Thu, 5 Jan 2023 16:06:57 +0100 Subject: [PATCH 05/14] Add join requests actions --- qortal-ui-core/language/de.json | 11 +- qortal-ui-core/language/es.json | 11 +- qortal-ui-core/language/fr.json | 11 +- qortal-ui-core/language/hindi.json | 13 +- qortal-ui-core/language/hr.json | 13 +- qortal-ui-core/language/hu.json | 11 +- qortal-ui-core/language/it.json | 11 +- qortal-ui-core/language/ko.json | 11 +- qortal-ui-core/language/no.json | 11 +- qortal-ui-core/language/pl.json | 13 +- qortal-ui-core/language/pt.json | 11 +- qortal-ui-core/language/ro.json | 11 +- qortal-ui-core/language/rs.json | 11 +- qortal-ui-core/language/ru.json | 11 +- qortal-ui-core/language/us.json | 11 +- qortal-ui-core/language/zhc.json | 11 +- qortal-ui-core/language/zht.json | 11 +- .../group-management/group-management.src.js | 348 ++++++++++++++++++ 18 files changed, 521 insertions(+), 20 deletions(-) diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index e197a97e..f10ddfe7 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -799,6 +799,15 @@ "mg49":"Beim Drücken auf Bestätigen wird die Anfrage zum Abbrechen der Einladung gesendet!", "mg50":"Kommt bald...", "mg51":"Minimum 3 Zeichen / Maximum 32 Zeichen", - "mg52":"Maximal 128 Zeichen" + "mg52":"Maximal 128 Zeichen", + "mg53":"Ihre offenen Beitrittsanfragen", + "mg54":"Keine offenen Beitrittsanfragen", + "mg55":"Sind Sie sicher, dass Sie die Beitrittsanfrage von diesem Mitglied annehmen werden?", + "mg56":"Beim Drücken von Bestätigen wird die Beitrittsanfrage gesendet!", + "mg57":"Beitrittsanfrage erfolgreich angenommen", + "mg58":"ETWAS GING FALSCH", + "mg59":"Beitrittsanfrage abbrechen erfolgreich akzeptiert", + "mg60":"Sind Sie sicher, dass Sie die Beitrittsanfrage dieses Mitglieds abbrechen möchten?", + "mg61":"Beim Drücken auf Bestätigen wird die Anfrage zum Abbrechen des Beitritts gesendet!" } } diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index 0312fa3f..4ae55367 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -799,6 +799,15 @@ "mg49":"¡Al presionar confirmar, se enviará la solicitud de cancelación de invitación!", "mg50":"Próximamente...", "mg51":"Mínimo 3 Caracteres / Máximo 32 Caracteres", - "mg52":"Máximo de 128 caracteres" + "mg52":"Máximo de 128 caracteres", + "mg53":"Tus solicitudes abiertas de unión", + "mg54":"Sin solicitudes de unión abiertas", + "mg55":"¿Está seguro de aceptar la solicitud de ingreso de este miembro?", + "mg56":"¡Al presionar confirmar, se enviará la solicitud de aceptación de ingreso!", + "mg57":"Solicitud de ingreso aceptada con éxito", + "mg58":"ALGO SALIO MAL", + "mg59":"Solicitud de cancelación de unión aceptada con éxito", + "mg60":"¿Está seguro de cancelar la solicitud de ingreso de este miembro?", + "mg61":"¡Al presionar confirmar, se enviará la solicitud de cancelación de unión!" } } diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index d677edd0..03b21617 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -799,6 +799,15 @@ "mg49":"En appuyant sur confirmer, la demande d'annulation d'invitation sera envoyée !", "mg50":"Bientôt disponible...", "mg51":"Minimum 3 caractères / Maximum 32 caractères", - "mg52":"Maximum 128 caractères" + "mg52":"Maximum 128 caractères", + "mg53":"Vos demandes d'ouverture de jointure", + "mg54":"Aucune demande de jointure ouverte", + "mg55":"Êtes-vous sûr d'accepter la demande d'adhésion de ce membre ?", + "mg56":"En appuyant sur confirmer, la demande d'adhésion acceptée sera envoyée !", + "mg57":"Demande d'adhésion acceptée avec succès", + "mg58":"QUELQUE CHOSE S'EST TROMPÉ", + "mg59":"Annuler la demande d'adhésion acceptée avec succès", + "mg60":"Êtes-vous sûr d'annuler la demande d'adhésion de ce membre ?", + "mg61":"En appuyant sur confirmer, la demande d'annulation de l'adhésion sera envoyée !" } } diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index a35856f4..02479e0f 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -799,7 +799,16 @@ "mg48":"क्या आप वाकई इस सदस्य के लिए आमंत्रण रद्द करना चाहते हैं?", "mg49":"पुष्टि करें दबाने पर, रद्द आमंत्रण अनुरोध भेजा जाएगा!", "mg50":"जल्द ही आ रहा है ...", - "mg51": "न्यूनतम 3 वर्ण / अधिकतम 32 वर्ण", - "mg52": "अधिकतम 128 वर्ण" + "mg51":"न्यूनतम 3 वर्ण / अधिकतम 32 वर्ण", + "mg52":"अधिकतम 128 वर्ण", + "mg53":"आपका खुला शामिल होने का अनुरोध", + "mg54":"नो ओपन जॉइन रिक्वेस्ट", + "mg55":"क्या आप निश्चित रूप से इस सदस्य के शामिल होने के अनुरोध को स्वीकार करना चाहते हैं?", + "mg56":"पुष्टि करें दबाने पर, स्वीकार करने का अनुरोध भेजा जाएगा!", + "mg57":"जुड़ने का अनुरोध सफलतापूर्वक स्वीकार किया गया", + "mg58":"कुछ गलत हो गया", + "mg59":"रद्द करें शामिल होने का अनुरोध सफलतापूर्वक स्वीकार किया गया", + "mg60":"क्या आप निश्चित रूप से इस सदस्य के शामिल होने के अनुरोध को रद्द करना चाहते हैं?", + "mg61":"पुष्टि करें दबाने पर, रद्द करने का अनुरोध भेजा जाएगा!" } } diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index 8478c342..dc066af8 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -798,7 +798,16 @@ "mg48":"Jeste li sigurni da želite otkazati poziv za ovog člana?", "mg49":"Pritiskom na potvrdu, zahtjev za pozivnicu za otkazivanje bit će poslan!", "mg50":"Uskoro...", - "mg51": "Minimalno 3 znaka / Maksimalno 32 znaka", - "mg52": "Maksimalno 128 znakova" + "mg51":"Minimalno 3 znaka / Maksimalno 32 znaka", + "mg52":"Maksimalno 128 znakova", + "mg53":"Vaši otvoreni zahtjevi za pridruživanje", + "mg54":"Nema otvorenih zahtjeva za pridruživanje", + "mg55":"Jeste li sigurni da prihvaćate zahtjev za pridruživanje ovog člana?", + "mg56":"Pritiskom na potvrdu, zahtjev za prihvaćanje pridruživanja bit će poslan!", + "mg57":"Zahtjev za pridruživanje uspješno prihvaćen", + "mg58":"NEŠTO JE POŠLO PO ZLOU", + "mg59":"Poništi zahtjev za pridruživanje uspješno prihvaćen", + "mg60":"Jeste li sigurni da želite otkazati zahtjev za pridruživanje ovog člana?", + "mg61":"Pritiskom na potvrdu, bit će poslan zahtjev za otkazivanje pridruživanja!" } } diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index 6fdc4f54..76f9fed4 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -799,6 +799,15 @@ "mg49":"A megerősítés megnyomására a rendszer elküldi a meghívó visszavonási kérelmét!", "mg50":"Hamarosan...", "mg51":"Minimum 3 karakter / legfeljebb 32 karakter", - "mg52":"Legfeljebb 128 karakter" + "mg52":"Legfeljebb 128 karakter", + "mg53":"Az Ön nyitott csatlakozási kérelmei", + "mg54":"Nincsenek nyitott csatlakozási kérelmek", + "mg55":"Biztosan elfogadja ennek a tagnak a csatlakozási kérelmét?", + "mg56":"A megerősítés megnyomására a rendszer elküldi az elfogadási csatlakozási kérelmet!", + "mg57":"Csatlakozási kérelem sikeresen elfogadva", + "mg58":"VALAMI RÁMADT", + "mg59":"Csatlakozási kérelem visszavonása sikeresen elfogadva", + "mg60":"Biztosan visszavonja ennek a tagnak a csatlakozási kérelmét?", + "mg61":"A megerősítés megnyomására a csatlakozás megszakítási kérelme elküldésre kerül!" } } diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index 7cea8646..42e55e2e 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -799,6 +799,15 @@ "mg49":"Premendo conferma, verrà inviata la richiesta di annullamento dell'invito!", "mg50":"Prossimamente...", "mg51":"Minimo 3 caratteri / Massimo 32 caratteri", - "mg52":"Massimo 128 caratteri" + "mg52":"Massimo 128 caratteri", + "mg53":"Le tue richieste di partecipazione aperte", + "mg54":"Nessuna richiesta di partecipazione aperta", + "mg55":"Sei sicuro di accettare la richiesta di adesione di questo membro ?", + "mg56":"Premendo conferma, verrà inviata la richiesta di partecipazione accettata!", + "mg57":"Richiesta di partecipazione accettata con successo", + "mg58":"QUALCOSA È ANDATO storto", + "mg59":"Annulla richiesta di partecipazione accettata con successo", + "mg60":"Sei sicuro di voler annullare la richiesta di adesione di questo membro ?", + "mg61":"Premendo conferma, verrà inviata la richiesta di annullamento partecipazione!" } } diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index edd76c68..2124eec4 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -799,6 +799,15 @@ "mg49":"확인을 누르면 초대 취소 요청이 전송됩니다!", "mg50":"출시 예정...", "mg51":"최소 3자 / 최대 32자", - "mg52":"최대 128자" + "mg52":"최대 128자", + "mg53":"귀하의 오픈 조인 요청", + "mg54":"오픈 조인 요청 없음", + "mg55":"이 회원의 가입 요청을 수락하시겠습니까?", + "mg56":"확인을 누르면 가입 수락 요청이 전송됩니다!", + "mg57":"가입 요청이 성공적으로 수락됨", + "mg58":"뭔가 잘못되었습니다", + "mg59":"가입 요청 취소 성공", + "mg60":"이 회원의 가입 요청을 취소하시겠습니까?", + "mg61":"확인을 누르면 가입 취소 요청이 전송됩니다!" } } diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index 24e94b7d..a38abbe8 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -799,6 +799,15 @@ "mg49":"Når du trykker på bekreftelse, vil forespørselen om kansellering av invitasjon bli sendt!", "mg50":"Kommer snart...", "mg51":"Minimum 3 tegn / maksimum 32 tegn", - "mg52": "Maksimalt 128 tegn" + "mg52":"Maksimalt 128 tegn", + "mg53":"Dine åpne forespørsler om bli med", + "mg54":"Ingen åpne forespørsler om medlemskap", + "mg55":"Er du sikker på å godta forespørselen fra dette medlemmet?", + "mg56":"Når du trykker på bekreft, vil forespørselen om å godta bli sendt bli sendt!", + "mg57":"Bli med forespørsel ble godtatt", + "mg58":"NOE GIKK FEIL", + "mg59":"Avbryt deltakelsesforespørsel ble godtatt", + "mg60":"Er du sikker på å avbryte forespørselen om å bli medlem fra dette medlemmet?", + "mg61":"Når du trykker på bekreft, vil forespørselen om kansellering bli sendt!" } } diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index cd4f76c3..aff8fbd4 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -798,7 +798,16 @@ "mg48":"Czy na pewno chcesz anulować zaproszenie dla tego członka?", "mg49":"Po naciśnięciu potwierdzenia zostanie wysłana prośba o anulowanie zaproszenia!", "mg50":"Już wkrótce...", - "mg51": "Minimum 3 znaki / Maksymalnie 32 znaki", - "mg52": "Maksymalnie 128 znaków" + "mg51":"Minimum 3 znaki / Maksymalnie 32 znaki", + "mg52":"Maksymalnie 128 znaków", + "mg53":"Twoje otwarte prośby o dołączenie", + "mg54":"Brak otwartych próśb o dołączenie", + "mg55":"Czy na pewno akceptujesz prośbę o dołączenie od tego członka?", + "mg56":"Po naciśnięciu potwierdzenia zostanie wysłana prośba o zaakceptowanie dołączenia!", + "mg57":"Prośba o dołączenie pomyślnie zaakceptowana", + "mg58":"COŚ POszło nie tak", + "mg59":"Anuluj prośbę o dołączenie pomyślnie zaakceptowaną", + "mg60":"Czy na pewno chcesz anulować prośbę o dołączenie od tego członka?", + "mg61":"Po naciśnięciu potwierdzenia, zostanie wysłana prośba o anulowanie dołączenia!" } } diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index bce9759d..035986c0 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -799,6 +799,15 @@ "mg49":"Ao pressionar confirmar, a solicitação de cancelamento do convite será enviada!", "mg50":"Em Breve...", "mg51":"Mínimo de 3 caracteres / Máximo de 32 caracteres", - "mg52":"Máximo de 128 caracteres" + "mg52":"Máximo de 128 caracteres", + "mg53":"Suas solicitações de entrada abertas", + "mg54":"Nenhuma solicitação de entrada aberta", + "mg55":"Tem certeza que aceita a solicitação de entrada deste membro?", + "mg56":"Ao pressionar confirmar, o pedido de aceitação de entrada será enviado!", + "mg57":"Solicitação de entrada aceita com sucesso", + "mg58":"ALGO DEU ERRADO", + "mg59":"Cancelar pedido de entrada aceito com sucesso", + "mg60":"Tem certeza que deseja cancelar a solicitação de ingresso deste membro ?", + "mg61":"Ao pressionar confirmar, o pedido de cancelamento de adesão será enviado!" } } diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index 97861bf6..891a0be4 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -799,6 +799,15 @@ "mg49":"La apăsarea confirmării, cererea de anulare a invitației va fi trimisă!", "mg50":"În curând...", "mg51":"Minim 3 caractere / Maxim 32 de caractere", - "mg52":"Maximum 128 de caractere" + "mg52":"Maximum 128 de caractere", + "mg53":"Solicitările dvs. de înscriere deschise", + "mg54":"Fără solicitări de aderare deschise", + "mg55":"Sunteți sigur că acceptați solicitarea de alăturare de la acest membru?", + "mg56":"La apăsarea confirmării, va fi trimisă cererea de acceptare a înscrierii!", + "mg57":"Solicitarea de înscriere a fost acceptată cu succes", + "mg58":"CEVA A MERAT GREUT", + "mg59":"Anularea cererii de aderare a fost acceptată cu succes", + "mg60":"Sigur anulați solicitarea de alăturare de la acest membru?", + "mg61":"La apăsarea confirmării, cererea de anulare a aderării va fi trimisă!" } } diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index 0670c642..432280f7 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -799,6 +799,15 @@ "mg49":"Pritiskom na potvrdi, zahtev za otkazivanje pozivnice će biti poslat!", "mg50":"Uskoro...", "mg51":"Najmanje 3 znaka / maksimalno 32 znaka", - "mg52":"Maksimalno 128 znakova" + "mg52":"Maksimalno 128 znakova", + "mg53":"Vaši zahtevi za otvoreno pridruživanje", + "mg54":"Nema otvorenih zahteva za pridruživanje", + "mg55":"Da li ste sigurni da prihvatate zahtev za pridruživanje ovog člana?", + "mg56":"Pritiskom na potvrdi, biće poslat zahtev za prihvatanje pridruživanja!", + "mg57":"Zahtev za pridruživanje je uspešno prihvaćen", + "mg58":"NEŠTO JE POŠLO NA ZLO", + "mg59":"Zahtev za otkazivanje pridruživanja je uspešno prihvaćen", + "mg60":"Da li ste sigurni da otkažete zahtev za pridruživanje ovog člana?", + "mg61":"Pritiskom na potvrdu, zahtev za otkazivanje pridruživanja će biti poslat!" } } diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index 1828a4af..e40c5ae3 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -799,6 +799,15 @@ "mg49":"При нажатии подтверждения будет отправлен запрос на отмену приглашения!", "mg50":"Скоро...", "mg51":"Минимум 3 символа / максимум 32 символа", - "mg52":"Максимум 128 символов" + "mg52":"Максимум 128 символов", + "mg53":"Ваши открытые запросы на вступление", + "mg54":"Открытых запросов на присоединение нет", + "mg55":"Вы уверены, что принимаете запрос на вступление от этого участника?", + "mg56":"При нажатии подтверждения будет отправлен запрос на присоединение!", + "mg57":"Запрос на присоединение успешно принят", + "mg58":"ЧТО-ТО ПОШЛО НЕ ТАК", + "mg59":"Запрос на отмену присоединения успешно принят", + "mg60":"Вы уверены, что отмените запрос на вступление от этого участника?", + "mg61":"При нажатии кнопки подтверждения будет отправлен запрос на отмену присоединения!" } } diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index c50aa9ec..7da41ef0 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -799,6 +799,15 @@ "mg49":"On pressing confirm, the cancel invite request will be sent!", "mg50":"Coming Soon...", "mg51":"Minimum 3 Characters / Maximum 32 Characters", - "mg52":"Maximum 128 Characters" + "mg52":"Maximum 128 Characters", + "mg53":"Your Open Join Requests", + "mg54":"No Open Join Requests", + "mg55":"Are you sure to accept the join request from this member ?", + "mg56":"On pressing confirm, the accept join request will be sent!", + "mg57":"Join Request Successfully Accepted", + "mg58":"SOMETHING WENT WRONG", + "mg59":"Cancel Join Request Successfully Accepted", + "mg60":"Are you sure to cancel the join request from this member ?", + "mg61":"On pressing confirm, the cancel join request will be sent!" } } diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index c034caf0..dfd1d98d 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -799,6 +799,15 @@ "mg49":"按下确认后,将发送取消邀请请求!", "mg50":"即将推出……", "mg51":"最少 3 个字符 / 最多 32 个字符", - "mg52":"最多 128 个字符" + "mg52":"最多 128 个字符", + "mg53":"您的公开加入请求", + "mg54":"没有开放的加入请求", + "mg55":"您确定接受该会员的加入请求吗?", + "mg56":"按下确认后,将发送接受加入请求!", + "mg57":"成功接受加入请求", + "mg58":"出了点问题", + "mg59":"取消加入请求已成功接受", + "mg60":"您确定要取消该会员的加入请求吗?", + "mg61":"按下确认后,将发送取消加入请求!" } } diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index 94e0c663..7572a9df 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -799,6 +799,15 @@ "mg49":"按下確認後,將發送取消邀請請求!", "mg50":"即將推出……", "mg51":"最少 3 個字符 / 最多 32 個字符", - "mg52":"最多 128 個字符" + "mg52":"最多 128 個字符", + "mg53":"您的公開加入請求", + "mg54":"沒有開放的加入請求", + "mg55":"您確定接受該會員的加入請求嗎?", + "mg56":"按下確認後,將發送接受加入請求!", + "mg57":"成功接受加入請求", + "mg58":"出了點問題", + "mg59":"取消加入請求已成功接受", + "mg60":"您確定要取消該會員的加入請求嗎?", + "mg61":"按下確認後,將發送取消加入請求!" } } diff --git a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js index db52b313..14a0388b 100644 --- a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js +++ b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js @@ -38,6 +38,7 @@ class GroupManagement extends LitElement { newAdminsList: { type: Array }, newBannedList: { type: Array }, newGroupInvitesList: { type: Array }, + newGroupJoinsList: { type: Array }, recipientPublicKey: { type: String }, selectedAddress: { type: Object }, manageGroupObj: { type: Object }, @@ -155,6 +156,11 @@ class GroupManagement extends LitElement { color: red; } + .success-icon { + font-size: 48px; + color: #198754; + } + .close-icon { font-size: 36px; } @@ -436,6 +442,7 @@ class GroupManagement extends LitElement { this.newAdminsList = [] this.newBannedList = [] this.newGroupInvitesList = [] + this.newGroupJoinsList = [] this.manageGroupObj = {} this.joinGroupObj = {} this.leaveGroupObj = {} @@ -836,6 +843,7 @@ class GroupManagement extends LitElement { groupInviteTemplate() { return html` +

${translate("managegroup.mg36")}

${translate("managegroup.mg35")} ` : html``} +


+

${translate("managegroup.mg53")}

+ + { + render(html`${this.renderAvatar(data.item)}`, root) + }} + > + { + render(html`${data.item.name}`, root) + }} + > + { + render(html`${data.item.owner}`, root) + }} + > + { + render(html`${this.renderConfirmRequestButton(data.item)}`, root) + }} + > + { + render(html`${this.renderDeclineRequestButton(data.item)}`, root) + }} + > + + ${this.isEmptyArray(this.newGroupJoinsList) ? html` + ${translate("managegroup.mg54")} + ` : html``} +
+
this.openInviteMemberToGroupDialog()}> ${translate("managegroup.mg2")} @@ -1033,6 +1089,72 @@ class GroupManagement extends LitElement { ${translate("general.close")} + + +
+ group_add +

${translate("managegroup.mg57")}

+

${translate("walletpage.wchange43")}

+
+ + this.closeSuccessJoinDialog()} + class="red" + > + ${translate("general.close")} + +
+ + +
+ warning +

${translate("managegroup.mg58")}

+

${this.errorMessage}

+

${translate("walletpage.wchange44")}

+
+ + this.closeErrorJoinDialog()} + class="red" + > + ${translate("general.close")} + +
+ + +
+ person_remove +

${translate("managegroup.mg59")}

+

${translate("walletpage.wchange43")}

+
+ + this.closeCancelSuccessJoinDialog()} + class="red" + > + ${translate("general.close")} + +
+ + +
+ warning +

${translate("managegroup.mg58")}

+

${this.errorMessage}

+

${translate("walletpage.wchange44")}

+
+ + this.closeCancelErrorJoinDialog()} + class="red" + > + ${translate("general.close")} + +
` } @@ -1986,6 +2108,38 @@ class GroupManagement extends LitElement { this.errorMessage = '' } + renderConfirmRequestButton(joinObj) { + return html` this.createAcceptJoinGroupMember(joinObj)}>add_task ${translate("transpage.tchange3")}` + } + + renderDeclineRequestButton(joinObj) { + return html` this.kickJoinGroupMember(joinObj)}>cancel ${translate("transpage.tchange2")}` + } + + closeSuccessJoinDialog() { + this.shadowRoot.querySelector('#successJoinDialog').close() + this.successMessage = '' + this.errorMessage = '' + } + + closeErrorJoinDialog() { + this.shadowRoot.querySelector('#errorJoinDialog').close() + this.successMessage = '' + this.errorMessage = '' + } + + closeCancelSuccessJoinDialog() { + this.shadowRoot.querySelector('#cancelSuccessJoinDialog').close() + this.successMessage = '' + this.errorMessage = '' + } + + closeCancelErrorJoinDialog() { + this.shadowRoot.querySelector('#cancelErrorJoinDialog').close() + this.successMessage = '' + this.errorMessage = '' + } + openMemberInfo(inviteGroupId) { const _inviteMemberInfo = this.shadowRoot.getElementById('toInviteMemberToGroup').value const _nviteMemberTime = this.shadowRoot.getElementById("inviteMemberTime").value @@ -2361,6 +2515,57 @@ class GroupManagement extends LitElement { } } + async getNewGroupJoinList(theGroup) { + let callGroupID = theGroup + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + + let joinObj = [] + this.groupJoinMembers = [] + + await parentEpml.request('apiCall', { + url: `/groups/joinrequests/${callGroupID}` + }).then(res => { + this.groupJoinMembers = res + }) + + if (this.groupJoinMembers.length === 0) { + return + } else { + this.groupJoinMembers.map(a => { + let callTheJoinMember = a.joiner + let callSingleJoinMemberUrl = `${nodeUrl}/names/address/${callTheJoinMember}` + fetch(callSingleJoinMemberUrl).then(res => { + return res.json() + }).then(jsonRes => { + if (jsonRes.length) { + jsonRes.map(b => { + const joinObjToAdd = { + groupId: a.groupId, + name: b.name, + owner: b.owner, + time: '86400', + reason: 'NotAllowed' + } + joinObj.push(joinObjToAdd) + }) + } else { + const noName = 'No registered name' + const noNameObj = { + groupId: a.groupId, + name: noName, + owner: a.joiner, + time: '86400', + reason: 'NotAllowed' + } + joinObj.push(noNameObj) + } + this.newGroupJoinsList = joinObj + }) + }) + } + } + closeManageGroupOwnerDialog() { this.resetDefaultSettings() this.shadowRoot.getElementById('manageGroupOwnerDialog').close() @@ -2389,6 +2594,7 @@ class GroupManagement extends LitElement { await this.getNewMemberList(groupObj.groupId) await this.getNewBannedList(groupObj.groupId) await this.getNewGroupInvitesList(groupObj.groupId) + await this.getNewGroupJoinList(groupObj.groupId) await manageGroupDelay(1000) this.shadowRoot.getElementById('manageGroupOwnerDialog').open() } @@ -3057,6 +3263,148 @@ class GroupManagement extends LitElement { validateReceiver() } + async createAcceptJoinGroupMember(joinObj) { + const member = joinObj.owner + const inviteTime = joinObj.time + const inviteGroupMemberFeeInput = this.inviteGroupMemberFee + const theGroupId = joinObj.groupId + this.isLoading = true + this.btnDisable = true + + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + } + + const validateReceiver = async () => { + let lastRef = await getLastRef() + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + } + + const makeTransactionRequest = async (lastRef) => { + const myMember = member + const myLastRef = lastRef + const myGroupId = theGroupId + const myFee = inviteGroupMemberFeeInput + const myInviteTime = inviteTime + const myInviteMemberDialog1 = get("managegroup.mg55") + const myInviteMemberDialog2 = get("managegroup.mg56") + + let myTxnrequest = await parentEpml.request('transaction', { + type: 29, + nonce: this.selectedAddress.nonce, + params: { + fee: myFee, + recipient: myMember, + rGroupId: myGroupId, + rInviteTime: myInviteTime, + lastReference: myLastRef, + inviteMemberDialog1: myInviteMemberDialog1, + inviteMemberDialog2: myInviteMemberDialog2 + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + if (txnResponse.success === false && txnResponse.message) { + this.errorMessage = txnResponse.message + this.shadowRoot.querySelector('#errorJoinDialog').show() + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } else if (txnResponse.success === true && !txnResponse.data.error) { + this.shadowRoot.querySelector('#successJoinDialog').show() + this.errorMessage = '' + this.successMessage = this.renderSuccessText() + this.isLoading = false + this.btnDisable = false + } else { + this.errorMessage = txnResponse.data.message + this.shadowRoot.querySelector('#errorJoinDialog').show() + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } + } + validateReceiver() + } + + async kickJoinGroupMember(joinObj) { + const member = joinObj.owner + const reason = joinObj.reason + const kickGroupMemberFeeInput = this.kickGroupMemberFee + const theGroupId = joinObj.groupId + this.isLoading = true + this.btnDisable = true + + const getLastRef = async () => { + let myRef = await parentEpml.request('apiCall', { + type: 'api', + url: `/addresses/lastreference/${this.selectedAddress.address}` + }) + return myRef + } + + const validateReceiver = async () => { + let lastRef = await getLastRef() + let myTransaction = await makeTransactionRequest(lastRef) + getTxnRequestResponse(myTransaction) + } + + const makeTransactionRequest = async (lastRef) => { + const myMember = member + const myLastRef = lastRef + const myGroupId = theGroupId + const myFee = kickGroupMemberFeeInput + const myReason = reason + const myKickMemberDialog1 = get("managegroup.mg60") + const myKickMemberDialog2 = get("managegroup.mg61") + + let myTxnrequest = await parentEpml.request('transaction', { + type: 28, + nonce: this.selectedAddress.nonce, + params: { + fee: myFee, + recipient: myMember, + rGroupId: myGroupId, + rBanReason: myReason, + lastReference: myLastRef, + kickMemberDialog1: myKickMemberDialog1, + kickMemberDialog2: myKickMemberDialog2 + } + }) + return myTxnrequest + } + + const getTxnRequestResponse = (txnResponse) => { + if (txnResponse.success === false && txnResponse.message) { + this.errorMessage = txnResponse.message + this.shadowRoot.querySelector('#cancelErrorJoinDialog').show() + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } else if (txnResponse.success === true && !txnResponse.data.error) { + this.shadowRoot.querySelector('#cancelSuccessJoinDialog').show() + this.errorMessage = '' + this.successMessage = this.renderSuccessText() + this.isLoading = false + this.btnDisable = false + } else { + this.errorMessage = txnResponse.data.message + this.shadowRoot.querySelector('#cancelErrorJoinDialog').show() + this.isLoading = false + this.btnDisable = false + throw new Error(txnResponse) + } + } + validateReceiver() + } + async addGroupAdmin(groupId) { const member = this.shadowRoot.getElementById('memberToAdmin').value const addGroupAdminFeeInput = this.addGroupAdminFee From abf736c6400b1ed5dd7f989d54ce4cef4ab8c3c7 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Thu, 5 Jan 2023 16:55:34 +0100 Subject: [PATCH 06/14] Add time elements --- qortal-ui-plugins/plugins/core/components/TimeAgo.js | 2 +- .../plugins/core/group-management/group-management.src.js | 2 +- qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/components/TimeAgo.js b/qortal-ui-plugins/plugins/core/components/TimeAgo.js index c212a736..ca3633b7 100644 --- a/qortal-ui-plugins/plugins/core/components/TimeAgo.js +++ b/qortal-ui-plugins/plugins/core/components/TimeAgo.js @@ -1,6 +1,6 @@ import { LitElement, html, css } from 'lit' -import '@github/time-elements' +import './time-elements/index.js' class TimeAgo extends LitElement { static get properties() { diff --git a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js index 14a0388b..edcfd254 100644 --- a/qortal-ui-plugins/plugins/core/group-management/group-management.src.js +++ b/qortal-ui-plugins/plugins/core/group-management/group-management.src.js @@ -7,6 +7,7 @@ registerTranslateConfig({ loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) }) +import '../components/time-elements/index.js' import '@material/mwc-button' import '@material/mwc-dialog' import '@material/mwc-formfield' @@ -21,7 +22,6 @@ import '@vaadin/icon' import '@vaadin/icons' import '@vaadin/grid' import '@vaadin/grid/vaadin-grid-filter-column.js' -import '@github/time-elements' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) diff --git a/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js b/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js index de4e77ed..197248f4 100644 --- a/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js +++ b/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js @@ -10,8 +10,8 @@ registerTranslateConfig({ import '../components/ButtonIconCopy.js' import '../components/QortalQrcodeGenerator.js' import '../components/frag-file-input.js' +import '../components/time-elements/index.js' import FileSaver from 'file-saver' -import '@github/time-elements' import '@material/mwc-button' import '@material/mwc-checkbox' import '@material/mwc-dialog' From 1a2a877a9dfc06ad1a194f0a6c06767667d36649 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Thu, 5 Jan 2023 16:57:26 +0100 Subject: [PATCH 07/14] JS files --- .../time-elements/extended-time-element.js | 87 +++ .../core/components/time-elements/index.js | 705 ++++++++++++++++++ .../time-elements/local-time-element.js | 81 ++ .../time-elements/relative-time-element.js | 44 ++ .../components/time-elements/relative-time.js | 290 +++++++ .../time-elements/time-ago-element.js | 21 + .../time-elements/time-until-element.js | 21 + .../core/components/time-elements/utils.js | 166 +++++ 8 files changed, 1415 insertions(+) create mode 100644 qortal-ui-plugins/plugins/core/components/time-elements/extended-time-element.js create mode 100644 qortal-ui-plugins/plugins/core/components/time-elements/index.js create mode 100644 qortal-ui-plugins/plugins/core/components/time-elements/local-time-element.js create mode 100644 qortal-ui-plugins/plugins/core/components/time-elements/relative-time-element.js create mode 100644 qortal-ui-plugins/plugins/core/components/time-elements/relative-time.js create mode 100644 qortal-ui-plugins/plugins/core/components/time-elements/time-ago-element.js create mode 100644 qortal-ui-plugins/plugins/core/components/time-elements/time-until-element.js create mode 100644 qortal-ui-plugins/plugins/core/components/time-elements/utils.js diff --git a/qortal-ui-plugins/plugins/core/components/time-elements/extended-time-element.js b/qortal-ui-plugins/plugins/core/components/time-elements/extended-time-element.js new file mode 100644 index 00000000..583dee79 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/time-elements/extended-time-element.js @@ -0,0 +1,87 @@ +import { makeFormatter } from './utils'; +const datetimes = new WeakMap(); +export default class ExtendedTimeElement extends HTMLElement { + static get observedAttributes() { + return [ + 'datetime', + 'day', + 'format', + 'lang', + 'hour', + 'minute', + 'month', + 'second', + 'title', + 'weekday', + 'year', + 'time-zone-name' + ]; + } + connectedCallback() { + const title = this.getFormattedTitle(); + if (title && !this.hasAttribute('title')) { + this.setAttribute('title', title); + } + const text = this.getFormattedDate(); + if (text) { + this.textContent = text; + } + } + attributeChangedCallback(attrName, oldValue, newValue) { + const oldTitle = this.getFormattedTitle(); + if (attrName === 'datetime') { + const millis = Date.parse(newValue); + if (isNaN(millis)) { + datetimes.delete(this); + } + else { + datetimes.set(this, new Date(millis)); + } + } + const title = this.getFormattedTitle(); + const currentTitle = this.getAttribute('title'); + if (attrName !== 'title' && title && (!currentTitle || currentTitle === oldTitle)) { + this.setAttribute('title', title); + } + const text = this.getFormattedDate(); + if (text) { + this.textContent = text; + } + } + get date() { + return datetimes.get(this); + } + getFormattedTitle() { + const date = this.date; + if (!date) + return; + const formatter = titleFormatter(); + if (formatter) { + return formatter.format(date); + } + else { + try { + return date.toLocaleString(); + } + catch (e) { + if (e instanceof RangeError) { + return date.toString(); + } + else { + throw e; + } + } + } + } + getFormattedDate() { + return; + } +} +const titleFormatter = makeFormatter({ + day: 'numeric', + month: 'short', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + timeZoneName: 'short' +}); diff --git a/qortal-ui-plugins/plugins/core/components/time-elements/index.js b/qortal-ui-plugins/plugins/core/components/time-elements/index.js new file mode 100644 index 00000000..8b3dbe4c --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/time-elements/index.js @@ -0,0 +1,705 @@ +const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; +const months = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' +]; +function pad(num) { + return `0${num}`.slice(-2); +} +function strftime(time, formatString) { + const day = time.getDay(); + const date = time.getDate(); + const month = time.getMonth(); + const year = time.getFullYear(); + const hour = time.getHours(); + const minute = time.getMinutes(); + const second = time.getSeconds(); + return formatString.replace(/%([%aAbBcdeHIlmMpPSwyYZz])/g, function (_arg) { + let match; + const modifier = _arg[1]; + switch (modifier) { + case '%': + return '%'; + case 'a': + return weekdays[day].slice(0, 3); + case 'A': + return weekdays[day]; + case 'b': + return months[month].slice(0, 3); + case 'B': + return months[month]; + case 'c': + return time.toString(); + case 'd': + return pad(date); + case 'e': + return String(date); + case 'H': + return pad(hour); + case 'I': + return pad(strftime(time, '%l')); + case 'l': + if (hour === 0 || hour === 12) { + return String(12); + } + else { + return String((hour + 12) % 12); + } + case 'm': + return pad(month + 1); + case 'M': + return pad(minute); + case 'p': + if (hour > 11) { + return 'PM'; + } + else { + return 'AM'; + } + case 'P': + if (hour > 11) { + return 'pm'; + } + else { + return 'am'; + } + case 'S': + return pad(second); + case 'w': + return String(day); + case 'y': + return pad(year % 100); + case 'Y': + return String(year); + case 'Z': + match = time.toString().match(/\((\w+)\)$/); + return match ? match[1] : ''; + case 'z': + match = time.toString().match(/\w([+-]\d\d\d\d) /); + return match ? match[1] : ''; + } + return ''; + }); +} +function makeFormatter(options) { + let format; + return function () { + if (format) + return format; + if ('Intl' in window) { + try { + format = new Intl.DateTimeFormat(undefined, options); + return format; + } + catch (e) { + if (!(e instanceof RangeError)) { + throw e; + } + } + } + }; +} +let dayFirst = null; +const dayFirstFormatter = makeFormatter({ day: 'numeric', month: 'short' }); +function isDayFirst() { + if (dayFirst !== null) { + return dayFirst; + } + const formatter = dayFirstFormatter(); + if (formatter) { + const output = formatter.format(new Date(0)); + dayFirst = !!output.match(/^\d/); + return dayFirst; + } + else { + return false; + } +} +let yearSeparator = null; +const yearFormatter = makeFormatter({ day: 'numeric', month: 'short', year: 'numeric' }); +function isYearSeparator() { + if (yearSeparator !== null) { + return yearSeparator; + } + const formatter = yearFormatter(); + if (formatter) { + const output = formatter.format(new Date(0)); + yearSeparator = !!output.match(/\d,/); + return yearSeparator; + } + else { + return true; + } +} +function isThisYear(date) { + const now = new Date(); + return now.getUTCFullYear() === date.getUTCFullYear(); +} +function makeRelativeFormat(locale, options) { + if ('Intl' in window && 'RelativeTimeFormat' in window.Intl) { + try { + return new Intl.RelativeTimeFormat(locale, options); + } + catch (e) { + if (!(e instanceof RangeError)) { + throw e; + } + } + } +} +function localeFromElement(el) { + const container = el.closest('[lang]'); + if (container instanceof HTMLElement && container.lang) { + return container.lang; + } + return 'default'; +} + +const datetimes = new WeakMap(); +class ExtendedTimeElement extends HTMLElement { + static get observedAttributes() { + return [ + 'datetime', + 'day', + 'format', + 'lang', + 'hour', + 'minute', + 'month', + 'second', + 'title', + 'weekday', + 'year', + 'time-zone-name' + ]; + } + connectedCallback() { + const title = this.getFormattedTitle(); + if (title && !this.hasAttribute('title')) { + this.setAttribute('title', title); + } + const text = this.getFormattedDate(); + if (text) { + this.textContent = text; + } + } + attributeChangedCallback(attrName, oldValue, newValue) { + const oldTitle = this.getFormattedTitle(); + if (attrName === 'datetime') { + const millis = Date.parse(newValue); + if (isNaN(millis)) { + datetimes.delete(this); + } + else { + datetimes.set(this, new Date(millis)); + } + } + const title = this.getFormattedTitle(); + const currentTitle = this.getAttribute('title'); + if (attrName !== 'title' && title && (!currentTitle || currentTitle === oldTitle)) { + this.setAttribute('title', title); + } + const text = this.getFormattedDate(); + if (text) { + this.textContent = text; + } + } + get date() { + return datetimes.get(this); + } + getFormattedTitle() { + const date = this.date; + if (!date) + return; + const formatter = titleFormatter(); + if (formatter) { + return formatter.format(date); + } + else { + try { + return date.toLocaleString(); + } + catch (e) { + if (e instanceof RangeError) { + return date.toString(); + } + else { + throw e; + } + } + } + } + getFormattedDate() { + return; + } +} +const titleFormatter = makeFormatter({ + day: 'numeric', + month: 'short', + year: 'numeric', + hour: 'numeric', + minute: '2-digit', + timeZoneName: 'short' +}); + +const formatters = new WeakMap(); +class LocalTimeElement extends ExtendedTimeElement { + attributeChangedCallback(attrName, oldValue, newValue) { + if (attrName === 'hour' || attrName === 'minute' || attrName === 'second' || attrName === 'time-zone-name') { + formatters.delete(this); + } + super.attributeChangedCallback(attrName, oldValue, newValue); + } + getFormattedDate() { + const d = this.date; + if (!d) + return; + const date = formatDate(this, d) || ''; + const time = formatTime(this, d) || ''; + return `${date} ${time}`.trim(); + } +} +function formatDate(el, date) { + const props = { + weekday: { + short: '%a', + long: '%A' + }, + day: { + numeric: '%e', + '2-digit': '%d' + }, + month: { + short: '%b', + long: '%B' + }, + year: { + numeric: '%Y', + '2-digit': '%y' + } + }; + let format = isDayFirst() ? 'weekday day month year' : 'weekday month day, year'; + for (const prop in props) { + const value = props[prop][el.getAttribute(prop) || '']; + format = format.replace(prop, value || ''); + } + format = format.replace(/(\s,)|(,\s$)/, ''); + return strftime(date, format).replace(/\s+/, ' ').trim(); +} +function formatTime(el, date) { + const options = {}; + const hour = el.getAttribute('hour'); + if (hour === 'numeric' || hour === '2-digit') + options.hour = hour; + const minute = el.getAttribute('minute'); + if (minute === 'numeric' || minute === '2-digit') + options.minute = minute; + const second = el.getAttribute('second'); + if (second === 'numeric' || second === '2-digit') + options.second = second; + const tz = el.getAttribute('time-zone-name'); + if (tz === 'short' || tz === 'long') + options.timeZoneName = tz; + if (Object.keys(options).length === 0) { + return; + } + let factory = formatters.get(el); + if (!factory) { + factory = makeFormatter(options); + formatters.set(el, factory); + } + const formatter = factory(); + if (formatter) { + return formatter.format(date); + } + else { + const timef = options.second ? '%H:%M:%S' : '%H:%M'; + return strftime(date, timef); + } +} +if (!window.customElements.get('local-time')) { + window.LocalTimeElement = LocalTimeElement; + window.customElements.define('local-time', LocalTimeElement); +} + +class RelativeTime { + constructor(date, locale) { + this.date = date; + this.locale = locale; + } + toString() { + const ago = this.timeElapsed(); + if (ago) { + return ago; + } + else { + const ahead = this.timeAhead(); + if (ahead) { + return ahead; + } + else { + return `on ${this.formatDate()}`; + } + } + } + timeElapsed() { + const ms = new Date().getTime() - this.date.getTime(); + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + if (ms >= 0 && day < 30) { + return this.timeAgoFromMs(ms); + } + else { + return null; + } + } + timeAhead() { + const ms = this.date.getTime() - new Date().getTime(); + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + if (ms >= 0 && day < 30) { + return this.timeUntil(); + } + else { + return null; + } + } + timeAgo() { + const ms = new Date().getTime() - this.date.getTime(); + return this.timeAgoFromMs(ms); + } + timeAgoFromMs(ms) { + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + const month = Math.round(day / 30); + const year = Math.round(month / 12); + if (ms < 0) { + return formatRelativeTime(this.locale, 0, 'second'); + } + else if (sec < 10) { + return formatRelativeTime(this.locale, 0, 'second'); + } + else if (sec < 45) { + return formatRelativeTime(this.locale, -sec, 'second'); + } + else if (sec < 90) { + return formatRelativeTime(this.locale, -min, 'minute'); + } + else if (min < 45) { + return formatRelativeTime(this.locale, -min, 'minute'); + } + else if (min < 90) { + return formatRelativeTime(this.locale, -hr, 'hour'); + } + else if (hr < 24) { + return formatRelativeTime(this.locale, -hr, 'hour'); + } + else if (hr < 36) { + return formatRelativeTime(this.locale, -day, 'day'); + } + else if (day < 30) { + return formatRelativeTime(this.locale, -day, 'day'); + } + else if (month < 18) { + return formatRelativeTime(this.locale, -month, 'month'); + } + else { + return formatRelativeTime(this.locale, -year, 'year'); + } + } + microTimeAgo() { + const ms = new Date().getTime() - this.date.getTime(); + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + const month = Math.round(day / 30); + const year = Math.round(month / 12); + if (min < 1) { + return '1m'; + } + else if (min < 60) { + return `${min}m`; + } + else if (hr < 24) { + return `${hr}h`; + } + else if (day < 365) { + return `${day}d`; + } + else { + return `${year}y`; + } + } + timeUntil() { + const ms = this.date.getTime() - new Date().getTime(); + return this.timeUntilFromMs(ms); + } + timeUntilFromMs(ms) { + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + const month = Math.round(day / 30); + const year = Math.round(month / 12); + if (month >= 18) { + return formatRelativeTime(this.locale, year, 'year'); + } + else if (month >= 12) { + return formatRelativeTime(this.locale, year, 'year'); + } + else if (day >= 45) { + return formatRelativeTime(this.locale, month, 'month'); + } + else if (day >= 30) { + return formatRelativeTime(this.locale, month, 'month'); + } + else if (hr >= 36) { + return formatRelativeTime(this.locale, day, 'day'); + } + else if (hr >= 24) { + return formatRelativeTime(this.locale, day, 'day'); + } + else if (min >= 90) { + return formatRelativeTime(this.locale, hr, 'hour'); + } + else if (min >= 45) { + return formatRelativeTime(this.locale, hr, 'hour'); + } + else if (sec >= 90) { + return formatRelativeTime(this.locale, min, 'minute'); + } + else if (sec >= 45) { + return formatRelativeTime(this.locale, min, 'minute'); + } + else if (sec >= 10) { + return formatRelativeTime(this.locale, sec, 'second'); + } + else { + return formatRelativeTime(this.locale, 0, 'second'); + } + } + microTimeUntil() { + const ms = this.date.getTime() - new Date().getTime(); + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + const month = Math.round(day / 30); + const year = Math.round(month / 12); + if (day >= 365) { + return `${year}y`; + } + else if (hr >= 24) { + return `${day}d`; + } + else if (min >= 60) { + return `${hr}h`; + } + else if (min > 1) { + return `${min}m`; + } + else { + return '1m'; + } + } + formatDate() { + let format = isDayFirst() ? '%e %b' : '%b %e'; + if (!isThisYear(this.date)) { + format += isYearSeparator() ? ', %Y' : ' %Y'; + } + return strftime(this.date, format); + } + formatTime() { + const formatter = timeFormatter(); + if (formatter) { + return formatter.format(this.date); + } + else { + return strftime(this.date, '%l:%M%P'); + } + } +} +function formatRelativeTime(locale, value, unit) { + const formatter = makeRelativeFormat(locale, { numeric: 'auto' }); + if (formatter) { + return formatter.format(value, unit); + } + else { + return formatEnRelativeTime(value, unit); + } +} +function formatEnRelativeTime(value, unit) { + if (value === 0) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + return `this ${unit}`; + case 'day': + return 'today'; + case 'hour': + case 'minute': + return `in 0 ${unit}s`; + case 'second': + return 'now'; + } + } + else if (value === 1) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + return `next ${unit}`; + case 'day': + return 'tomorrow'; + case 'hour': + case 'minute': + case 'second': + return `in 1 ${unit}`; + } + } + else if (value === -1) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + return `last ${unit}`; + case 'day': + return 'yesterday'; + case 'hour': + case 'minute': + case 'second': + return `1 ${unit} ago`; + } + } + else if (value > 1) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + case 'day': + case 'hour': + case 'minute': + case 'second': + return `in ${value} ${unit}s`; + } + } + else if (value < -1) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + case 'day': + case 'hour': + case 'minute': + case 'second': + return `${-value} ${unit}s ago`; + } + } + throw new RangeError(`Invalid unit argument for format() '${unit}'`); +} +const timeFormatter = makeFormatter({ hour: 'numeric', minute: '2-digit' }); + +class RelativeTimeElement extends ExtendedTimeElement { + getFormattedDate() { + const date = this.date; + if (!date) + return; + return new RelativeTime(date, localeFromElement(this)).toString(); + } + connectedCallback() { + nowElements.push(this); + if (!updateNowElementsId) { + updateNowElements(); + updateNowElementsId = window.setInterval(updateNowElements, 60 * 1000); + } + super.connectedCallback(); + } + disconnectedCallback() { + const ix = nowElements.indexOf(this); + if (ix !== -1) { + nowElements.splice(ix, 1); + } + if (!nowElements.length) { + if (updateNowElementsId) { + clearInterval(updateNowElementsId); + updateNowElementsId = null; + } + } + } +} +const nowElements = []; +let updateNowElementsId; +function updateNowElements() { + let time, i, len; + for (i = 0, len = nowElements.length; i < len; i++) { + time = nowElements[i]; + time.textContent = time.getFormattedDate() || ''; + } +} +if (!window.customElements.get('relative-time')) { + window.RelativeTimeElement = RelativeTimeElement; + window.customElements.define('relative-time', RelativeTimeElement); +} + +class TimeAgoElement extends RelativeTimeElement { + getFormattedDate() { + const format = this.getAttribute('format'); + const date = this.date; + if (!date) + return; + if (format === 'micro') { + return new RelativeTime(date, localeFromElement(this)).microTimeAgo(); + } + else { + return new RelativeTime(date, localeFromElement(this)).timeAgo(); + } + } +} +if (!window.customElements.get('time-ago')) { + window.TimeAgoElement = TimeAgoElement; + window.customElements.define('time-ago', TimeAgoElement); +} + +class TimeUntilElement extends RelativeTimeElement { + getFormattedDate() { + const format = this.getAttribute('format'); + const date = this.date; + if (!date) + return; + if (format === 'micro') { + return new RelativeTime(date, localeFromElement(this)).microTimeUntil(); + } + else { + return new RelativeTime(date, localeFromElement(this)).timeUntil(); + } + } +} +if (!window.customElements.get('time-until')) { + window.TimeUntilElement = TimeUntilElement; + window.customElements.define('time-until', TimeUntilElement); +} + +export { LocalTimeElement, RelativeTimeElement, TimeAgoElement, TimeUntilElement }; diff --git a/qortal-ui-plugins/plugins/core/components/time-elements/local-time-element.js b/qortal-ui-plugins/plugins/core/components/time-elements/local-time-element.js new file mode 100644 index 00000000..3494d218 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/time-elements/local-time-element.js @@ -0,0 +1,81 @@ +import { strftime, makeFormatter, isDayFirst } from './utils'; +import ExtendedTimeElement from './extended-time-element'; +const formatters = new WeakMap(); +export default class LocalTimeElement extends ExtendedTimeElement { + attributeChangedCallback(attrName, oldValue, newValue) { + if (attrName === 'hour' || attrName === 'minute' || attrName === 'second' || attrName === 'time-zone-name') { + formatters.delete(this); + } + super.attributeChangedCallback(attrName, oldValue, newValue); + } + getFormattedDate() { + const d = this.date; + if (!d) + return; + const date = formatDate(this, d) || ''; + const time = formatTime(this, d) || ''; + return `${date} ${time}`.trim(); + } +} +function formatDate(el, date) { + const props = { + weekday: { + short: '%a', + long: '%A' + }, + day: { + numeric: '%e', + '2-digit': '%d' + }, + month: { + short: '%b', + long: '%B' + }, + year: { + numeric: '%Y', + '2-digit': '%y' + } + }; + let format = isDayFirst() ? 'weekday day month year' : 'weekday month day, year'; + for (const prop in props) { + const value = props[prop][el.getAttribute(prop) || '']; + format = format.replace(prop, value || ''); + } + format = format.replace(/(\s,)|(,\s$)/, ''); + return strftime(date, format).replace(/\s+/, ' ').trim(); +} +function formatTime(el, date) { + const options = {}; + const hour = el.getAttribute('hour'); + if (hour === 'numeric' || hour === '2-digit') + options.hour = hour; + const minute = el.getAttribute('minute'); + if (minute === 'numeric' || minute === '2-digit') + options.minute = minute; + const second = el.getAttribute('second'); + if (second === 'numeric' || second === '2-digit') + options.second = second; + const tz = el.getAttribute('time-zone-name'); + if (tz === 'short' || tz === 'long') + options.timeZoneName = tz; + if (Object.keys(options).length === 0) { + return; + } + let factory = formatters.get(el); + if (!factory) { + factory = makeFormatter(options); + formatters.set(el, factory); + } + const formatter = factory(); + if (formatter) { + return formatter.format(date); + } + else { + const timef = options.second ? '%H:%M:%S' : '%H:%M'; + return strftime(date, timef); + } +} +if (!window.customElements.get('local-time')) { + window.LocalTimeElement = LocalTimeElement; + window.customElements.define('local-time', LocalTimeElement); +} diff --git a/qortal-ui-plugins/plugins/core/components/time-elements/relative-time-element.js b/qortal-ui-plugins/plugins/core/components/time-elements/relative-time-element.js new file mode 100644 index 00000000..5654e08d --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/time-elements/relative-time-element.js @@ -0,0 +1,44 @@ +import RelativeTime from './relative-time'; +import ExtendedTimeElement from './extended-time-element'; +import { localeFromElement } from './utils'; +export default class RelativeTimeElement extends ExtendedTimeElement { + getFormattedDate() { + const date = this.date; + if (!date) + return; + return new RelativeTime(date, localeFromElement(this)).toString(); + } + connectedCallback() { + nowElements.push(this); + if (!updateNowElementsId) { + updateNowElements(); + updateNowElementsId = window.setInterval(updateNowElements, 60 * 1000); + } + super.connectedCallback(); + } + disconnectedCallback() { + const ix = nowElements.indexOf(this); + if (ix !== -1) { + nowElements.splice(ix, 1); + } + if (!nowElements.length) { + if (updateNowElementsId) { + clearInterval(updateNowElementsId); + updateNowElementsId = null; + } + } + } +} +const nowElements = []; +let updateNowElementsId; +function updateNowElements() { + let time, i, len; + for (i = 0, len = nowElements.length; i < len; i++) { + time = nowElements[i]; + time.textContent = time.getFormattedDate() || ''; + } +} +if (!window.customElements.get('relative-time')) { + window.RelativeTimeElement = RelativeTimeElement; + window.customElements.define('relative-time', RelativeTimeElement); +} diff --git a/qortal-ui-plugins/plugins/core/components/time-elements/relative-time.js b/qortal-ui-plugins/plugins/core/components/time-elements/relative-time.js new file mode 100644 index 00000000..a2b4a6e5 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/time-elements/relative-time.js @@ -0,0 +1,290 @@ +import { strftime, makeFormatter, makeRelativeFormat, isDayFirst, isThisYear, isYearSeparator } from './utils'; +export default class RelativeTime { + constructor(date, locale) { + this.date = date; + this.locale = locale; + } + toString() { + const ago = this.timeElapsed(); + if (ago) { + return ago; + } + else { + const ahead = this.timeAhead(); + if (ahead) { + return ahead; + } + else { + return `on ${this.formatDate()}`; + } + } + } + timeElapsed() { + const ms = new Date().getTime() - this.date.getTime(); + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + if (ms >= 0 && day < 30) { + return this.timeAgoFromMs(ms); + } + else { + return null; + } + } + timeAhead() { + const ms = this.date.getTime() - new Date().getTime(); + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + if (ms >= 0 && day < 30) { + return this.timeUntil(); + } + else { + return null; + } + } + timeAgo() { + const ms = new Date().getTime() - this.date.getTime(); + return this.timeAgoFromMs(ms); + } + timeAgoFromMs(ms) { + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + const month = Math.round(day / 30); + const year = Math.round(month / 12); + if (ms < 0) { + return formatRelativeTime(this.locale, 0, 'second'); + } + else if (sec < 10) { + return formatRelativeTime(this.locale, 0, 'second'); + } + else if (sec < 45) { + return formatRelativeTime(this.locale, -sec, 'second'); + } + else if (sec < 90) { + return formatRelativeTime(this.locale, -min, 'minute'); + } + else if (min < 45) { + return formatRelativeTime(this.locale, -min, 'minute'); + } + else if (min < 90) { + return formatRelativeTime(this.locale, -hr, 'hour'); + } + else if (hr < 24) { + return formatRelativeTime(this.locale, -hr, 'hour'); + } + else if (hr < 36) { + return formatRelativeTime(this.locale, -day, 'day'); + } + else if (day < 30) { + return formatRelativeTime(this.locale, -day, 'day'); + } + else if (month < 18) { + return formatRelativeTime(this.locale, -month, 'month'); + } + else { + return formatRelativeTime(this.locale, -year, 'year'); + } + } + microTimeAgo() { + const ms = new Date().getTime() - this.date.getTime(); + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + const month = Math.round(day / 30); + const year = Math.round(month / 12); + if (min < 1) { + return '1m'; + } + else if (min < 60) { + return `${min}m`; + } + else if (hr < 24) { + return `${hr}h`; + } + else if (day < 365) { + return `${day}d`; + } + else { + return `${year}y`; + } + } + timeUntil() { + const ms = this.date.getTime() - new Date().getTime(); + return this.timeUntilFromMs(ms); + } + timeUntilFromMs(ms) { + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + const month = Math.round(day / 30); + const year = Math.round(month / 12); + if (month >= 18) { + return formatRelativeTime(this.locale, year, 'year'); + } + else if (month >= 12) { + return formatRelativeTime(this.locale, year, 'year'); + } + else if (day >= 45) { + return formatRelativeTime(this.locale, month, 'month'); + } + else if (day >= 30) { + return formatRelativeTime(this.locale, month, 'month'); + } + else if (hr >= 36) { + return formatRelativeTime(this.locale, day, 'day'); + } + else if (hr >= 24) { + return formatRelativeTime(this.locale, day, 'day'); + } + else if (min >= 90) { + return formatRelativeTime(this.locale, hr, 'hour'); + } + else if (min >= 45) { + return formatRelativeTime(this.locale, hr, 'hour'); + } + else if (sec >= 90) { + return formatRelativeTime(this.locale, min, 'minute'); + } + else if (sec >= 45) { + return formatRelativeTime(this.locale, min, 'minute'); + } + else if (sec >= 10) { + return formatRelativeTime(this.locale, sec, 'second'); + } + else { + return formatRelativeTime(this.locale, 0, 'second'); + } + } + microTimeUntil() { + const ms = this.date.getTime() - new Date().getTime(); + const sec = Math.round(ms / 1000); + const min = Math.round(sec / 60); + const hr = Math.round(min / 60); + const day = Math.round(hr / 24); + const month = Math.round(day / 30); + const year = Math.round(month / 12); + if (day >= 365) { + return `${year}y`; + } + else if (hr >= 24) { + return `${day}d`; + } + else if (min >= 60) { + return `${hr}h`; + } + else if (min > 1) { + return `${min}m`; + } + else { + return '1m'; + } + } + formatDate() { + let format = isDayFirst() ? '%e %b' : '%b %e'; + if (!isThisYear(this.date)) { + format += isYearSeparator() ? ', %Y' : ' %Y'; + } + return strftime(this.date, format); + } + formatTime() { + const formatter = timeFormatter(); + if (formatter) { + return formatter.format(this.date); + } + else { + return strftime(this.date, '%l:%M%P'); + } + } +} +function formatRelativeTime(locale, value, unit) { + const formatter = makeRelativeFormat(locale, { numeric: 'auto' }); + if (formatter) { + return formatter.format(value, unit); + } + else { + return formatEnRelativeTime(value, unit); + } +} +function formatEnRelativeTime(value, unit) { + if (value === 0) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + return `this ${unit}`; + case 'day': + return 'today'; + case 'hour': + case 'minute': + return `in 0 ${unit}s`; + case 'second': + return 'now'; + } + } + else if (value === 1) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + return `next ${unit}`; + case 'day': + return 'tomorrow'; + case 'hour': + case 'minute': + case 'second': + return `in 1 ${unit}`; + } + } + else if (value === -1) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + return `last ${unit}`; + case 'day': + return 'yesterday'; + case 'hour': + case 'minute': + case 'second': + return `1 ${unit} ago`; + } + } + else if (value > 1) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + case 'day': + case 'hour': + case 'minute': + case 'second': + return `in ${value} ${unit}s`; + } + } + else if (value < -1) { + switch (unit) { + case 'year': + case 'quarter': + case 'month': + case 'week': + case 'day': + case 'hour': + case 'minute': + case 'second': + return `${-value} ${unit}s ago`; + } + } + throw new RangeError(`Invalid unit argument for format() '${unit}'`); +} +const timeFormatter = makeFormatter({ hour: 'numeric', minute: '2-digit' }); diff --git a/qortal-ui-plugins/plugins/core/components/time-elements/time-ago-element.js b/qortal-ui-plugins/plugins/core/components/time-elements/time-ago-element.js new file mode 100644 index 00000000..6ac6b25a --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/time-elements/time-ago-element.js @@ -0,0 +1,21 @@ +import RelativeTime from './relative-time'; +import RelativeTimeElement from './relative-time-element'; +import { localeFromElement } from './utils'; +export default class TimeAgoElement extends RelativeTimeElement { + getFormattedDate() { + const format = this.getAttribute('format'); + const date = this.date; + if (!date) + return; + if (format === 'micro') { + return new RelativeTime(date, localeFromElement(this)).microTimeAgo(); + } + else { + return new RelativeTime(date, localeFromElement(this)).timeAgo(); + } + } +} +if (!window.customElements.get('time-ago')) { + window.TimeAgoElement = TimeAgoElement; + window.customElements.define('time-ago', TimeAgoElement); +} diff --git a/qortal-ui-plugins/plugins/core/components/time-elements/time-until-element.js b/qortal-ui-plugins/plugins/core/components/time-elements/time-until-element.js new file mode 100644 index 00000000..2b57a845 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/time-elements/time-until-element.js @@ -0,0 +1,21 @@ +import RelativeTime from './relative-time'; +import RelativeTimeElement from './relative-time-element'; +import { localeFromElement } from './utils'; +export default class TimeUntilElement extends RelativeTimeElement { + getFormattedDate() { + const format = this.getAttribute('format'); + const date = this.date; + if (!date) + return; + if (format === 'micro') { + return new RelativeTime(date, localeFromElement(this)).microTimeUntil(); + } + else { + return new RelativeTime(date, localeFromElement(this)).timeUntil(); + } + } +} +if (!window.customElements.get('time-until')) { + window.TimeUntilElement = TimeUntilElement; + window.customElements.define('time-until', TimeUntilElement); +} diff --git a/qortal-ui-plugins/plugins/core/components/time-elements/utils.js b/qortal-ui-plugins/plugins/core/components/time-elements/utils.js new file mode 100644 index 00000000..7bf4b1e7 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/components/time-elements/utils.js @@ -0,0 +1,166 @@ +const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; +const months = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' +]; +function pad(num) { + return `0${num}`.slice(-2); +} +export function strftime(time, formatString) { + const day = time.getDay(); + const date = time.getDate(); + const month = time.getMonth(); + const year = time.getFullYear(); + const hour = time.getHours(); + const minute = time.getMinutes(); + const second = time.getSeconds(); + return formatString.replace(/%([%aAbBcdeHIlmMpPSwyYZz])/g, function (_arg) { + let match; + const modifier = _arg[1]; + switch (modifier) { + case '%': + return '%'; + case 'a': + return weekdays[day].slice(0, 3); + case 'A': + return weekdays[day]; + case 'b': + return months[month].slice(0, 3); + case 'B': + return months[month]; + case 'c': + return time.toString(); + case 'd': + return pad(date); + case 'e': + return String(date); + case 'H': + return pad(hour); + case 'I': + return pad(strftime(time, '%l')); + case 'l': + if (hour === 0 || hour === 12) { + return String(12); + } + else { + return String((hour + 12) % 12); + } + case 'm': + return pad(month + 1); + case 'M': + return pad(minute); + case 'p': + if (hour > 11) { + return 'PM'; + } + else { + return 'AM'; + } + case 'P': + if (hour > 11) { + return 'pm'; + } + else { + return 'am'; + } + case 'S': + return pad(second); + case 'w': + return String(day); + case 'y': + return pad(year % 100); + case 'Y': + return String(year); + case 'Z': + match = time.toString().match(/\((\w+)\)$/); + return match ? match[1] : ''; + case 'z': + match = time.toString().match(/\w([+-]\d\d\d\d) /); + return match ? match[1] : ''; + } + return ''; + }); +} +export function makeFormatter(options) { + let format; + return function () { + if (format) + return format; + if ('Intl' in window) { + try { + format = new Intl.DateTimeFormat(undefined, options); + return format; + } + catch (e) { + if (!(e instanceof RangeError)) { + throw e; + } + } + } + }; +} +let dayFirst = null; +const dayFirstFormatter = makeFormatter({ day: 'numeric', month: 'short' }); +export function isDayFirst() { + if (dayFirst !== null) { + return dayFirst; + } + const formatter = dayFirstFormatter(); + if (formatter) { + const output = formatter.format(new Date(0)); + dayFirst = !!output.match(/^\d/); + return dayFirst; + } + else { + return false; + } +} +let yearSeparator = null; +const yearFormatter = makeFormatter({ day: 'numeric', month: 'short', year: 'numeric' }); +export function isYearSeparator() { + if (yearSeparator !== null) { + return yearSeparator; + } + const formatter = yearFormatter(); + if (formatter) { + const output = formatter.format(new Date(0)); + yearSeparator = !!output.match(/\d,/); + return yearSeparator; + } + else { + return true; + } +} +export function isThisYear(date) { + const now = new Date(); + return now.getUTCFullYear() === date.getUTCFullYear(); +} +export function makeRelativeFormat(locale, options) { + if ('Intl' in window && 'RelativeTimeFormat' in window.Intl) { + try { + return new Intl.RelativeTimeFormat(locale, options); + } + catch (e) { + if (!(e instanceof RangeError)) { + throw e; + } + } + } +} +export function localeFromElement(el) { + const container = el.closest('[lang]'); + if (container instanceof HTMLElement && container.lang) { + return container.lang; + } + return 'default'; +} From ec22beb513754066bb57ed2c094180a760ee6b35 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Thu, 5 Jan 2023 17:01:36 +0100 Subject: [PATCH 08/14] Update deps --- qortal-ui-core/package.json | 4 ++-- qortal-ui-plugins/package.json | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/qortal-ui-core/package.json b/qortal-ui-core/package.json index 7cdec4ba..d5b00b64 100644 --- a/qortal-ui-core/package.json +++ b/qortal-ui-core/package.json @@ -22,7 +22,7 @@ "sass": "1.57.1" }, "devDependencies": { - "@babel/core": "7.20.7", + "@babel/core": "7.20.12", "@material/mwc-button": "0.27.0", "@material/mwc-checkbox": "0.27.0", "@material/mwc-dialog": "0.27.0", @@ -74,7 +74,7 @@ "random-sentence-generator": "0.0.8", "redux": "4.2.0", "redux-thunk": "2.4.2", - "rollup": "3.9.0", + "rollup": "3.9.1", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2", "rollup-plugin-scss": "3.0.0" diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json index 88dfd7af..ad9f149a 100644 --- a/qortal-ui-plugins/package.json +++ b/qortal-ui-plugins/package.json @@ -22,8 +22,7 @@ "emoji-picker-js": "https://github.com/Qortal/emoji-picker-js" }, "devDependencies": { - "@babel/core": "7.20.7", - "@github/time-elements": "3.1.2", + "@babel/core": "7.20.12", "@material/mwc-button": "0.27.0", "@material/mwc-checkbox": "0.27.0", "@material/mwc-dialog": "0.27.0", @@ -59,11 +58,11 @@ "html-escaper": "3.0.3", "lit": "2.5.0", "lit-translate": "2.0.1", - "rollup": "3.9.0", + "rollup": "3.9.1", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-progress": "1.1.2" }, "engines": { "node": ">=16.17.1" } -} +} \ No newline at end of file From d4e6caaf31e196adea599e7b0615d1530a3fd9af Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Sat, 7 Jan 2023 12:41:39 +0100 Subject: [PATCH 09/14] Add export master key --- qortal-ui-core/language/de.json | 6 +- qortal-ui-core/language/es.json | 6 +- qortal-ui-core/language/fr.json | 6 +- qortal-ui-core/language/hindi.json | 6 +- qortal-ui-core/language/hr.json | 6 +- qortal-ui-core/language/hu.json | 6 +- qortal-ui-core/language/it.json | 6 +- qortal-ui-core/language/ko.json | 6 +- qortal-ui-core/language/no.json | 6 +- qortal-ui-core/language/pl.json | 6 +- qortal-ui-core/language/pt.json | 6 +- qortal-ui-core/language/ro.json | 6 +- qortal-ui-core/language/rs.json | 6 +- qortal-ui-core/language/ru.json | 6 +- qortal-ui-core/language/us.json | 6 +- qortal-ui-core/language/zhc.json | 6 +- qortal-ui-core/language/zht.json | 6 +- .../components/settings-view/export-keys.js | 236 ++++++++++++++++++ .../components/settings-view/user-settings.js | 25 +- 19 files changed, 338 insertions(+), 25 deletions(-) create mode 100644 qortal-ui-core/src/components/settings-view/export-keys.js diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index f10ddfe7..cedb6452 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -128,7 +128,11 @@ "snack2":"UI mit Knoten verbunden", "snack3":"Benutzerdefinierter Knoten erfolgreich hinzugefügt und gespeichert", "snack4":"Knoten erfolgreich gespeichert als", - "snack5":"Knoten erfolgreich importiert" + "snack5":"Knoten erfolgreich importiert", + "exp1":"Privaten Hauptschlüssel exportieren", + "exp2":"Hauptschlüssel exportieren", + "exp3":"Exportieren", + "exp4":"Bitte wählen Sie eine Brieftasche aus, um den privaten Hauptschlüssel zu sichern." }, "appinfo":{ "blockheight":"Blockhöhe", diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index 4ae55367..adf6d85c 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -128,7 +128,11 @@ "snack2":"UI conectada al nodo", "snack3":"Nodo personalizado agregado y guardado con éxito", "snack4":"Nodos guardados con éxito como", - "snack5":"Nodos importados con éxito" + "snack5":"Nodos importados con éxito", + "exp1":"Exportar clave maestra privada", + "exp2":"Exportar clave maestra", + "exp3":"Exportar", + "exp4":"Elija una billetera para hacer una copia de seguridad de la clave maestra privada." }, "appinfo":{ "blockheight":"Altura del Bloque", diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index 03b21617..86b02d49 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -128,7 +128,11 @@ "snack2":"Interface utilisateur connectée au noeud", "snack3":"Noeud personnalisé ajouté et enregistré avec succès", "snack4":"Les noeuds ont été enregistrés avec succès sous", - "snack5":"Les noeuds ont été importés avec succès" + "snack5":"Les noeuds ont été importés avec succès", + "exp1":"Exporter la clé principale privée", + "exp2":"Exporter la clé principale", + "exp3":"Exporter", + "exp4":"Veuillez choisir un portefeuille pour sauvegarder la clé principale privée." }, "appinfo":{ "blockheight":"Hauteur de bloc", diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index 02479e0f..e82ab1b0 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -129,7 +129,11 @@ "snack2":"यूआई नोड से जुड़ा", "snack3":"कस्टम नोड को सफलतापूर्वक जोड़ा और सहेजा गया", "snack4":"नोड्स सफलतापूर्वक सहेजे गए", - "snack5":"नोड्स सफलतापूर्वक आयात किए गए" + "snack5":"नोड्स सफलतापूर्वक आयात किए गए", + "exp1":"निजी मास्टर कुंजी निर्यात करें", + "exp2":"निर्यात मास्टर कुंजी", + "exp3":"निर्यात", + "exp4":"निजी मास्टर कुंजी का बैकअप लेने के लिए कृपया एक वॉलेट चुनें।" }, "appinfo":{ "blockheight":"ब्लॉक ऊँचाई", diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index dc066af8..40689c8f 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -128,7 +128,11 @@ "snack2":"UI spojen na čvor", "snack3":"Uspješno dodan i spremljen prilagođeni čvor", "snack4":"Čvorovi su uspješno spremljeni kao", - "snack5":"Čvorovi su uspješno uvezeni" + "snack5":"Čvorovi su uspješno uvezeni", + "exp1":"Izvezi privatni glavni ključ", + "exp2":"Glavni ključ izvoza", + "exp3":"Izvoz", + "exp4":"Odaberite novčanik za sigurnosnu kopiju privatnog glavnog ključa." }, "appinfo":{ "blockheight":"Visina bloka", diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index 76f9fed4..7b57ad70 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -128,7 +128,11 @@ "snack2":"UI csatlakozik a csomóponthoz", "snack3":"Az egyéni csomópont sikeresen hozzáadva és mentve", "snack4":"A csomópontok sikeresen mentve másként", - "snack5":"A csomópontok sikeresen importálva" + "snack5":"A csomópontok sikeresen importálva", + "exp1":"Privát főkulcs exportálása", + "exp2":"Főkulcs exportálása", + "exp3":"Exportálás", + "exp4":"Kérjük, válasszon egy tárcát a privát főkulcs biztonsági mentéséhez." }, "appinfo":{ "blockheight":"Blokk Magassága", diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index 42e55e2e..e669f6de 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -128,7 +128,11 @@ "snack2":"Interfaccia utente collegata al nodo", "snack3":"Nodo personalizzato aggiunto e salvato con successo", "snack4":"Nodi salvati con successo come", - "snack5":"Nodi importati correttamente" + "snack5":"Nodi importati correttamente", + "exp1":"Esporta chiave master privata", + "exp2":"Esporta chiave master", + "exp3":"Esporta", + "exp4":"Scegli un portafoglio per il backup della chiave master privata." }, "appinfo":{ "blockheight":"Altezza blocco", diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index 2124eec4..4eed1cbc 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -128,7 +128,11 @@ "snack2":"노드에 연결된 UI", "snack3":"사용자 정의 노드를 성공적으로 추가하고 저장했습니다.", "snack4":"노드가 다음으로 성공적으로 저장되었습니다", - "snack5":"노드를 성공적으로 가져왔습니다." + "snack5":"노드를 성공적으로 가져왔습니다.", + "exp1":"개인 마스터 키 내보내기", + "exp2":"마스터 키 내보내기", + "exp3":"내보내기", + "exp4":"개인 마스터 키를 백업할 지갑을 선택하세요." }, "appinfo":{ "blockheight":"블록 높이", diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index a38abbe8..7795fa96 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -128,7 +128,11 @@ "snack2":"UI koblet til node", "snack3":"Egendefinert node er lagt til og lagret", "snack4":"Noder ble lagret som", - "snack5":"Noder ble importert" + "snack5":"Noder ble importert", + "exp1":"Eksporter privat hovednøkkel", + "exp2":"Eksporter hovednøkkel", + "exp3":"Eksporter", + "exp4":"Velg en lommebok for å sikkerhetskopiere den private hovednøkkelen." }, "appinfo":{ "blockheight":"Blokkhøyde", diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index aff8fbd4..5fbd3224 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -128,7 +128,11 @@ "snack2":"Interfejs użytkownika połączony z węzłem", "snack3":"Pomyślnie dodano i zapisano niestandardowy węzeł", "snack4":"Węzły pomyślnie zapisane jako", - "snack5":"Węzły pomyślnie zaimportowane" + "snack5":"Węzły pomyślnie zaimportowane", + "exp1":"Eksportuj prywatny klucz główny", + "exp2":"Eksportuj klucz główny", + "exp3":"Eksportuj", + "exp4":"Wybierz portfel do wykonania kopii zapasowej prywatnego klucza głównego." }, "appinfo":{ "blockheight":"Wysokość bloku", diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index 035986c0..c731226f 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -128,7 +128,11 @@ "snack2":"UI conectada ao nó", "snack3":"Nó personalizado adicionado e salvo com sucesso", "snack4":"Nós salvos com sucesso como", - "snack5":"Nós importados com sucesso" + "snack5":"Nós importados com sucesso", + "exp1":"Exportar Chave Mestra Privada", + "exp2":"Exportar Chave Mestra", + "exp3":"Exportar", + "exp4":"Por favor, escolha uma carteira para fazer backup da chave mestra privada." }, "appinfo":{ "blockheight":"Altura do Bloco", diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index 891a0be4..60bce846 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -128,7 +128,11 @@ "snack2":"Interfata de utilizare conectata la nod", "snack3":"Nod personalizat adaugat si salvat cu succes", "snack4":"Nodurile au fost salvate cu succes ca", - "snack5":"Nodurile au fost importate cu succes" + "snack5":"Nodurile au fost importate cu succes", + "exp1":"Exportați cheia principală privată", + "exp2":"Exportați cheia principală", + "exp3":"Export", + "exp4":"Vă rugăm să alegeți un portofel pentru a face backup cheii master private." }, "appinfo":{ "blockheight":"Dimensiunea blocului", diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index 432280f7..c7ad6d88 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -128,7 +128,11 @@ "snack2":"UI je povezan sa čvorom", "snack3":"Uspešno dodat i sačuvan prilagođeni čvor", "snack4":"Čvorovi su uspešno sačuvani kao", - "snack5":"Čvorovi su uspešno uvezeni" + "snack5":"Čvorovi su uspešno uvezeni", + "exp1":"Izvezi privatni glavni ključ", + "exp2":"Izvezi glavni ključ", + "exp3":"Izvoz", + "exp4":"Molimo izaberite novčanik za rezervnu kopiju privatnog glavnog ključa." }, "appinfo":{ "blockheight":"Visina Bloka", diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index e40c5ae3..abad841f 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -128,7 +128,11 @@ "snack2":"Пользовательский интерфейс, подключенный к узлу", "snack3":"Пользовательский узел успешно добавлен и сохранен", "snack4":"Узлы успешно сохранены как", - "snack5":"Узлы успешно импортированы" + "snack5":"Узлы успешно импортированы", + "exp1":"Экспорт закрытого мастер-ключа", + "exp2":"Экспорт мастер-ключа", + "exp3":"Экспорт", + "exp4":"Пожалуйста, выберите кошелек для резервного копирования приватного главного ключа." }, "appinfo":{ "blockheight":"Высота блока", diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 7da41ef0..580d0549 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -128,7 +128,11 @@ "snack2":"UI conected to node", "snack3":"Successfully added and saved custom node", "snack4":"Nodes successfully saved as", - "snack5":"Nodes successfully imported" + "snack5":"Nodes successfully imported", + "exp1":"Export Private Master Key", + "exp2":"Export Master Key", + "exp3":"Export", + "exp4":"Please choose a wallet to backup the private master key." }, "appinfo":{ "blockheight":"Block Height", diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index dfd1d98d..b41114a8 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -128,7 +128,11 @@ "snack2":"连接到节点的 UI", "snack3":"成功添加并保存自定义节点", "snack4":"节点成功保存为", - "snack5":"节点成功导入" + "snack5":"节点成功导入", + "exp1":"导出主密钥", + "exp2":"导出主密钥", + "exp3":"导出", + "exp4":"请选择一个钱包来备份私钥。" }, "appinfo":{ "blockheight":"区块高度", diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index 7572a9df..52fa5400 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -128,7 +128,11 @@ "snack2":"連接到節點的 UI", "snack3":"成功添加並保存自定義節點", "snack4":"節點成功保存為", - "snack5":"節點成功導入" + "snack5":"節點成功導入", + "exp1":"導出主密鑰", + "exp2":"導出主密鑰", + "exp3":"導出", + "exp4":"請選擇一個錢包來備份私鑰。" }, "appinfo":{ "blockheight":"區塊高度", diff --git a/qortal-ui-core/src/components/settings-view/export-keys.js b/qortal-ui-core/src/components/settings-view/export-keys.js new file mode 100644 index 00000000..1456ce3a --- /dev/null +++ b/qortal-ui-core/src/components/settings-view/export-keys.js @@ -0,0 +1,236 @@ +import { LitElement, html, css } from 'lit' +import { connect } from 'pwa-helpers' +import { store } from '../../store.js' +import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' + +import '@material/mwc-dialog' +import '@material/mwc-button' +import '@material/mwc-icon' +import FileSaver from 'file-saver' + +class ExportKeys extends connect(store)(LitElement) { + static get properties() { + return { + theme: { type: String, reflect: true }, + backupErrorMessage: { type: String }, + btcPMK: { type: String }, + ltcPMK: { type: String }, + dogePMK: { type: String }, + dgbPMK: { type: String }, + rvnPMK: { type: String }, + btcWALLET: { type: String }, + ltcWALLET: { type: String }, + dogeWALLET: { type: String }, + dgbWALLET: { type: String }, + rvnWALLET: { type: String }, + btcName: { type: String }, + ltcName: { type: String }, + dogeName: { type: String }, + dgbName: { type: String }, + rvnName: { type: String }, + btcShort: { type: String }, + ltcShort: { type: String }, + dogeShort: { type: String }, + dgbShort: { type: String }, + rvnShort: { type: String }, + dWalletAddress: { type: String }, + dPrivateKey: { type: String }, + dCoinName: { type: String }, + dCoinShort: { type: String } + } + } + + static get styles() { + return css` + * { + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-surface: var(--white); + --mdc-dialog-content-ink-color: var(--black); + --mdc-dialog-min-width: 500px; + --mdc-dialog-max-width: 500px; + --lumo-primary-text-color: rgb(0, 167, 245); + --lumo-primary-color-50pct: rgba(0, 167, 245, 0.5); + --lumo-primary-color-10pct: rgba(0, 167, 245, 0.1); + --lumo-primary-color: hsl(199, 100%, 48%); + --lumo-base-color: var(--white); + --lumo-body-text-color: var(--black); + --lumo-secondary-text-color: var(--sectxt); + --lumo-contrast-60pct: var(--vdicon); + } + + .center-box { + position: absolute; + width: 100%; + top: 50%; + left: 50%; + transform: translate(-50%, 0%); + text-align: center; + } + + .sub-main { + position: relative; + text-align: center; + width: 100%; + } + + .content-box { + text-align: center; + display: inline-block; + min-width: 400px; + margin-bottom: 10px; + margin-left: 10px; + margin-top: 20px; + } + + .export-button { + display: inline-flex; + flex-direction: column; + justify-content: center; + align-content: center; + border: none; + border-radius: 20px; + padding-left: 10px; + padding-right: 10px; + color: white; + background: #03a9f4; + width: 75%; + font-size: 16px; + cursor: pointer; + height: 40px; + margin-top: 1rem; + text-transform: uppercase; + text-decoration: none; + transition: all .2s; + position: relative; + } + + .red { + --mdc-theme-primary: red; + } + ` + } + + constructor() { + super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.backupErrorMessage = '' + this.btcPMK = store.getState().app.selectedAddress.btcWallet.derivedMasterPrivateKey + this.btcWALLET = store.getState().app.selectedAddress.btcWallet.address + this.btcName = 'Bitcoin' + this.btcShort = 'btc' + this.ltcPMK = store.getState().app.selectedAddress.ltcWallet.derivedMasterPrivateKey + this.ltcWALLET = store.getState().app.selectedAddress.ltcWallet.address + this.ltcName = 'Litecoin' + this.ltcShort = 'ltc' + this.dogePMK = store.getState().app.selectedAddress.dogeWallet.derivedMasterPrivateKey + this.dogeWALLET = store.getState().app.selectedAddress.dogeWallet.address + this.dogeName = 'Dogecoin' + this.dogeShort = 'doge' + this.dgbPMK = store.getState().app.selectedAddress.dgbWallet.derivedMasterPrivateKey + this.dgbWALLET = store.getState().app.selectedAddress.dgbWallet.address + this.dgbName = 'Digibyte' + this.dgbShort = 'dgb' + this.rvnPMK = store.getState().app.selectedAddress.rvnWallet.derivedMasterPrivateKey + this.rvnWALLET = store.getState().app.selectedAddress.rvnWallet.address + this.rvnName = 'Ravencoin' + this.rvnShort = 'rvn' + this.dWalletAddress = '' + this.dPrivateKey = '' + this.dCoinName = '' + this.dCoinShort = 'btc' + } + + render() { + return html` +
+
+

+ ${translate("settings.exp4")} +

+
+
+
+
+
+   ${this.btcWALLET}
+
+
this.checkForPmkDownload(this.btcWALLET, this.btcPMK, this.btcName, this.btcShort)} class="export-button"> ${translate("settings.exp2")}
+
+
+
+   ${this.ltcWALLET}
+
+
this.checkForPmkDownload(this.ltcWALLET, this.ltcPMK, this.ltcName, this.ltcShort)} class="export-button"> ${translate("settings.exp2")}
+
+
+
+   ${this.dogeWALLET}
+
+
this.checkForPmkDownload(this.dogeWALLET, this.dogePMK, this.dogeName, this.dogeShort)} class="export-button"> ${translate("settings.exp2")}
+
+
+
+   ${this.dgbWALLET}
+
+
this.checkForPmkDownload(this.dgbWALLET, this.dgbPMK, this.dgbName, this.dgbShort)} class="export-button"> ${translate("settings.exp2")}
+
+
+
+   ${this.rvnWALLET}
+
+
this.checkForPmkDownload(this.rvnWALLET, this.rvnPMK, this.rvnName, this.rvnShort)} class="export-button"> ${translate("settings.exp2")}
+
+
+
+ + +

${this.dCoinName} ${translate("settings.exp2")}

+
+

${translate("settings.address")}: ${this.dWalletAddress}

+ + ${translate("general.close")} + + + ${translate("settings.exp3")} + +
+
+ ` + } + + closeSavePkmDialog() { + this.shadowRoot.querySelector('#savePkmDialog').close() + } + + checkForPmkDownload(wAddress, wPkm, wName, wShort) { + this.dWalletAddress = '' + this.dPrivateKey = '' + this.dCoinName = '' + this.dCoinShort = '' + this.dWalletAddress = wAddress + this.dPrivateKey = wPkm + this.dCoinName = wName + this.dCoinShort = wShort + this.shadowRoot.querySelector('#savePkmDialog').show() + } + + async exportKey(cMasterKey, cName, cAddress) { + const myPrivateMasterKey = cMasterKey + const myCoinName = cName + const myCoinAddress = cAddress + const blob = new Blob([`${myPrivateMasterKey}`], { type: 'text/plain;charset=utf-8' }) + FileSaver.saveAs(blob, `Private_Master_Key_${myCoinName}_${myCoinAddress}.txt`) + } + + stateChanged(state) { + } +} + +window.customElements.define('export-keys', ExportKeys) diff --git a/qortal-ui-core/src/components/settings-view/user-settings.js b/qortal-ui-core/src/components/settings-view/user-settings.js index 4d522f17..66a8c887 100644 --- a/qortal-ui-core/src/components/settings-view/user-settings.js +++ b/qortal-ui-core/src/components/settings-view/user-settings.js @@ -10,6 +10,7 @@ import './account-view.js' import './security-view.js' import './notifications-view.js' import './qr-login-view.js' +import './export-keys.js' import { doLogout } from '../../redux/app/app-actions.js' @@ -156,6 +157,7 @@ class UserSettings extends connect(store)(LitElement) { font-size: 16px; text-align: center; min-height: 460px; + height: 60vh; } @media(max-width:700px) { @@ -226,6 +228,7 @@ class UserSettings extends connect(store)(LitElement) { @@ -247,25 +250,29 @@ class UserSettings extends connect(store)(LitElement) { renderSettingViews(selectedView) { if (selectedView.id === 'info') { - return html``; + return html`` } else if (selectedView.id === 'security') { - return html``; + return html`` + } else if (selectedView.id === 'export') { + return html`` } else if (selectedView.id === 'notification') { - return html``; + return html`` } else if (selectedView.id === 'qr-login') { - return html``; + return html`` } } renderHeaderViews() { if (this.selectedView.id === 'info') { - return html`${translate("settings.generalinfo")}`; + return html`${translate("settings.generalinfo")}` } else if (this.selectedView.id === 'security') { - return html`${translate("settings.accountsecurity")}`; + return html`${translate("settings.accountsecurity")}` + } else if (this.selectedView.id === 'export') { + return html`${translate("settings.exp1")}` } else if (this.selectedView.id === 'notification') { - return html`UI ${translate("settings.notifications")}`; + return html`UI ${translate("settings.notifications")}` } else if (this.selectedView.id === 'qr-login') { - return html`${translate("settings.qr_login_menu_item")}`; + return html`${translate("settings.qr_login_menu_item")}` } } @@ -274,6 +281,8 @@ class UserSettings extends connect(store)(LitElement) { return this.selectedView = { id: 'info', name: 'General Account Info' } } else if (pageId === 'security') { return this.selectedView = { id: 'security', name: 'Account Security' } + } else if (pageId === 'export') { + return this.selectedView = { id: 'export', name: 'Export Master Keys' } } else if (pageId === 'notification') { return this.selectedView = { id: 'notification', name: 'UI Notifications' } } else if (pageId === 'qr-login') { From 7fdc33cb82587efe46131b19ca3532075d224ac9 Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Mon, 9 Jan 2023 06:50:36 -0500 Subject: [PATCH 10/14] Adjust spacing --- .../plugins/core/wallet/wallet-app.src.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js b/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js index 197248f4..e8c6a955 100644 --- a/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js +++ b/qortal-ui-plugins/plugins/core/wallet/wallet-app.src.js @@ -55,7 +55,7 @@ class MultiWallet extends LitElement { dogeAmount: { type: Number }, dgbRecipient: { type: String }, dgbAmount: { type: Number }, - rvnRecipient: { type: String }, + rvnRecipient: { type: String }, rvnAmount: { type: Number }, arrrRecipient: { type: String }, arrrAmount: { type: Number }, @@ -73,7 +73,7 @@ class MultiWallet extends LitElement { ltcFeePerByte: { type: Number }, dogeFeePerByte: { type: Number }, dgbFeePerByte: { type: Number }, - rvnFeePerByte: { type: Number }, + rvnFeePerByte: { type: Number }, qortBook: { type: Array }, btcBook: { type: Array }, ltcBook: { type: Array }, @@ -472,7 +472,7 @@ class MultiWallet extends LitElement { background-image: url('/img/dgb.png'); } - .rvn .currency-image { + .rvn .currency-image { background-image: url('/img/rvn.png'); } @@ -508,12 +508,12 @@ class MultiWallet extends LitElement { .btn-clear-success { --mdc-icon-button-size: 32px; color: red; - } + } .btn-clear-error { --mdc-icon-button-size: 32px; color: green; - } + } @keyframes fade-in { 0% { @@ -680,7 +680,7 @@ class MultiWallet extends LitElement { this.isValidAmount = false this.btnDisable = false this.qortWarning = false - this.balance = 0 + this.balance = 0 this.amount = 0 this.btcAmount = 0 this.ltcAmount = 0 @@ -1092,7 +1092,7 @@ class MultiWallet extends LitElement { - +

${translate("walletpage.wchange5")}


@@ -1577,7 +1577,7 @@ class MultiWallet extends LitElement { - +
@@ -2759,7 +2759,7 @@ class MultiWallet extends LitElement { checkSelectedTextAndShowMenu() }) - this.shadowRoot.getElementById('rvnAmountInput').addEventListener('contextmenu', (event) => { + this.shadowRoot.getElementById('rvnAmountInput').addEventListener('contextmenu', (event) => { const getSelectedText = () => { var text = '' if (typeof window.getSelection != 'undefined') { @@ -2809,7 +2809,7 @@ class MultiWallet extends LitElement { checkSelectedTextAndShowMenu() }) - this.shadowRoot.getElementById('arrrAmountInput').addEventListener('contextmenu', (event) => { + this.shadowRoot.getElementById('arrrAmountInput').addEventListener('contextmenu', (event) => { const getSelectedText = () => { var text = '' if (typeof window.getSelection != 'undefined') { @@ -4499,7 +4499,7 @@ class MultiWallet extends LitElement { case 'ltc': case 'doge': case 'dgb': - case 'rvn': + case 'rvn': this.balanceString = this.renderFetchText() const walletName = `${coin}Wallet` parentEpml.request('apiCall', { @@ -4844,9 +4844,9 @@ class MultiWallet extends LitElement { render(this.renderDogeTransactions(this.wallets.get(this._selectedWallet).transactions, this._selectedWallet), this.transactionsDOM) } else if (this._selectedWallet === 'dgb') { render(this.renderDgbTransactions(this.wallets.get(this._selectedWallet).transactions, this._selectedWallet), this.transactionsDOM) - } else if (this._selectedWallet === 'rvn') { + } else if (this._selectedWallet === 'rvn') { render(this.renderRvnTransactions(this.wallets.get(this._selectedWallet).transactions, this._selectedWallet), this.transactionsDOM) - } else if (this._selectedWallet === 'arrr') { + } else if (this._selectedWallet === 'arrr') { render(this.renderArrrTransactions(this.wallets.get(this._selectedWallet).transactions, this._selectedWallet), this.transactionsDOM) } } From 6e30bb0c69ae76398d1a1483015b522d0dc4473d Mon Sep 17 00:00:00 2001 From: QuickMythril Date: Mon, 9 Jan 2023 06:55:44 -0500 Subject: [PATCH 11/14] Adjust spacing --- .../core/trade-portal/trade-portal.src.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js b/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js index 47a97042..47910961 100644 --- a/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js +++ b/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js @@ -74,7 +74,7 @@ class TradePortal extends LitElement { displayTradeAddress: { type: String }, displayTradeLevel: { type: String }, displayTradeBalance: { type: String }, - qortRatio: {type: Number} + qortRatio: {type: Number} } } @@ -494,7 +494,7 @@ class TradePortal extends LitElement { .rvn.coinName:before { background-image: url('/img/qortrvn.png'); } - .arrr.coinName:before { + .arrr.coinName:before { background-image: url('/img/qortarrr.png'); } .coinName { @@ -655,7 +655,7 @@ class TradePortal extends LitElement { tradeFee: "~0.0005" } - let ravencoin = { + let ravencoin = { name: "RAVENCOIN", balance: "0", coinCode: "RVN", @@ -721,7 +721,7 @@ class TradePortal extends LitElement { handleStuckTradesConnectedWorker: null }) - workers.set("RAVENCOIN", { + workers.set("RAVENCOIN", { tradesConnectedWorker: null, handleStuckTradesConnectedWorker: null }) @@ -1160,7 +1160,7 @@ class TradePortal extends LitElement { QORT / DOGE QORT / DGB QORT / RVN - QORT / ARRR + QORT / ARRR
@@ -1504,8 +1504,8 @@ class TradePortal extends LitElement { case 'DIGIBYTE': _url = `/crosschain/dgb/walletbalance?apiKey=${this.getApiKey()}` _body = window.parent.reduxStore.getState().app.selectedAddress.dgbWallet.derivedMasterPublicKey - break - case 'RAVENCOIN': + break + case 'RAVENCOIN': _url = `/crosschain/rvn/walletbalance?apiKey=${this.getApiKey()}` _body = window.parent.reduxStore.getState().app.selectedAddress.rvnWallet.derivedMasterPublicKey break @@ -2945,4 +2945,4 @@ class TradePortal extends LitElement { } } -window.customElements.define('trade-portal', TradePortal) \ No newline at end of file +window.customElements.define('trade-portal', TradePortal) From 5bb1570a35778c3a41f763709805091631841b27 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Mon, 9 Jan 2023 14:05:13 +0100 Subject: [PATCH 12/14] last child full visible --- qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js index 193883ee..dc59b97f 100644 --- a/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js +++ b/qortal-ui-plugins/plugins/core/messaging/q-chat/q-chat.src.js @@ -122,7 +122,7 @@ class Chat extends LitElement { box-shadow: 0 3px 5px rgba(0, 0, 0, .2); } .people-list ul { - padding: 0; + padding: 0px 0px 60px 0px; height: 85vh; overflow-y: auto; overflow-x: hidden; From f8ceaa56c51eb376b539b29d6e7c15e9bae53558 Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Mon, 9 Jan 2023 15:03:59 +0100 Subject: [PATCH 13/14] Add chart js files --- .../core/trade-portal/charts/arrr-charts.js | 214 +++++++++++++++++ .../core/trade-portal/charts/btc-charts.js | 215 ++++++++++++++++++ .../core/trade-portal/charts/dgb-charts.js | 214 +++++++++++++++++ .../core/trade-portal/charts/doge-charts.js | 214 +++++++++++++++++ .../core/trade-portal/charts/ltc-charts.js | 214 +++++++++++++++++ .../core/trade-portal/charts/rvn-charts.js | 214 +++++++++++++++++ 6 files changed, 1285 insertions(+) create mode 100644 qortal-ui-plugins/plugins/core/trade-portal/charts/arrr-charts.js create mode 100644 qortal-ui-plugins/plugins/core/trade-portal/charts/btc-charts.js create mode 100644 qortal-ui-plugins/plugins/core/trade-portal/charts/dgb-charts.js create mode 100644 qortal-ui-plugins/plugins/core/trade-portal/charts/doge-charts.js create mode 100644 qortal-ui-plugins/plugins/core/trade-portal/charts/ltc-charts.js create mode 100644 qortal-ui-plugins/plugins/core/trade-portal/charts/rvn-charts.js diff --git a/qortal-ui-plugins/plugins/core/trade-portal/charts/arrr-charts.js b/qortal-ui-plugins/plugins/core/trade-portal/charts/arrr-charts.js new file mode 100644 index 00000000..561cd9df --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-portal/charts/arrr-charts.js @@ -0,0 +1,214 @@ +import { LitElement, html, css } from 'lit' +import { render } from 'lit/html.js' +import { Epml } from '../../../../epml.js' +import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' + +registerTranslateConfig({ + loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) +}) + +import '@polymer/paper-dialog/paper-dialog.js' +import * as Highcharts from 'highcharts' +import Exporting from 'highcharts/modules/exporting' +Exporting(Highcharts) +import StockChart from 'highcharts/modules/stock' +StockChart(Highcharts) +import 'highcharts/highcharts-more.js' +import 'highcharts/modules/accessibility.js' +import 'highcharts/modules/boost.js' +import 'highcharts/modules/data.js' +import 'highcharts/modules/export-data.js' +import 'highcharts/modules/offline-exporting.js' + +let arrrChartDialog + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class ArrrCharts extends LitElement { + static get properties() { + return { + isLoadingTradesChart: { type: Boolean }, + arrrTrades: { type: Array }, + arrrPrice: { type: Array } + } + } + + static get styles() { + return css` + .loadingContainer { + height: 100%; + width: 100%; + } + + .trades-chart { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 25px; + padding: 15px; + } + + .chart-container { + margin: auto; + color: var(--black); + text-align: center; + padding: 15px; + height: 30vh; + width: 80vw; + } + + .chart-info-wrapper { + background: transparent; + height: 38vh; + width: 83vw; + overflow: auto; + } + + .chart-loading-wrapper { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 15px; + } + ` + } + + constructor() { + super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.isLoadingTradesChart = false + this.arrrTrades = [] + this.arrrPrice = [] + } + + render() { + return html` + +
+ ${translate("login.loading")} +
+
+ +
+
+
+
+ ` + } + + async firstUpdated() { + this.changeTheme() + this.changeLanguage() + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme) + }) + } + + async loadTradesChart() { + this.isLoadingTradesChart = true + this.shadowRoot.getElementById('loadChartDialog').open() + await this.getArrrTrades() + this.isLoadingTradesChart = false + this.shadowRoot.getElementById('loadChartDialog').close() + this.enableArrrStockPriceChart() + } + + async getArrrTrades() { + let currentArrrTimestamp = Date.now() + const monthBackArrr = currentArrrTimestamp - 31556952000 + await parentEpml.request("apiCall", { url: `/crosschain/trades?foreignBlockchain=PIRATECHAIN&minimumTimestamp=${monthBackArrr}&limit=0&reverse=false` }).then((res) => { + this.arrrTrades = res + }) + this.arrrPrice = this.arrrTrades.map(item => { + const arrrSellPrice = this.round(parseFloat(item.foreignAmount) / parseFloat(item.qortAmount)) + return [item.tradeTimestamp, parseFloat(arrrSellPrice)] + }).filter(item => !!item) + } + + enableArrrStockPriceChart() { + const arrrStockPriceData = this.arrrPrice + const header = 'QORT / ARRR ' + get("tradepage.tchange49") + Highcharts.stockChart(this.shadowRoot.querySelector('#arrrStockPriceContainer'), { + accessibility: { + enabled: false + }, + credits: { + enabled: false + }, + rangeSelector: { + selected: 1, + labelStyle: {color: 'var(--black)'}, + inputStyle: {color: '#03a9f4'} + }, + chart: { + backgroundColor: 'transparent' + }, + title: { + text: header, + style: {color: 'var(--black)'} + }, + xAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + yAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + series: [{ + name: 'QORT / ARRR', + data: arrrStockPriceData, + tooltip: { + valueDecimals: 8 + } + }] + }) + } + + async open() { + await this.loadTradesChart() + this.shadowRoot.getElementById('arrrChartDialog').open() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme); + } + + changeLanguage() { + const checkLanguage = localStorage.getItem('qortalLanguage') + + if (checkLanguage === null || checkLanguage.length === 0) { + localStorage.setItem('qortalLanguage', 'us') + use('us') + } else { + use(checkLanguage) + } + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } +} + +window.customElements.define('arrr-charts', ArrrCharts) + +const chartsarrr = document.createElement('arrr-charts') +arrrChartDialog = document.body.appendChild(chartsarrr) + +export default arrrChartDialog \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-portal/charts/btc-charts.js b/qortal-ui-plugins/plugins/core/trade-portal/charts/btc-charts.js new file mode 100644 index 00000000..c31d272b --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-portal/charts/btc-charts.js @@ -0,0 +1,215 @@ +import { LitElement, html, css } from 'lit' +import { render } from 'lit/html.js' +import { Epml } from '../../../../epml.js' +import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' + +registerTranslateConfig({ + loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) +}) + +import '@polymer/paper-dialog/paper-dialog.js' +import * as Highcharts from 'highcharts' +import Exporting from 'highcharts/modules/exporting' +Exporting(Highcharts) +import StockChart from 'highcharts/modules/stock' +StockChart(Highcharts) +import 'highcharts/highcharts-more.js' +import 'highcharts/modules/accessibility.js' +import 'highcharts/modules/boost.js' +import 'highcharts/modules/data.js' +import 'highcharts/modules/export-data.js' +import 'highcharts/modules/offline-exporting.js' + +let btcChartDialog + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class BtcCharts extends LitElement { + static get properties() { + return { + isLoadingTradesChart: { type: Boolean }, + btcTrades: { type: Array }, + btcPrice: { type: Array } + } + } + + static get styles() { + return css` + .loadingContainer { + height: 100%; + width: 100%; + } + + .trades-chart { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 25px; + padding: 15px; + } + + .chart-container { + margin: auto; + color: var(--black); + text-align: center; + padding: 15px; + height: 30vh; + width: 80vw; + } + + .chart-info-wrapper { + background: transparent; + height: 38vh; + width: 83vw; + overflow: auto; + } + + .chart-loading-wrapper { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 15px; + } + ` + } + + constructor() { + super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.isLoadingTradesChart = false + this.btcTrades = [] + this.btcPrice = [] + } + + render() { + return html` + +
+ ${translate("login.loading")} +
+
+ +
+
+
+
+ ` + } + + async firstUpdated() { + this.changeTheme() + this.changeLanguage() + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme) + }) + } + + async loadTradesChart() { + this.isLoadingTradesChart = true + this.shadowRoot.getElementById('loadChartDialog').open() + await this.getBtcTrades() + this.isLoadingTradesChart = false + this.shadowRoot.getElementById('loadChartDialog').close() + this.enableBtcStockPriceChart() + } + + async getBtcTrades() { + let currentBtcTimestamp = Date.now() + const monthBackBtc = currentBtcTimestamp - 31556952000 + await parentEpml.request("apiCall", { url: `/crosschain/trades?foreignBlockchain=BITCOIN&minimumTimestamp=${monthBackBtc}&limit=0&reverse=false` }).then((res) => { + this.btcTrades = res + }) + + this.btcPrice = this.btcTrades.map(item => { + const btcSellPrice = this.round(parseFloat(item.foreignAmount) / parseFloat(item.qortAmount)) + return [item.tradeTimestamp, parseFloat(btcSellPrice)] + }).filter(item => !!item) + } + + enableBtcStockPriceChart() { + const btcStockPriceData = this.btcPrice + const header = 'QORT / BTC ' + get("tradepage.tchange49") + Highcharts.stockChart(this.shadowRoot.querySelector('#btcStockPriceContainer'), { + accessibility: { + enabled: false + }, + credits: { + enabled: false + }, + rangeSelector: { + selected: 1, + labelStyle: {color: 'var(--black)'}, + inputStyle: {color: '#03a9f4'} + }, + chart: { + backgroundColor: 'transparent' + }, + title: { + text: header, + style: {color: 'var(--black)'} + }, + xAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + yAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + series: [{ + name: 'QORT / BTC', + data: btcStockPriceData, + tooltip: { + valueDecimals: 8 + } + }] + }) + } + + async open() { + await this.loadTradesChart() + this.shadowRoot.getElementById('btcChartDialog').open() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme); + } + + changeLanguage() { + const checkLanguage = localStorage.getItem('qortalLanguage') + + if (checkLanguage === null || checkLanguage.length === 0) { + localStorage.setItem('qortalLanguage', 'us') + use('us') + } else { + use(checkLanguage) + } + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } +} + +window.customElements.define('btc-charts', BtcCharts) + +const chartsbtc = document.createElement('btc-charts') +btcChartDialog = document.body.appendChild(chartsbtc) + +export default btcChartDialog \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-portal/charts/dgb-charts.js b/qortal-ui-plugins/plugins/core/trade-portal/charts/dgb-charts.js new file mode 100644 index 00000000..5e57aad7 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-portal/charts/dgb-charts.js @@ -0,0 +1,214 @@ +import { LitElement, html, css } from 'lit' +import { render } from 'lit/html.js' +import { Epml } from '../../../../epml.js' +import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' + +registerTranslateConfig({ + loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) +}) + +import '@polymer/paper-dialog/paper-dialog.js' +import * as Highcharts from 'highcharts' +import Exporting from 'highcharts/modules/exporting' +Exporting(Highcharts) +import StockChart from 'highcharts/modules/stock' +StockChart(Highcharts) +import 'highcharts/highcharts-more.js' +import 'highcharts/modules/accessibility.js' +import 'highcharts/modules/boost.js' +import 'highcharts/modules/data.js' +import 'highcharts/modules/export-data.js' +import 'highcharts/modules/offline-exporting.js' + +let dgbChartDialog + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class DgbCharts extends LitElement { + static get properties() { + return { + isLoadingTradesChart: { type: Boolean }, + dgbTrades: { type: Array }, + dgbPrice: { type: Array } + } + } + + static get styles() { + return css` + .loadingContainer { + height: 100%; + width: 100%; + } + + .trades-chart { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 25px; + padding: 15px; + } + + .chart-container { + margin: auto; + color: var(--black); + text-align: center; + padding: 15px; + height: 30vh; + width: 80vw; + } + + .chart-info-wrapper { + background: transparent; + height: 38vh; + width: 83vw; + overflow: auto; + } + + .chart-loading-wrapper { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 15px; + } + ` + } + + constructor() { + super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.isLoadingTradesChart = false + this.dgbTrades = [] + this.dgbPrice = [] + } + + render() { + return html` + +
+ ${translate("login.loading")} +
+
+ +
+
+
+
+ ` + } + + async firstUpdated() { + this.changeTheme() + this.changeLanguage() + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme) + }) + } + + async loadTradesChart() { + this.isLoadingTradesChart = true + this.shadowRoot.getElementById('loadChartDialog').open() + await this.getDgbTrades() + this.isLoadingTradesChart = false + this.shadowRoot.getElementById('loadChartDialog').close() + this.enableDgbStockPriceChart() + } + + async getDgbTrades() { + let currentDgbTimestamp = Date.now() + const monthBackDgb = currentDgbTimestamp - 31556952000 + await parentEpml.request("apiCall", { url: `/crosschain/trades?foreignBlockchain=DIGIBYTE&minimumTimestamp=${monthBackDgb}&limit=0&reverse=false` }).then((res) => { + this.dgbTrades = res + }) + this.dgbPrice = this.dgbTrades.map(item => { + const dgbSellPrice = this.round(parseFloat(item.foreignAmount) / parseFloat(item.qortAmount)) + return [item.tradeTimestamp, parseFloat(dgbSellPrice)] + }).filter(item => !!item) + } + + enableDgbStockPriceChart() { + const dgbStockPriceData = this.dgbPrice + const header = 'QORT / DGB ' + get("tradepage.tchange49") + Highcharts.stockChart(this.shadowRoot.querySelector('#dgbStockPriceContainer'), { + accessibility: { + enabled: false + }, + credits: { + enabled: false + }, + rangeSelector: { + selected: 1, + labelStyle: {color: 'var(--black)'}, + inputStyle: {color: '#03a9f4'} + }, + chart: { + backgroundColor: 'transparent' + }, + title: { + text: header, + style: {color: 'var(--black)'} + }, + xAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + yAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + series: [{ + name: 'QORT / DGB', + data: dgbStockPriceData, + tooltip: { + valueDecimals: 8 + } + }] + }) + } + + async open() { + await this.loadTradesChart() + this.shadowRoot.getElementById('dgbChartDialog').open() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme); + } + + changeLanguage() { + const checkLanguage = localStorage.getItem('qortalLanguage') + + if (checkLanguage === null || checkLanguage.length === 0) { + localStorage.setItem('qortalLanguage', 'us') + use('us') + } else { + use(checkLanguage) + } + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } +} + +window.customElements.define('dgb-charts', DgbCharts) + +const chartsdgb = document.createElement('dgb-charts') +dgbChartDialog = document.body.appendChild(chartsdgb) + +export default dgbChartDialog \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-portal/charts/doge-charts.js b/qortal-ui-plugins/plugins/core/trade-portal/charts/doge-charts.js new file mode 100644 index 00000000..a4267361 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-portal/charts/doge-charts.js @@ -0,0 +1,214 @@ +import { LitElement, html, css } from 'lit' +import { render } from 'lit/html.js' +import { Epml } from '../../../../epml.js' +import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' + +registerTranslateConfig({ + loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) +}) + +import '@polymer/paper-dialog/paper-dialog.js' +import * as Highcharts from 'highcharts' +import Exporting from 'highcharts/modules/exporting' +Exporting(Highcharts) +import StockChart from 'highcharts/modules/stock' +StockChart(Highcharts) +import 'highcharts/highcharts-more.js' +import 'highcharts/modules/accessibility.js' +import 'highcharts/modules/boost.js' +import 'highcharts/modules/data.js' +import 'highcharts/modules/export-data.js' +import 'highcharts/modules/offline-exporting.js' + +let dogeChartDialog + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class DogeCharts extends LitElement { + static get properties() { + return { + isLoadingTradesChart: { type: Boolean }, + dogeTrades: { type: Array }, + dogePrice: { type: Array } + } + } + + static get styles() { + return css` + .loadingContainer { + height: 100%; + width: 100%; + } + + .trades-chart { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 25px; + padding: 15px; + } + + .chart-container { + margin: auto; + color: var(--black); + text-align: center; + padding: 15px; + height: 30vh; + width: 80vw; + } + + .chart-info-wrapper { + background: transparent; + height: 38vh; + width: 83vw; + overflow: auto; + } + + .chart-loading-wrapper { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 15px; + } + ` + } + + constructor() { + super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.isLoadingTradesChart = false + this.dogeTrades = [] + this.dogePrice = [] + } + + render() { + return html` + +
+ ${translate("login.loading")} +
+
+ +
+
+
+
+ ` + } + + async firstUpdated() { + this.changeTheme() + this.changeLanguage() + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme) + }) + } + + async loadTradesChart() { + this.isLoadingTradesChart = true + this.shadowRoot.getElementById('loadChartDialog').open() + await this.getDogeTrades() + this.isLoadingTradesChart = false + this.shadowRoot.getElementById('loadChartDialog').close() + this.enableDogeStockPriceChart() + } + + async getDogeTrades() { + let currentDogeTimestamp = Date.now() + const monthBackDoge = currentDogeTimestamp - 31556952000 + await parentEpml.request("apiCall", { url: `/crosschain/trades?foreignBlockchain=DOGECOIN&minimumTimestamp=${monthBackDoge}&limit=0&reverse=false` }).then((res) => { + this.dogeTrades = res + }) + this.dogePrice = this.dogeTrades.map(item => { + const dogeSellPrice = this.round(parseFloat(item.foreignAmount) / parseFloat(item.qortAmount)) + return [item.tradeTimestamp, parseFloat(dogeSellPrice)] + }).filter(item => !!item) + } + + enableDogeStockPriceChart() { + const dogeStockPriceData = this.dogePrice + const header = 'QORT / DOGE ' + get("tradepage.tchange49") + Highcharts.stockChart(this.shadowRoot.querySelector('#dogeStockPriceContainer'), { + accessibility: { + enabled: false + }, + credits: { + enabled: false + }, + rangeSelector: { + selected: 1, + labelStyle: {color: 'var(--black)'}, + inputStyle: {color: '#03a9f4'} + }, + chart: { + backgroundColor: 'transparent' + }, + title: { + text: header, + style: {color: 'var(--black)'} + }, + xAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + yAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + series: [{ + name: 'QORT / DOGE', + data: dogeStockPriceData, + tooltip: { + valueDecimals: 8 + } + }] + }) + } + + async open() { + await this.loadTradesChart() + this.shadowRoot.getElementById('dogeChartDialog').open() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme); + } + + changeLanguage() { + const checkLanguage = localStorage.getItem('qortalLanguage') + + if (checkLanguage === null || checkLanguage.length === 0) { + localStorage.setItem('qortalLanguage', 'us') + use('us') + } else { + use(checkLanguage) + } + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } +} + +window.customElements.define('doge-charts', DogeCharts) + +const chartsdoge = document.createElement('doge-charts') +dogeChartDialog = document.body.appendChild(chartsdoge) + +export default dogeChartDialog \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-portal/charts/ltc-charts.js b/qortal-ui-plugins/plugins/core/trade-portal/charts/ltc-charts.js new file mode 100644 index 00000000..08ffd339 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-portal/charts/ltc-charts.js @@ -0,0 +1,214 @@ +import { LitElement, html, css } from 'lit' +import { render } from 'lit/html.js' +import { Epml } from '../../../../epml.js' +import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' + +registerTranslateConfig({ + loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) +}) + +import '@polymer/paper-dialog/paper-dialog.js' +import * as Highcharts from 'highcharts' +import Exporting from 'highcharts/modules/exporting' +Exporting(Highcharts) +import StockChart from 'highcharts/modules/stock' +StockChart(Highcharts) +import 'highcharts/highcharts-more.js' +import 'highcharts/modules/accessibility.js' +import 'highcharts/modules/boost.js' +import 'highcharts/modules/data.js' +import 'highcharts/modules/export-data.js' +import 'highcharts/modules/offline-exporting.js' + +let ltcChartDialog + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class LtcCharts extends LitElement { + static get properties() { + return { + isLoadingTradesChart: { type: Boolean }, + ltcTrades: { type: Array }, + ltcPrice: { type: Array } + } + } + + static get styles() { + return css` + .loadingContainer { + height: 100%; + width: 100%; + } + + .trades-chart { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 25px; + padding: 15px; + } + + .chart-container { + margin: auto; + color: var(--black); + text-align: center; + padding: 15px; + height: 30vh; + width: 80vw; + } + + .chart-info-wrapper { + background: transparent; + height: 38vh; + width: 83vw; + overflow: auto; + } + + .chart-loading-wrapper { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 15px; + } + ` + } + + constructor() { + super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.isLoadingTradesChart = false + this.ltcTrades = [] + this.ltcPrice = [] + } + + render() { + return html` + +
+ ${translate("login.loading")} +
+
+ +
+
+
+
+ ` + } + + async firstUpdated() { + this.changeTheme() + this.changeLanguage() + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme) + }) + } + + async loadTradesChart() { + this.isLoadingTradesChart = true + this.shadowRoot.getElementById('loadChartDialog').open() + await this.getLtcTrades() + this.isLoadingTradesChart = false + this.shadowRoot.getElementById('loadChartDialog').close() + this.enableLtcStockPriceChart() + } + + async getLtcTrades() { + let currentLtcTimestamp = Date.now() + const monthBackLtc = currentLtcTimestamp - 31556952000 + await parentEpml.request("apiCall", { url: `/crosschain/trades?foreignBlockchain=LITECOIN&minimumTimestamp=${monthBackLtc}&limit=0&reverse=false` }).then((res) => { + this.ltcTrades = res + }) + this.ltcPrice = this.ltcTrades.map(item => { + const ltcSellPrice = this.round(parseFloat(item.foreignAmount) / parseFloat(item.qortAmount)) + return [item.tradeTimestamp, parseFloat(ltcSellPrice)] + }).filter(item => !!item) + } + + enableLtcStockPriceChart() { + const ltcStockPriceData = this.ltcPrice + const header = 'QORT / LTC ' + get("tradepage.tchange49") + Highcharts.stockChart(this.shadowRoot.querySelector('#ltcStockPriceContainer'), { + accessibility: { + enabled: false + }, + credits: { + enabled: false + }, + rangeSelector: { + selected: 1, + labelStyle: {color: 'var(--black)'}, + inputStyle: {color: '#03a9f4'} + }, + chart: { + backgroundColor: 'transparent' + }, + title: { + text: header, + style: {color: 'var(--black)'} + }, + xAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + yAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + series: [{ + name: 'QORT / LTC', + data: ltcStockPriceData, + tooltip: { + valueDecimals: 8 + } + }] + }) + } + + async open() { + await this.loadTradesChart() + this.shadowRoot.getElementById('ltcChartDialog').open() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme); + } + + changeLanguage() { + const checkLanguage = localStorage.getItem('qortalLanguage') + + if (checkLanguage === null || checkLanguage.length === 0) { + localStorage.setItem('qortalLanguage', 'us') + use('us') + } else { + use(checkLanguage) + } + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } +} + +window.customElements.define('ltc-charts', LtcCharts) + +const chartsltc = document.createElement('ltc-charts') +ltcChartDialog = document.body.appendChild(chartsltc) + +export default ltcChartDialog \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-portal/charts/rvn-charts.js b/qortal-ui-plugins/plugins/core/trade-portal/charts/rvn-charts.js new file mode 100644 index 00000000..1110bdfc --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-portal/charts/rvn-charts.js @@ -0,0 +1,214 @@ +import { LitElement, html, css } from 'lit' +import { render } from 'lit/html.js' +import { Epml } from '../../../../epml.js' +import { use, get, translate, translateUnsafeHTML, registerTranslateConfig } from 'lit-translate' + +registerTranslateConfig({ + loader: lang => fetch(`/language/${lang}.json`).then(res => res.json()) +}) + +import '@polymer/paper-dialog/paper-dialog.js' +import * as Highcharts from 'highcharts' +import Exporting from 'highcharts/modules/exporting' +Exporting(Highcharts) +import StockChart from 'highcharts/modules/stock' +StockChart(Highcharts) +import 'highcharts/highcharts-more.js' +import 'highcharts/modules/accessibility.js' +import 'highcharts/modules/boost.js' +import 'highcharts/modules/data.js' +import 'highcharts/modules/export-data.js' +import 'highcharts/modules/offline-exporting.js' + +let rvnChartDialog + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +class RvnCharts extends LitElement { + static get properties() { + return { + isLoadingTradesChart: { type: Boolean }, + rvnTrades: { type: Array }, + rvnPrice: { type: Array } + } + } + + static get styles() { + return css` + .loadingContainer { + height: 100%; + width: 100%; + } + + .trades-chart { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 25px; + padding: 15px; + } + + .chart-container { + margin: auto; + color: var(--black); + text-align: center; + padding: 15px; + height: 30vh; + width: 80vw; + } + + .chart-info-wrapper { + background: transparent; + height: 38vh; + width: 83vw; + overflow: auto; + } + + .chart-loading-wrapper { + color: var(--black); + background: var(--white); + border: 1px solid var(--black); + border-radius: 15px; + } + ` + } + + constructor() { + super() + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.isLoadingTradesChart = false + this.rvnTrades = [] + this.rvnPrice = [] + } + + render() { + return html` + +
+ ${translate("login.loading")} +
+
+ +
+
+
+
+ ` + } + + async firstUpdated() { + this.changeTheme() + this.changeLanguage() + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme) + }) + } + + async loadTradesChart() { + this.isLoadingTradesChart = true + this.shadowRoot.getElementById('loadChartDialog').open() + await this.getRvnTrades() + this.isLoadingTradesChart = false + this.shadowRoot.getElementById('loadChartDialog').close() + this.enableRvnStockPriceChart() + } + + async getRvnTrades() { + let currentRvnTimestamp = Date.now() + const monthBackRvn = currentRvnTimestamp - 31556952000 + await parentEpml.request("apiCall", { url: `/crosschain/trades?foreignBlockchain=RAVENCOIN&minimumTimestamp=${monthBackRvn}&limit=0&reverse=false` }).then((res) => { + this.rvnTrades = res + }) + this.rvnPrice = this.rvnTrades.map(item => { + const rvnSellPrice = this.round(parseFloat(item.foreignAmount) / parseFloat(item.qortAmount)) + return [item.tradeTimestamp, parseFloat(rvnSellPrice)] + }).filter(item => !!item) + } + + enableRvnStockPriceChart() { + const rvnStockPriceData = this.rvnPrice + const header = 'QORT / RVN ' + get("tradepage.tchange49") + Highcharts.stockChart(this.shadowRoot.querySelector('#rvnStockPriceContainer'), { + accessibility: { + enabled: false + }, + credits: { + enabled: false + }, + rangeSelector: { + selected: 1, + labelStyle: {color: 'var(--black)'}, + inputStyle: {color: '#03a9f4'} + }, + chart: { + backgroundColor: 'transparent' + }, + title: { + text: header, + style: {color: 'var(--black)'} + }, + xAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + yAxis: { + labels: { + style: { + color: '#03a9f4' + } + } + }, + series: [{ + name: 'QORT / RVN', + data: rvnStockPriceData, + tooltip: { + valueDecimals: 8 + } + }] + }) + } + + async open() { + await this.loadTradesChart() + this.shadowRoot.getElementById('rvnChartDialog').open() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + this.theme = (checkTheme === 'dark') ? 'dark' : 'light' + document.querySelector('html').setAttribute('theme', this.theme); + } + + changeLanguage() { + const checkLanguage = localStorage.getItem('qortalLanguage') + + if (checkLanguage === null || checkLanguage.length === 0) { + localStorage.setItem('qortalLanguage', 'us') + use('us') + } else { + use(checkLanguage) + } + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } +} + +window.customElements.define('rvn-charts', RvnCharts) + +const chartsrvn = document.createElement('rvn-charts') +rvnChartDialog = document.body.appendChild(chartsrvn) + +export default rvnChartDialog \ No newline at end of file From df9dcd20f942ed106dc5b2669f7648cd12fbf76c Mon Sep 17 00:00:00 2001 From: AlphaX-Projects <77661270+AlphaX-Projects@users.noreply.github.com> Date: Mon, 9 Jan 2023 15:09:00 +0100 Subject: [PATCH 14/14] Added charts --- qortal-ui-core/language/de.json | 3 +- qortal-ui-core/language/es.json | 3 +- qortal-ui-core/language/fr.json | 3 +- qortal-ui-core/language/hindi.json | 3 +- qortal-ui-core/language/hr.json | 3 +- qortal-ui-core/language/hu.json | 3 +- qortal-ui-core/language/it.json | 3 +- qortal-ui-core/language/ko.json | 3 +- qortal-ui-core/language/no.json | 3 +- qortal-ui-core/language/pl.json | 3 +- qortal-ui-core/language/pt.json | 3 +- qortal-ui-core/language/ro.json | 3 +- qortal-ui-core/language/rs.json | 3 +- qortal-ui-core/language/ru.json | 3 +- qortal-ui-core/language/us.json | 3 +- qortal-ui-core/language/zhc.json | 3 +- qortal-ui-core/language/zht.json | 3 +- .../core/trade-portal/trade-portal.src.js | 48 ++++++++++++++++--- 18 files changed, 75 insertions(+), 24 deletions(-) diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index cedb6452..518cab5c 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -333,7 +333,8 @@ "tchange45":"AUTO KAUFEN MIT", "tchange46":"AUTOKAUF", "tchange47":"Verkaufe für diesen Preis", - "tchange48":"NICHT GENUG" + "tchange48":"NICHT GENUG", + "tchange49":"Preisdiagramm" }, "rewardsharepage":{ "rchange1":"Belohnungsanteile", diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index adf6d85c..eab3f6d0 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -333,7 +333,8 @@ "tchange45":"AUTO COMPRAR CON", "tchange46":"COMPRA AUTOMÁTICA", "tchange47":"Vender por este precio", - "tchange48":"NO ES SUFICIENTE" + "tchange48":"NO ES SUFICIENTE", + "tchange49":"Gráfico de precios" }, "rewardsharepage":{ "rchange1":"Rewardshares", diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index 86b02d49..f22be6c5 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -333,7 +333,8 @@ "tchange45":"ACHAT AUTO AVEC", "tchange46":"ACHAT AUTOMATIQUE", "tchange47":"Vendre à ce prix", - "tchange48":"PAS ASSEZ" + "tchange48":"PAS ASSEZ", + "tchange49":"Tableau des prix" }, "rewardsharepage":{ "rchange1":"Récompenses", diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index e82ab1b0..617a2b64 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -334,7 +334,8 @@ "tchange45":"ऑटो के साथ खरीदें", "tchange46":"ऑटो खरीदें", "tchange47":"इस कीमत पर बेचें", - "tchange48":"पर्याप्त नहीं" + "tchange48":"पर्याप्त नहीं", + "tchange49":"मूल्य चार्ट" }, "rewardsharepage":{ "rchange1":"रिवॉर्डशेयर", diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index 40689c8f..3b369348 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -333,7 +333,8 @@ "tchange45":"AUTO KUPITE SA", "tchange46":"AUTO OTKUP", "tchange47":"Prodaj za ovu cijenu", - "tchange48":"NEDOVOLJNO" + "tchange48":"NEDOVOLJNO", + "tchange49":"Grafikon cijena" }, "rewardsharepage":{ "rchange1":"Nagradni udio (Rewardshares)", diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index 7b57ad70..c30d020d 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -333,7 +333,8 @@ "tchange45":"AUTOMATIKUS VÁSÁRLÁS", "tchange46":"AUTOMATIKUS VÁSÁRLÁS", "tchange47":"Eladni ezen az áron", - "tchange48":"NEM ELÉG" + "tchange48":"NEM ELÉG", + "tchange49":"Árdiagram" }, "rewardsharepage":{ "rchange1":"Jutalommegosztások", diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index e669f6de..6efe10d4 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -333,7 +333,8 @@ "tchange45":"ACQUISTA AUTO CON", "tchange46":"ACQUISTO AUTO", "tchange47":"Vendi a questo prezzo", - "tchange48":"NON ABBASTANZA" + "tchange48":"NON ABBASTANZA", + "tchange49":"Tabella dei prezzi" }, "rewardsharepage":{ "rchange1":"Quote di ricompensa", diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index 4eed1cbc..2dc9b4d6 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -333,7 +333,8 @@ "tchange45":"자동 구매", "tchange46":"자동 구매", "tchange47":"이 가격에 팔아요", - "tchange48":"부족한" + "tchange48":"부족한", + "tchange49":"가격 차트" }, "rewardsharepage":{ "rchange1":"보상 공유", diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index 7795fa96..85054308 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -333,7 +333,8 @@ "tchange45":"AUTOKJØP MED", "tchange46":"AUTOKJØP", "tchange47":"Selges for denne prisen", - "tchange48":"IKKE NOK" + "tchange48":"IKKE NOK", + "tchange49":"Prisdiagram" }, "rewardsharepage":{ "rchange1":"Belønningsdel", diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index 5fbd3224..0b3ef019 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -333,7 +333,8 @@ "tchange45":"AUTO KUP Z", "tchange46":"AUTO KUP", "tchange47":"Sprzedaj za tę cenę", - "tchange48":"NIEWYSTARCZAJĄCO" + "tchange48":"NIEWYSTARCZAJĄCO", + "tchange49":"Tabela cen" }, "rewardsharepage":{ "rchange1":"Podział nagród", diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index c731226f..41e55557 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -333,7 +333,8 @@ "tchange45":"COMPRA AUTOMÁTICA COM", "tchange46":"COMPRA AUTOMÁTICA", "tchange47":"Vendo por este preço", - "tchange48":"INSUFICIENTE" + "tchange48":"INSUFICIENTE", + "tchange49":"Tabela de preços" }, "rewardsharepage":{ "rchange1":"Ações de recompensa", diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index 60bce846..29efe5b0 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -333,7 +333,8 @@ "tchange45":"CUMPARA AUTOMATA CU", "tchange46":"CUMPARARE AUTOMATA", "tchange47":"Vinde la acest pret", - "tchange48":"INSUFICIENT" + "tchange48":"INSUFICIENT", + "tchange49":"Diagrama prețurilor" }, "rewardsharepage":{ "rchange1":"Cote de recompensa", diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index c7ad6d88..ccd07326 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -333,7 +333,8 @@ "tchange45":"AUTO KUPI SA", "tchange46":"AUTO BUI", "tchange47":"Prodaj za ovu cenu", - "tchange48":"NEDOVOLJNO" + "tchange48":"NEDOVOLJNO", + "tchange49":"Grafik cena" }, "rewardsharepage":{ "rchange1":"Udeo nagrade", diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index abad841f..b7539374 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -333,7 +333,8 @@ "tchange45":"АВТО КУПИТЬ С", "tchange46":"АВТО КУПИТЬ", "tchange47":"Продать по этой цене", - "tchange48":"НЕДОСТАТОЧНО" + "tchange48":"НЕДОСТАТОЧНО", + "tchange49":"График цен" }, "rewardsharepage":{ "rchange1":"Вознаграждения", diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 580d0549..e0444893 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -333,7 +333,8 @@ "tchange45":"AUTO BUY WITH", "tchange46":"AUTO BUY", "tchange47":"Sell for this price", - "tchange48":"NOT ENOUGH" + "tchange48":"NOT ENOUGH", + "tchange49":"Price Chart" }, "rewardsharepage":{ "rchange1":"Rewardshares", diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index b41114a8..b2b1a6b4 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -333,7 +333,8 @@ "tchange45":"自动购买", "tchange46":"自动购买", "tchange47":"以这个价格出售", - "tchange48":"不够" + "tchange48":"不够", + "tchange49":"价格图表" }, "rewardsharepage":{ "rchange1":"铸币密钥", diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index 52fa5400..2c5c86f3 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -333,7 +333,8 @@ "tchange45":"自動購買", "tchange46":"自動購買", "tchange47":"以這個價格出售", - "tchange48":"不夠" + "tchange48":"不夠", + "tchange49":"價格圖表" }, "rewardsharepage":{ "rchange1":"鑄幣密鑰", diff --git a/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js b/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js index 47910961..bcfd5130 100644 --- a/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js +++ b/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js @@ -21,6 +21,12 @@ import '@polymer/paper-icon-button/paper-icon-button.js' import '@polymer/paper-spinner/paper-spinner-lite.js' import '@vaadin/grid' import '@vaadin/grid/vaadin-grid-sorter' +import chartsbtc from './charts/btc-charts.js' +import chartsltc from './charts/ltc-charts.js' +import chartsdoge from './charts/doge-charts.js' +import chartsdgb from './charts/dgb-charts.js' +import chartsrvn from './charts/rvn-charts.js' +import chartsarrr from './charts/arrr-charts.js' const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) @@ -74,7 +80,7 @@ class TradePortal extends LitElement { displayTradeAddress: { type: String }, displayTradeLevel: { type: String }, displayTradeBalance: { type: String }, - qortRatio: {type: Number} + qortRatio: {type: Number} } } @@ -494,7 +500,7 @@ class TradePortal extends LitElement { .rvn.coinName:before { background-image: url('/img/qortrvn.png'); } - .arrr.coinName:before { + .arrr.coinName:before { background-image: url('/img/qortarrr.png'); } .coinName { @@ -655,7 +661,7 @@ class TradePortal extends LitElement { tradeFee: "~0.0005" } - let ravencoin = { + let ravencoin = { name: "RAVENCOIN", balance: "0", coinCode: "RVN", @@ -721,7 +727,7 @@ class TradePortal extends LitElement { handleStuckTradesConnectedWorker: null }) - workers.set("RAVENCOIN", { + workers.set("RAVENCOIN", { tradesConnectedWorker: null, handleStuckTradesConnectedWorker: null }) @@ -1162,6 +1168,9 @@ class TradePortal extends LitElement { QORT / RVN QORT / ARRR +
+ ${this.chartShowCoin()} +
@@ -1412,6 +1421,31 @@ class TradePortal extends LitElement { return html`NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode}` } + chartShowCoin() { + switch(this.listedCoins.get(this.selectedCoin).coinCode) { + case "BTC": + return html` chartsbtc.open()}>` + break + case "LTC": + return html` chartsltc.open()}>` + break + case "DOGE": + return html` chartsdoge.open()}>` + break + case "DGB": + return html` chartsdgb.open()}>` + break + case "RVN": + return html` chartsrvn.open()}>` + break + case "ARRR": + return html` chartsarrr.open()}>` + break + default: + break + } + } + exchangeRateQort() { switch(this.listedCoins.get(this.selectedCoin).coinCode) { case "BTC": @@ -1504,8 +1538,8 @@ class TradePortal extends LitElement { case 'DIGIBYTE': _url = `/crosschain/dgb/walletbalance?apiKey=${this.getApiKey()}` _body = window.parent.reduxStore.getState().app.selectedAddress.dgbWallet.derivedMasterPublicKey - break - case 'RAVENCOIN': + break + case 'RAVENCOIN': _url = `/crosschain/rvn/walletbalance?apiKey=${this.getApiKey()}` _body = window.parent.reduxStore.getState().app.selectedAddress.rvnWallet.derivedMasterPublicKey break @@ -2945,4 +2979,4 @@ class TradePortal extends LitElement { } } -window.customElements.define('trade-portal', TradePortal) +window.customElements.define('trade-portal', TradePortal) \ No newline at end of file