Merge remote-tracking branch 'upstream/master' into feature/implement-logic-edit-reply-messages

This commit is contained in:
Phillip Lang Martinez 2023-01-09 17:57:27 -05:00
commit 50cd17caba
42 changed files with 4001 additions and 227 deletions

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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 !"
}
}

View File

@ -129,7 +129,11 @@
"snack2":"यूआई नोड से जुड़ा",
"snack3":"कस्टम नोड को सफलतापूर्वक जोड़ा और सहेजा गया",
"snack4":"नोड्स सफलतापूर्वक सहेजे गए",
"snack5":"नोड्स सफलतापूर्वक आयात किए गए"
"snack5":"नोड्स सफलतापूर्वक आयात किए गए",
"exp1":"निजी मास्टर कुंजी निर्यात करें",
"exp2":"निर्यात मास्टर कुंजी",
"exp3":"निर्यात",
"exp4":"निजी मास्टर कुंजी का बैकअप लेने के लिए कृपया एक वॉलेट चुनें।"
},
"appinfo":{
"blockheight":"ब्लॉक ऊँचाई",
@ -156,7 +160,8 @@
"continue":"जारी रखें",
"save":"सहेजें",
"balance":"संतुलन",
"balances":"आपका वॉलेट बैलेंस"
"balances":"आपका वॉलेट बैलेंस",
"update":"अपडेट वॉलेट बैलेंस"
},
"startminting":{
"smchange1":"खनन खाते नहीं लाए जा सकते",
@ -329,7 +334,8 @@
"tchange45":"ऑटो के साथ खरीदें",
"tchange46":"ऑटो खरीदें",
"tchange47":"इस कीमत पर बेचें",
"tchange48":"पर्याप्त नहीं"
"tchange48":"पर्याप्त नहीं",
"tchange49":"मूल्य चार्ट"
},
"rewardsharepage":{
"rchange1":"रिवॉर्डशेयर",
@ -607,7 +613,11 @@
"gchange51":"शामिल हों",
"gchange52":"व्यवस्थापक",
"gchange53":"सदस्य",
"gchange54":"सदस्यों"
"gchange54":"सदस्यों",
"gchange55":"निजी समूह खोजें",
"gchange56":"खोजने के लिए समूह का नाम",
"gchange57":"निजी समूह का नाम नहीं मिला",
"gchange58":"ध्यान दें कि समूह का नाम सटीक मेल खाना चाहिए।"
},
"puzzlepage":{
"pchange1":"पहेलि",
@ -794,7 +804,16 @@
"mg48":"क्या आप वाकई इस सदस्य के लिए आमंत्रण रद्द करना चाहते हैं?",
"mg49":"पुष्टि करें दबाने पर, रद्द आमंत्रण अनुरोध भेजा जाएगा!",
"mg50":"जल्द ही आ रहा है ...",
"mg51": "न्यूनतम 3 वर्ण / अधिकतम 32 वर्ण",
"mg52": "अधिकतम 128 वर्ण"
"mg51":"न्यूनतम 3 वर्ण / अधिकतम 32 वर्ण",
"mg52":"अधिकतम 128 वर्ण",
"mg53":"आपका खुला शामिल होने का अनुरोध",
"mg54":"नो ओपन जॉइन रिक्वेस्ट",
"mg55":"क्या आप निश्चित रूप से इस सदस्य के शामिल होने के अनुरोध को स्वीकार करना चाहते हैं?",
"mg56":"पुष्टि करें दबाने पर, स्वीकार करने का अनुरोध भेजा जाएगा!",
"mg57":"जुड़ने का अनुरोध सफलतापूर्वक स्वीकार किया गया",
"mg58":"कुछ गलत हो गया",
"mg59":"रद्द करें शामिल होने का अनुरोध सफलतापूर्वक स्वीकार किया गया",
"mg60":"क्या आप निश्चित रूप से इस सदस्य के शामिल होने के अनुरोध को रद्द करना चाहते हैं?",
"mg61":"पुष्टि करें दबाने पर, रद्द करने का अनुरोध भेजा जाएगा!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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)",
@ -606,7 +612,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",
@ -793,7 +803,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!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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!"
}
}

View File

@ -128,7 +128,11 @@
"snack2":"노드에 연결된 UI",
"snack3":"사용자 정의 노드를 성공적으로 추가하고 저장했습니다.",
"snack4":"노드가 다음으로 성공적으로 저장되었습니다",
"snack5":"노드를 성공적으로 가져왔습니다."
"snack5":"노드를 성공적으로 가져왔습니다.",
"exp1":"개인 마스터 키 내보내기",
"exp2":"마스터 키 내보내기",
"exp3":"내보내기",
"exp4":"개인 마스터 키를 백업할 지갑을 선택하세요."
},
"appinfo":{
"blockheight":"블록 높이",
@ -155,7 +159,8 @@
"continue":"계속하다",
"save":"저장",
"balance":"균형",
"balances":"지갑 잔액"
"balances":"지갑 잔액",
"update":"월렛 잔액 업데이트"
},
"startminting":{
"smchange1":"발행 계정을 가져올 수 없습니다",
@ -328,7 +333,8 @@
"tchange45":"자동 구매",
"tchange46":"자동 구매",
"tchange47":"이 가격에 팔아요",
"tchange48":"부족한"
"tchange48":"부족한",
"tchange49":"가격 차트"
},
"rewardsharepage":{
"rchange1":"보상 공유",
@ -606,7 +612,11 @@
"gchange51":"가입",
"gchange52":"관리자",
"gchange53":"회원",
"gchange54":"회원들"
"gchange54":"회원들",
"gchange55":"비공개 그룹 검색",
"gchange56":"검색할 그룹 이름",
"gchange57":"비공개 그룹 이름을 찾을 수 없음",
"gchange58":"그룹 이름이 정확히 일치해야 합니다."
},
"puzzlepage":{
"pchange1":"퍼즐",
@ -794,6 +804,15 @@
"mg49":"확인을 누르면 초대 취소 요청이 전송됩니다!",
"mg50":"출시 예정...",
"mg51":"최소 3자 / 최대 32자",
"mg52":"최대 128자"
"mg52":"최대 128자",
"mg53":"귀하의 오픈 조인 요청",
"mg54":"오픈 조인 요청 없음",
"mg55":"이 회원의 가입 요청을 수락하시겠습니까?",
"mg56":"확인을 누르면 가입 수락 요청이 전송됩니다!",
"mg57":"가입 요청이 성공적으로 수락됨",
"mg58":"뭔가 잘못되었습니다",
"mg59":"가입 요청 취소 성공",
"mg60":"이 회원의 가입 요청을 취소하시겠습니까?",
"mg61":"확인을 누르면 가입 취소 요청이 전송됩니다!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -793,7 +803,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!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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ă!"
}
}

View File

@ -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",
@ -155,7 +159,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",
@ -328,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",
@ -606,7 +612,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",
@ -794,6 +804,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!"
}
}

View File

@ -128,7 +128,11 @@
"snack2":"Пользовательский интерфейс, подключенный к узлу",
"snack3":"Пользовательский узел успешно добавлен и сохранен",
"snack4":"Узлы успешно сохранены как",
"snack5":"Узлы успешно импортированы"
"snack5":"Узлы успешно импортированы",
"exp1":"Экспорт закрытого мастер-ключа",
"exp2":"Экспорт мастер-ключа",
"exp3":"Экспорт",
"exp4":"Пожалуйста, выберите кошелек для резервного копирования приватного главного ключа."
},
"appinfo":{
"blockheight":"Высота блока",
@ -155,7 +159,8 @@
"continue":"Продолжить",
"save":"Сохранить",
"balance":"кредит",
"balances":"БАЛАНС ВАШЕГО КОШЕЛЬКА"
"balances":"БАЛАНС ВАШЕГО КОШЕЛЬКА",
"update":"ОБНОВИТЬ БАЛАНС КОШЕЛЬКА"
},
"startminting":{
"smchange1":"Не удается получить учетные записи минтинга",
@ -328,7 +333,8 @@
"tchange45":"АВТО КУПИТЬ С",
"tchange46":"АВТО КУПИТЬ",
"tchange47":"Продать по этой цене",
"tchange48":"НЕДОСТАТОЧНО"
"tchange48":"НЕДОСТАТОЧНО",
"tchange49":"График цен"
},
"rewardsharepage":{
"rchange1":"Вознаграждения",
@ -606,7 +612,11 @@
"gchange51":"Присоединиться",
"gchange52":"Администратор",
"gchange53":"Член",
"gchange54":"Члены"
"gchange54":"Члены",
"gchange55":"Поиск в закрытой группе",
"gchange56":"Имя группы для поиска",
"gchange57":"Имя частной группы не найдено",
"gchange58":"Обратите внимание, что название группы должно точно совпадать."
},
"puzzlepage":{
"pchange1":"Головоломки",
@ -794,6 +804,15 @@
"mg49":"При нажатии подтверждения будет отправлен запрос на отмену приглашения!",
"mg50":"Скоро...",
"mg51":"Минимум 3 символа / максимум 32 символа",
"mg52":"Максимум 128 символов"
"mg52":"Максимум 128 символов",
"mg53":"Ваши открытые запросы на вступление",
"mg54":"Открытых запросов на присоединение нет",
"mg55":"Вы уверены, что принимаете запрос на вступление от этого участника?",
"mg56":"При нажатии подтверждения будет отправлен запрос на присоединение!",
"mg57":"Запрос на присоединение успешно принят",
"mg58":"ЧТО-ТО ПОШЛО НЕ ТАК",
"mg59":"Запрос на отмену присоединения успешно принят",
"mg60":"Вы уверены, что отмените запрос на вступление от этого участника?",
"mg61":"При нажатии кнопки подтверждения будет отправлен запрос на отмену присоединения!"
}
}

View File

@ -93,42 +93,46 @@
"selectfile": "Select file",
"dragfile": "Drag and drop backup here"
},
"settings": {
"generalinfo": "General Account Info",
"address": "Address",
"publickey": "Public Key",
"settings": "Settings",
"account": "Account",
"security": "Security",
"qr_login_menu_item": "QR Login",
"qr_login_description_1": "Scan this code to unlock your wallet on other device using the same password which you logged in with.",
"qr_login_description_2": "Choose a password which you will use to unlock your wallet on other device after scanning the QR code.",
"qr_login_button_1": "Show login QR code",
"qr_login_button_2": "Generate login QR code",
"notifications": "Notifications",
"accountsecurity": "Account Security",
"password": "Password",
"download": "Download Backup File",
"choose": "Please choose a password to encrypt your backup with. (This can be the same as the one you logged in with, or different)",
"block": "Block Notifications (Coming Soon...)",
"playsound": "Play Sound",
"shownotifications": "Show Notifications",
"nodeurl": "Node Url",
"nodehint": "Select a node from the default list of nodes above or add a custom node to the list above by clicking on the button below",
"addcustomnode": "Add Custom Node",
"addandsave": "Add And Save",
"protocol": "Protocol",
"domain": "Domain",
"port": "Port",
"import": "Import Nodes",
"export": "Export Nodes",
"deletecustomnode": "Remove All Custom Nodes",
"warning": "Your existing nodes will be deleted and from backup new created.",
"snack1": "Successfully deleted and added standard nodes",
"snack2": "UI conected to node",
"snack3": "Successfully added and saved custom node",
"snack4": "Nodes successfully saved as",
"snack5": "Nodes successfully imported"
"settings":{
"generalinfo":"General Account Info",
"address":"Address",
"publickey":"Public Key",
"settings":"Settings",
"account":"Account",
"security":"Security",
"qr_login_menu_item":"QR Login",
"qr_login_description_1":"Scan this code to unlock your wallet on other device using the same password which you logged in with.",
"qr_login_description_2":"Choose a password which you will use to unlock your wallet on other device after scanning the QR code.",
"qr_login_button_1":"Show login QR code",
"qr_login_button_2":"Generate login QR code",
"notifications":"Notifications",
"accountsecurity":"Account Security",
"password":"Password",
"download":"Download Backup File",
"choose":"Please choose a password to encrypt your backup with. (This can be the same as the one you logged in with, or different)",
"block":"Block Notifications (Coming Soon...)",
"playsound":"Play Sound",
"shownotifications":"Show Notifications",
"nodeurl":"Node Url",
"nodehint":"Select a node from the default list of nodes above or add a custom node to the list above by clicking on the button below",
"addcustomnode":"Add Custom Node",
"addandsave":"Add And Save",
"protocol":"Protocol",
"domain":"Domain",
"port":"Port",
"import":"Import Nodes",
"export":"Export Nodes",
"deletecustomnode":"Remove All Custom Nodes",
"warning":"Your existing nodes will be deleted and from backup new created.",
"snack1":"Successfully deleted and added standard nodes",
"snack2":"UI conected to node",
"snack3":"Successfully added and saved custom node",
"snack4":"Nodes successfully saved as",
"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",
@ -155,7 +159,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",
@ -328,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",
@ -575,61 +581,65 @@
"bcchange16": "Choose Recipient or Search for One Below",
"bcchange17": "FORWARDED"
},
"grouppage": {
"gchange1": "Qortal Groups",
"gchange2": "Create Group",
"gchange3": "Your Joined Groups",
"gchange4": "Group Name",
"gchange5": "Description",
"gchange6": "Role",
"gchange7": "Action",
"gchange8": "Not a member of any group!",
"gchange9": "Public Groups",
"gchange10": "Owner",
"gchange11": "No Open Public Groups available!",
"gchange12": "Create a New Group",
"gchange13": "Group Type",
"gchange14": "This Field is Required",
"gchange15": "Select an option",
"gchange16": "Public",
"gchange17": "Private",
"gchange18": "Group Approval Threshold (number / percentage of Admins that must approve a transaction):",
"gchange19": "NONE",
"gchange20": "ONE",
"gchange21": "Minimum Block delay for Group Transaction Approvals:",
"gchange22": "minutes",
"gchange23": "hour",
"gchange24": "hours",
"gchange25": "day",
"gchange26": "days",
"gchange27": "Maximum Block delay for Group Transaction Approvals:",
"gchange28": "Creating Group",
"gchange29": "Create Group",
"gchange30": "Join Group Request",
"gchange31": "Date Created",
"gchange32": "Date Updated",
"gchange33": "Joining",
"gchange34": "Join Group",
"gchange35": "Leave Group Request",
"gchange36": "Leaving",
"gchange37": "Leave Group",
"gchange38": "Manage Group Owner:",
"gchange39": "Manage Group Admin:",
"gchange40": "Manage Group",
"gchange41": "Group Creation Successful!",
"gchange42": "Invalid Group Name",
"gchange43": "Invalid Group Description",
"gchange44": "Select a Group Typ",
"gchange45": "Select a Group Approval Threshold",
"gchange46": "Select a Minimum Block delay for Group Transaction Approvals",
"gchange47": "Select a Maximum Block delay for Group Transaction Approvals",
"gchange48": "Join Group Request Sent Successfully!",
"gchange49": "Leave Group Request Sent Successfully!",
"gchange50": "Leave",
"gchange51": "Join",
"gchange52": "Admin",
"gchange53": "Member",
"gchange54": "Members"
"grouppage":{
"gchange1":"Qortal Groups",
"gchange2":"Create Group",
"gchange3":"Your Joined Groups",
"gchange4":"Group Name",
"gchange5":"Description",
"gchange6":"Role",
"gchange7":"Action",
"gchange8":"Not a member of any group!",
"gchange9":"Public Groups",
"gchange10":"Owner",
"gchange11":"No Open Public Groups available!",
"gchange12":"Create a New Group",
"gchange13":"Group Type",
"gchange14":"This Field is Required",
"gchange15":"Select an option",
"gchange16":"Public",
"gchange17":"Private",
"gchange18":"Group Approval Threshold (number / percentage of Admins that must approve a transaction):",
"gchange19":"NONE",
"gchange20":"ONE",
"gchange21":"Minimum Block delay for Group Transaction Approvals:",
"gchange22":"minutes",
"gchange23":"hour",
"gchange24":"hours",
"gchange25":"day",
"gchange26":"days",
"gchange27":"Maximum Block delay for Group Transaction Approvals:",
"gchange28":"Creating Group",
"gchange29":"Create Group",
"gchange30":"Join Group Request",
"gchange31":"Date Created",
"gchange32":"Date Updated",
"gchange33":"Joining",
"gchange34":"Join Group",
"gchange35":"Leave Group Request",
"gchange36":"Leaving",
"gchange37":"Leave Group",
"gchange38":"Manage Group Owner:",
"gchange39":"Manage Group Admin:",
"gchange40":"Manage Group",
"gchange41":"Group Creation Successful!",
"gchange42":"Invalid Group Name",
"gchange43":"Invalid Group Description",
"gchange44":"Select a Group Typ",
"gchange45":"Select a Group Approval Threshold",
"gchange46":"Select a Minimum Block delay for Group Transaction Approvals",
"gchange47":"Select a Maximum Block delay for Group Transaction Approvals",
"gchange48":"Join Group Request Sent Successfully!",
"gchange49":"Leave Group Request Sent Successfully!",
"gchange50":"Leave",
"gchange51":"Join",
"gchange52":"Admin",
"gchange53":"Member",
"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",
@ -817,6 +827,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!"
}
}

View File

@ -128,7 +128,11 @@
"snack2":"连接到节点的 UI",
"snack3":"成功添加并保存自定义节点",
"snack4":"节点成功保存为",
"snack5":"节点成功导入"
"snack5":"节点成功导入",
"exp1":"导出主密钥",
"exp2":"导出主密钥",
"exp3":"导出",
"exp4":"请选择一个钱包来备份私钥。"
},
"appinfo":{
"blockheight":"区块高度",
@ -155,7 +159,8 @@
"continue":"继续",
"save":"保存",
"balance":"信用",
"balances":"您的钱包余额"
"balances":"您的钱包余额",
"update":"更新钱包余额"
},
"startminting":{
"smchange1":"无法获取铸币帐户",
@ -328,7 +333,8 @@
"tchange45":"自动购买",
"tchange46":"自动购买",
"tchange47":"以这个价格出售",
"tchange48":"不够"
"tchange48":"不够",
"tchange49":"价格图表"
},
"rewardsharepage":{
"rchange1":"铸币密钥",
@ -606,7 +612,11 @@
"gchange51":"加入",
"gchange52":"管理员",
"gchange53":"成员",
"gchange54":"成员数量"
"gchange54":"成员数量",
"gchange55":"搜索私人群组",
"gchange56":"要搜索的组名",
"gchange57":"未找到私人组名",
"gchange58":"注意组名必须完全匹配。"
},
"puzzlepage":{
"pchange1":"益智游戏",
@ -794,6 +804,15 @@
"mg49":"按下确认后,将发送取消邀请请求!",
"mg50":"即将推出……",
"mg51":"最少 3 个字符 / 最多 32 个字符",
"mg52":"最多 128 个字符"
"mg52":"最多 128 个字符",
"mg53":"您的公开加入请求",
"mg54":"没有开放的加入请求",
"mg55":"您确定接受该会员的加入请求吗?",
"mg56":"按下确认后,将发送接受加入请求!",
"mg57":"成功接受加入请求",
"mg58":"出了点问题",
"mg59":"取消加入请求已成功接受",
"mg60":"您确定要取消该会员的加入请求吗?",
"mg61":"按下确认后,将发送取消加入请求!"
}
}

View File

@ -128,7 +128,11 @@
"snack2":"連接到節點的 UI",
"snack3":"成功添加並保存自定義節點",
"snack4":"節點成功保存為",
"snack5":"節點成功導入"
"snack5":"節點成功導入",
"exp1":"導出主密鑰",
"exp2":"導出主密鑰",
"exp3":"導出",
"exp4":"請選擇一個錢包來備份私鑰。"
},
"appinfo":{
"blockheight":"區塊高度",
@ -155,7 +159,8 @@
"continue":"繼續",
"save":"保存",
"balance":"信用",
"balances":"您的錢包餘額"
"balances":"您的錢包餘額",
"update":"更新錢包餘額"
},
"startminting":{
"smchange1":"無法獲取鑄幣帳戶",
@ -328,7 +333,8 @@
"tchange45":"自動購買",
"tchange46":"自動購買",
"tchange47":"以這個價格出售",
"tchange48":"不夠"
"tchange48":"不夠",
"tchange49":"價格圖表"
},
"rewardsharepage":{
"rchange1":"鑄幣密鑰",
@ -606,7 +612,11 @@
"gchange51":"加入",
"gchange52":"管理員",
"gchange53":"成員",
"gchange54":"成員數量"
"gchange54":"成員數量",
"gchange55":"搜索私人群組",
"gchange56":"要搜索的組名",
"gchange57":"未找到私人組名",
"gchange58":"注意組名必須完全匹配。"
},
"puzzlepage":{
"pchange1":"益智游戲",
@ -794,6 +804,15 @@
"mg49":"按下確認後,將發送取消邀請請求!",
"mg50":"即將推出……",
"mg51":"最少 3 個字符 / 最多 32 個字符",
"mg52":"最多 128 個字符"
"mg52":"最多 128 個字符",
"mg53":"您的公開加入請求",
"mg54":"沒有開放的加入請求",
"mg55":"您確定接受該會員的加入請求嗎?",
"mg56":"按下確認後,將發送接受加入請求!",
"mg57":"成功接受加入請求",
"mg58":"出了點問題",
"mg59":"取消加入請求已成功接受",
"mg60":"您確定要取消該會員的加入請求嗎?",
"mg61":"按下確認後,將發送取消加入請求!"
}
}

View File

@ -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",

View File

@ -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) {
</div>
<div id="balanceheader">
<span class="balanceheadertext">
${translate("general.balances")}
<vaadin-button theme="icon" id="reload" @click="${() => this.getAllBalances()}">
<vaadin-icon icon="vaadin:refresh"></vaadin-icon>
</vaadin-button>
<vaadin-tooltip text="Reload Balances" for="reload" position="top"></vaadin-tooltip>
${this.getAllBalancesLoading ? html`
${translate("general.update")}
` : html`
${translate("general.balances")}
<vaadin-button theme="icon" id="reload" @click="${() => this.getAllBalances()}">
<vaadin-icon icon="vaadin:refresh"></vaadin-icon>
</vaadin-button>
<vaadin-tooltip text="${translate("general.update")}" for="reload" position="top"></vaadin-tooltip>
`}
</span>
</div>
${this.getAllBalancesLoading ? html`<paper-progress indeterminate style="width: 100%; margin: 4px;"></paper-progress>` : ''}
${this.balanceTicker}
<app-info></app-info>
</div>
@ -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)

View File

@ -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`
<div>
<div>
<p>
${translate("settings.exp4")}
</p>
</div>
<div class="sub-main">
<div class="center-box">
<div class="content-box">
<div style="display: flex; align-items: center; justify-content: center;">
<img src="/img/btc.png" style="width: 32px; height: 32px;">&nbsp;&nbsp;${this.btcWALLET}<br>
</div>
<div @click=${() => this.checkForPmkDownload(this.btcWALLET, this.btcPMK, this.btcName, this.btcShort)} class="export-button"> ${translate("settings.exp2")} </div>
</div>
<div class="content-box">
<div style="display: flex; align-items: center; justify-content: center;">
<img src="/img/ltc.png" style="width: 32px; height: 32px;">&nbsp;&nbsp;${this.ltcWALLET}<br>
</div>
<div @click=${() => this.checkForPmkDownload(this.ltcWALLET, this.ltcPMK, this.ltcName, this.ltcShort)} class="export-button"> ${translate("settings.exp2")} </div>
</div>
<div class="content-box">
<div style="display: flex; align-items: center; justify-content: center;">
<img src="/img/doge.png" style="width: 32px; height: 32px;">&nbsp;&nbsp;${this.dogeWALLET}<br>
</div>
<div @click=${() => this.checkForPmkDownload(this.dogeWALLET, this.dogePMK, this.dogeName, this.dogeShort)} class="export-button"> ${translate("settings.exp2")} </div>
</div>
<div class="content-box">
<div style="display: flex; align-items: center; justify-content: center;">
<img src="/img/dgb.png" style="width: 32px; height: 32px;">&nbsp;&nbsp;${this.dgbWALLET}<br>
</div>
<div @click=${() => this.checkForPmkDownload(this.dgbWALLET, this.dgbPMK, this.dgbName, this.dgbShort)} class="export-button"> ${translate("settings.exp2")} </div>
</div>
<div class="content-box">
<div style="display: flex; align-items: center; justify-content: center;">
<img src="/img/rvn.png" style="width: 32px; height: 32px;">&nbsp;&nbsp;${this.rvnWALLET}<br>
</div>
<div @click=${() => this.checkForPmkDownload(this.rvnWALLET, this.rvnPMK, this.rvnName, this.rvnShort)} class="export-button"> ${translate("settings.exp2")} </div>
</div>
</div>
</div>
<mwc-dialog id="savePkmDialog" scrimClickAction="" escapeKeyAction="">
<img src="/img/${this.dCoinShort}.png" style="width: 32px; height: 32px;">
<h3>${this.dCoinName} ${translate("settings.exp2")}</h3>
<hr>
<h4>${translate("settings.address")}: ${this.dWalletAddress}</h4>
<mwc-button
slot="primaryAction"
@click="${() => this.closeSavePkmDialog()}"
class="red"
>
${translate("general.close")}
</mwc-button>
<mwc-button
slot="secondaryAction"
@click="${() => this.exportKey(this.dPrivateKey, this.dCoinName, this.dWalletAddress)}"
>
${translate("settings.exp3")}
</mwc-button>
</mwc-dialog>
</div>
`
}
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)

View File

@ -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) {
<ul>
<li @click=${ () => this.setSettingsView('info')} ><a class=${this.selectedView.id === 'info' ? 'active' : ''} href="javascript:void(0)">${translate("settings.account")}</a></li>
<li @click=${ () => this.setSettingsView('security')} ><a class=${this.selectedView.id === 'security' ? 'active' : ''} href="javascript:void(0)">${translate("settings.security")}</a></li>
<li @click=${ () => this.setSettingsView('export')} ><a class=${this.selectedView.id === 'export' ? 'active' : ''} href="javascript:void(0)">${translate("settings.exp1") }</a></li>
<li @click=${ () => this.setSettingsView('qr-login')} ><a class=${this.selectedView.id === 'qr-login' ? 'active' : ''} href="javascript:void(0)">${translate("settings.qr_login_menu_item") }</a></li>
<li @click=${ () => this.setSettingsView('notification')} ><a class=${this.selectedView.id === 'notification' ? 'active' : ''} href="javascript:void(0)">${translate("settings.notifications")}</a></li>
</ul>
@ -247,25 +250,29 @@ class UserSettings extends connect(store)(LitElement) {
renderSettingViews(selectedView) {
if (selectedView.id === 'info') {
return html`<account-view></account-view>`;
return html`<account-view></account-view>`
} else if (selectedView.id === 'security') {
return html`<security-view></security-view>`;
return html`<security-view></security-view>`
} else if (selectedView.id === 'export') {
return html`<export-keys></export-keys>`
} else if (selectedView.id === 'notification') {
return html`<notifications-view></notifications-view>`;
return html`<notifications-view></notifications-view>`
} else if (selectedView.id === 'qr-login') {
return html`<qr-login-view></qr-login-view>`;
return html`<qr-login-view></qr-login-view>`
}
}
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') {

View File

@ -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) {
</div>
</paper-dialog>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px;" id="userBoughtDialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px;" id="userBoughtDialog" modal>
<div class="card-explorer-container">
<div id="first-explorer-section">
${this.boughtBTCTemplate()}
@ -1193,7 +1191,7 @@ class UserInfoView extends connect(store)(LitElement) {
</div>
</paper-dialog>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px; overflow: auto;" id="userSoldDialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px; overflow: auto;" id="userSoldDialog" modal>
<div class="card-explorer-container">
<div id="first-explorer-section">
${this.soldBTCTemplate()}
@ -1213,7 +1211,7 @@ class UserInfoView extends connect(store)(LitElement) {
</div>
</paper-dialog>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px;" id="showTxDetailsDialog" entry-animation="scale-up-animation" exit-animation="fade-out-animation" with-backdrop>
<paper-dialog style="background: var(--white); border: 1px solid var(--black); border-radius: 5px;" id="showTxDetailsDialog" modal>
<div style="text-align: center; color: var(--black);">
<h1>${translate("walletpage.wchange5")}</h1>
<hr />
@ -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() {

View File

@ -26,8 +26,7 @@
"short-unique-id": "^4.4.4"
},
"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",
@ -63,7 +62,7 @@
"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",
"rollup-plugin-terser": "7.0.2",
@ -72,4 +71,4 @@
"engines": {
"node": ">=16.17.1"
}
}
}

View File

@ -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() {

View File

@ -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'
});

View File

@ -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 };

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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' });

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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';
}

View File

@ -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 })
@ -30,12 +30,15 @@ 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 },
newGroupInvitesList: { type: Array },
newGroupJoinsList: { type: Array },
recipientPublicKey: { type: String },
selectedAddress: { type: Object },
manageGroupObj: { type: Object },
@ -77,6 +80,7 @@ class GroupManagement extends LitElement {
toInviteMemberToGroup: { type: String },
toCancelInviteMemberName: { type: String },
toCancelInviteMemberAddress: { type: String },
searchGroupName: { type: String },
errorMessage: { type: String },
successMessage: { type: String }
}
@ -152,6 +156,11 @@ class GroupManagement extends LitElement {
color: red;
}
.success-icon {
font-size: 48px;
color: #198754;
}
.close-icon {
font-size: 36px;
}
@ -410,6 +419,13 @@ class GroupManagement extends LitElement {
background: var(--white);
color: var(--black);
}
#search {
width: 100%;
display: flex;
margin: auto;
align-items: center;
}
`
}
@ -418,12 +434,15 @@ 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 = []
this.newGroupInvitesList = []
this.newGroupJoinsList = []
this.manageGroupObj = {}
this.joinGroupObj = {}
this.leaveGroupObj = {}
@ -458,6 +477,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' }
@ -823,6 +843,7 @@ class GroupManagement extends LitElement {
groupInviteTemplate() {
return html`
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">${translate("managegroup.mg36")}</h3>
<vaadin-grid theme="large" id="groupInvitesGrid" ?hidden="${this.isEmptyArray(this.newGroupInvitesList)}" .items="${this.newGroupInvitesList}" aria-label="Group Invites" all-rows-visible>
<vaadin-grid-column
width="6rem"
@ -867,6 +888,54 @@ class GroupManagement extends LitElement {
${this.isEmptyArray(this.newGroupInvitesList) ? html`
<span style="color: var(--black);">${translate("managegroup.mg35")}</span>
` : html``}
<br><hr><br>
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">${translate("managegroup.mg53")}</h3>
<vaadin-grid theme="large" id="groupJoinsGrid" ?hidden="${this.isEmptyArray(this.newGroupJoinsList)}" .items="${this.newGroupJoinsList}" aria-label="Group Join Requests" all-rows-visible>
<vaadin-grid-column
width="6rem"
flex-grow="0"
header="${translate("websitespage.schange5")}"
.renderer=${(root, column, data) => {
render(html`${this.renderAvatar(data.item)}`, root)
}}
></vaadin-grid-column>
<vaadin-grid-column
auto-width
resizable
header="${translate("puzzlepage.pchange4")}"
.renderer=${(root, column, data) => {
render(html`${data.item.name}`, root)
}}
></vaadin-grid-column>
<vaadin-grid-column
auto-width
resizable
header="${translate("login.address")}"
.renderer=${(root, column, data) => {
render(html`${data.item.owner}`, root)
}}
></vaadin-grid-column>
<vaadin-grid-column
width="12rem"
flex-grow="0"
header="${translate("websitespage.schange8")}"
.renderer=${(root, column, data) => {
render(html`${this.renderConfirmRequestButton(data.item)}`, root)
}}
></vaadin-grid-column>
<vaadin-grid-column
width="12rem"
flex-grow="0"
.renderer=${(root, column, data) => {
render(html`${this.renderDeclineRequestButton(data.item)}`, root)
}}
></vaadin-grid-column>
</vaadin-grid>
${this.isEmptyArray(this.newGroupJoinsList) ? html`
<span style="color: var(--black);">${translate("managegroup.mg54")}</span>
` : html``}
<br>
<hr>
<div style="padding-top: 20px;">
<vaadin-button theme="primary medium" @click=${() => this.openInviteMemberToGroupDialog()}>
${translate("managegroup.mg2")}
@ -1020,6 +1089,72 @@ class GroupManagement extends LitElement {
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="successJoinDialog" scrimClickAction="" escapeKeyAction="">
<div class="card-container">
<mwc-icon class="success-icon">group_add</mwc-icon>
<h2>${translate("managegroup.mg57")}</h2>
<h4>${translate("walletpage.wchange43")}</h4>
</div>
<mwc-button
slot="primaryAction"
@click=${() => this.closeSuccessJoinDialog()}
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="errorJoinDialog" scrimClickAction="" escapeKeyAction="">
<div class="card-container">
<mwc-icon class="error-icon">warning</mwc-icon>
<h2>${translate("managegroup.mg58")}</h2>
<h4>${this.errorMessage}</h4>
<h4>${translate("walletpage.wchange44")}</h4>
</div>
<mwc-button
slot="primaryAction"
@click=${() => this.closeErrorJoinDialog()}
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="cancelSuccessJoinDialog" scrimClickAction="" escapeKeyAction="">
<div class="card-container">
<mwc-icon class="success-icon">person_remove</mwc-icon>
<h2>${translate("managegroup.mg59")}</h2>
<h4>${translate("walletpage.wchange43")}</h4>
</div>
<mwc-button
slot="primaryAction"
@click=${() => this.closeCancelSuccessJoinDialog()}
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="cancelErrorJoinDialog" scrimClickAction="" escapeKeyAction="">
<div class="card-container">
<mwc-icon class="error-icon">warning</mwc-icon>
<h2>${translate("managegroup.mg58")}</h2>
<h4>${this.errorMessage}</h4>
<h4>${translate("walletpage.wchange44")}</h4>
</div>
<mwc-button
slot="primaryAction"
@click=${() => this.closeCancelErrorJoinDialog()}
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
`
}
@ -1136,6 +1271,45 @@ class GroupManagement extends LitElement {
<mwc-button style="float:right;" @click=${() => this.shadowRoot.querySelector('#createGroupDialog').show()}><mwc-icon>add</mwc-icon>${translate("grouppage.gchange2")}</mwc-button>
</div>
<div class="divCard">
<h3 style="margin: 0; margin-bottom: 1em; text-align: left;">${translate("grouppage.gchange55")}</h3>
<div id="search">
<vaadin-text-field
theme="medium"
style="width: 20em"
minlength="3"
maxlength="32"
id="searchGroupName"
placeholder="${translate("grouppage.gchange56")}"
value="${this.searchGroupName}"
@keydown="${this.searchGroupListener}"
clear-button-visible
>
<vaadin-icon slot="prefix" icon="vaadin:user"></vaadin-icon>
</vaadin-text-field>&nbsp;&nbsp;<br>
<vaadin-button theme="medium" @click="${(e) => this.doGroupSearch(e)}">
<vaadin-icon icon="vaadin:search" slot="prefix"></vaadin-icon>
${translate("websitespage.schange35")}
</vaadin-button>
</div><br />
<vaadin-grid theme="large" id="priveGroupSearchGrid" ?hidden="${this.isEmptyArray(this.privateGroupSearch)}" .items="${this.privateGroupSearch}" aria-label="My Search Result" all-rows-visible>
<vaadin-grid-column width="8rem" flex-grow="0" header="${translate("grouppage.gchange54")}" path="memberCount"></vaadin-grid-column>
<vaadin-grid-column header="${translate("grouppage.gchange4")}" path="groupName"></vaadin-grid-column>
<vaadin-grid-column header="${translate("managegroup.mg42")}" .renderer=${(root, column, data) => {
if (data.item.isOpen === true) {
render(html`${translate("managegroup.mg44")}`, root)
} else {
render(html`${translate("managegroup.mg45")}`, root)
}
}}></vaadin-grid-column>
<vaadin-grid-column header="${translate("grouppage.gchange5")}" path="description"></vaadin-grid-column>
<vaadin-grid-column header="${translate("grouppage.gchange10")}" path="owner"></vaadin-grid-column>
<vaadin-grid-column width="11rem" flex-grow="0" header="${translate("grouppage.gchange7")}" .renderer=${(root, column, data) => {
render(html`<mwc-button @click=${() => this.joinGroup(data.item)}><mwc-icon>queue</mwc-icon>&nbsp;${translate("grouppage.gchange51")}</mwc-button>`, root)
}}></vaadin-grid-column>
</vaadin-grid>
</div>
<div class="divCard">
<h3 style="margin: 0; margin-bottom: 1em; text-align: center;">${translate("grouppage.gchange3")}</h3>
<vaadin-grid theme="large" id="joinedGroupsGrid" ?hidden="${this.isEmptyArray(this.joinedGroups)}" .items="${this.joinedGroups}" aria-label="Joined Groups" all-rows-visible>
@ -1150,7 +1324,9 @@ class GroupManagement extends LitElement {
}}></vaadin-grid-column>
</vaadin-grid>
${this.isEmptyArray(this.joinedGroups) ? html`
<span style="color: var(--black);">${translate("grouppage.gchange8")}</span>
<div style="text-align: center;">
<span style="color: var(--black);">${translate("grouppage.gchange8")}</span>
</div>
`: ''}
</div>
@ -1175,7 +1351,9 @@ class GroupManagement extends LitElement {
}}></vaadin-grid-column>
</vaadin-grid>
${this.isEmptyArray(this.groupInvites) ? html`
<span style="color: var(--black);">${translate("managegroup.mg35")}</span>
<div style="text-align: center;">
<span style="color: var(--black);">${translate("managegroup.mg35")}</span>
</div>
`: ''}
</div>
@ -1477,6 +1655,22 @@ class GroupManagement extends LitElement {
${translate("general.close")}
</mwc-button>
</mwc-dialog>
<mwc-dialog id="privateGroupErrorDialog" scrimClickAction="" escapeKeyAction="">
<div style="text-align: center;">
<mwc-icon class="error-icon">warning</mwc-icon>
<h2>${translate("grouppage.gchange57")}</h2>
<h4>${translate("grouppage.gchange58")}</h4>
</div>
<mwc-button
slot="primaryAction"
@click=${() => this.closePrivateGroupErrorDialog()}
class="red"
>
${translate("general.close")}
</mwc-button>
</mwc-dialog>
</div>
`
}
@ -1504,6 +1698,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 +1765,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 +1926,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`<mwc-button class="warning" @click=${() => this.openCreateBanMemberDialog(groupObj)}><mwc-icon>create</mwc-icon>&nbsp;${translate("managegroup.mg6")}</mwc-button>`
}
@ -1874,6 +2108,38 @@ class GroupManagement extends LitElement {
this.errorMessage = ''
}
renderConfirmRequestButton(joinObj) {
return html`<mwc-button class="green" @click=${() => this.createAcceptJoinGroupMember(joinObj)}><mwc-icon>add_task</mwc-icon>&nbsp;${translate("transpage.tchange3")}</mwc-button>`
}
renderDeclineRequestButton(joinObj) {
return html`<mwc-button class="red" @click=${() => this.kickJoinGroupMember(joinObj)}><mwc-icon>cancel</mwc-icon>&nbsp;${translate("transpage.tchange2")}</mwc-button>`
}
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
@ -1939,6 +2205,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
@ -2248,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()
@ -2276,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()
}
@ -2944,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
@ -3178,4 +3639,4 @@ class GroupManagement extends LitElement {
}
}
window.customElements.define('group-management', GroupManagement)
window.customElements.define('group-management', GroupManagement)

View File

@ -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`
}

View File

@ -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`
<paper-dialog id="loadChartDialog" class="chart-loading-wrapper" modal>
<div class="loadingContainer" style="display:${this.isLoadingTradesChart ? 'inline-block' : 'none'}">
<span style="color: var(--black);">${translate("login.loading")}</span>
</div>
</paper-dialog>
<paper-dialog id="arrrChartDialog" class="chart-info-wrapper">
<div class="chart-container">
<div id='arrrStockPriceContainer' class='trades-chart'></div>
</div>
</paper-dialog>
`
}
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

View File

@ -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`
<paper-dialog id="loadChartDialog" class="chart-loading-wrapper" modal>
<div class="loadingContainer" style="display:${this.isLoadingTradesChart ? 'inline-block' : 'none'}">
<span style="color: var(--black);">${translate("login.loading")}</span>
</div>
</paper-dialog>
<paper-dialog id="btcChartDialog" class="chart-info-wrapper">
<div class="chart-container">
<div id='btcStockPriceContainer' class='trades-chart'></div>
</div>
</paper-dialog>
`
}
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

View File

@ -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`
<paper-dialog id="loadChartDialog" class="chart-loading-wrapper" modal>
<div class="loadingContainer" style="display:${this.isLoadingTradesChart ? 'inline-block' : 'none'}">
<span style="color: var(--black);">${translate("login.loading")}</span>
</div>
</paper-dialog>
<paper-dialog id="dgbChartDialog" class="chart-info-wrapper">
<div class="chart-container">
<div id='dgbStockPriceContainer' class='trades-chart'></div>
</div>
</paper-dialog>
`
}
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

View File

@ -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`
<paper-dialog id="loadChartDialog" class="chart-loading-wrapper" modal>
<div class="loadingContainer" style="display:${this.isLoadingTradesChart ? 'inline-block' : 'none'}">
<span style="color: var(--black);">${translate("login.loading")}</span>
</div>
</paper-dialog>
<paper-dialog id="dogeChartDialog" class="chart-info-wrapper">
<div class="chart-container">
<div id='dogeStockPriceContainer' class='trades-chart'></div>
</div>
</paper-dialog>
`
}
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

View File

@ -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`
<paper-dialog id="loadChartDialog" class="chart-loading-wrapper" modal>
<div class="loadingContainer" style="display:${this.isLoadingTradesChart ? 'inline-block' : 'none'}">
<span style="color: var(--black);">${translate("login.loading")}</span>
</div>
</paper-dialog>
<paper-dialog id="ltcChartDialog" class="chart-info-wrapper">
<div class="chart-container">
<div id='ltcStockPriceContainer' class='trades-chart'></div>
</div>
</paper-dialog>
`
}
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

View File

@ -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`
<paper-dialog id="loadChartDialog" class="chart-loading-wrapper" modal>
<div class="loadingContainer" style="display:${this.isLoadingTradesChart ? 'inline-block' : 'none'}">
<span style="color: var(--black);">${translate("login.loading")}</span>
</div>
</paper-dialog>
<paper-dialog id="rvnChartDialog" class="chart-info-wrapper">
<div class="chart-container">
<div id='rvnStockPriceContainer' class='trades-chart'></div>
</div>
</paper-dialog>
`
}
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

View File

@ -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 })
@ -1160,8 +1166,11 @@ class TradePortal extends LitElement {
<mwc-list-item value="DOGECOIN"><span class="coinName doge" style="color: var(--black);">QORT / DOGE</span></mwc-list-item>
<mwc-list-item value="DIGIBYTE"><span class="coinName dgb" style="color: var(--black);">QORT / DGB</span></mwc-list-item>
<mwc-list-item value="RAVENCOIN"><span class="coinName rvn" style="color: var(--black);">QORT / RVN</span></mwc-list-item>
<mwc-list-item value="PIRATECHAIN"><span class="coinName arrr" style="color: var(--black);">QORT / ARRR</span></mwc-list-item>
<mwc-list-item value="PIRATECHAIN"><span class="coinName arrr" style="color: var(--black);">QORT / ARRR</span></mwc-list-item>
</mwc-select>
<div style="padding-left: 25px; padding-top: 15px;">
${this.chartShowCoin()}
</div>
</div>
<div id="trade-portal">
<div id="first-trade-section">
@ -1412,6 +1421,31 @@ class TradePortal extends LitElement {
return html`<span class="warning-text">NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode}</span>`
}
chartShowCoin() {
switch(this.listedCoins.get(this.selectedCoin).coinCode) {
case "BTC":
return html`<mwc-button dense unelevated label="BTC ${translate("tradepage.tchange49")}" @click=${() => chartsbtc.open()}></mwc-button>`
break
case "LTC":
return html`<mwc-button dense unelevated label="LTC ${translate("tradepage.tchange49")}" @click=${() => chartsltc.open()}></mwc-button>`
break
case "DOGE":
return html`<mwc-button dense unelevated label="DOGE ${translate("tradepage.tchange49")}" @click=${() => chartsdoge.open()}></mwc-button>`
break
case "DGB":
return html`<mwc-button dense unelevated label="DGB ${translate("tradepage.tchange49")}" @click=${() => chartsdgb.open()}></mwc-button>`
break
case "RVN":
return html`<mwc-button dense unelevated label="RVN ${translate("tradepage.tchange49")}" @click=${() => chartsrvn.open()}></mwc-button>`
break
case "ARRR":
return html`<mwc-button dense unelevated label="ARRR ${translate("tradepage.tchange49")}" @click=${() => chartsarrr.open()}></mwc-button>`
break
default:
break
}
}
exchangeRateQort() {
switch(this.listedCoins.get(this.selectedCoin).coinCode) {
case "BTC":

View File

@ -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'
@ -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 {
</mwc-button>
</mwc-dialog>
<mwc-dialog id="showRvnTransactionDetailsDialog" scrimClickAction="${this.showRvnTransactionDetailsLoading ? '' : 'close'}">
<mwc-dialog id="showRvnTransactionDetailsDialog" scrimClickAction="${this.showRvnTransactionDetailsLoading ? '' : 'close'}">
<div style="text-align: center;">
<h1>${translate("walletpage.wchange5")}</h1>
<hr />
@ -1577,7 +1577,7 @@ class MultiWallet extends LitElement {
</mwc-button>
</mwc-dialog>
<mwc-dialog id="sendRvnDialog" scrimClickAction="" escapeKeyAction="">
<mwc-dialog id="sendRvnDialog" scrimClickAction="" escapeKeyAction="">
<div class="send-coin-dialog">
<div style="text-align: center;">
<img src="/img/rvn.png" width="32" height="32">
@ -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)
}
}