diff --git a/img/notfound.png b/img/notfound.png new file mode 100644 index 00000000..a70a4056 Binary files /dev/null and b/img/notfound.png differ diff --git a/package.json b/package.json index 45c93c74..a2e16766 100644 --- a/package.json +++ b/package.json @@ -1,48 +1,48 @@ { - "name": "qortal-ui", - "version": "2.1.1", - "description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet", - "keywords": [ - "QORT", - "QORTAL", - "DECENTRALIZED" - ], - "main": "electron.js", - "repository": { - "type": "git", - "url": "git+https://github.com/Qortal/qortal-ui.git" - }, - "homepage": "https://qortal.org", - "author": "QORTAL ", - "license": "GPL-3.0", - "scripts": { - "install_link:all": "(cd qortal-ui-core && yarn install && yarn link) && (cd qortal-ui-plugins && yarn install && yarn link) && (cd qortal-ui-crypto && yarn install && yarn link) && (yarn link qortal-ui-core && yarn link qortal-ui-plugins && yarn link qortal-ui-crypto)", - "dev": "node server.js", - "prebuild": "node -p \"'export const UI_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > qortal-ui-core/src/redux/app/version.js", - "build-dev": "node build.js", - "build": "NODE_ENV=production node build.js", - "server": "NODE_ENV=production node server.js", - "watch": "node watch.js", - "watch-inline": "node watch-inline.js", - "start-electron": "NODE_ENV=production electron .", - "build-electron": "electron-builder build --publish never", - "deploy-electron": "electron-builder build --win --publish never", - "release": "NODE_ENV=production electron-builder build --publish never", - "update-package-json": "node update-package-json.js", - "publish": "electron-builder -p always" - }, - "dependencies": { - "electron-log": "4.4.8", - "electron-updater": "5.2.1" - }, - "devDependencies": { - "electron": "21.1.1", - "electron-builder": "23.3.3", - "electron-notarize": "1.2.1", - "electron-packager": "16.0.0", - "shelljs": "0.8.5" - }, - "engines": { - "node": ">=16.15.0" - } -} + "name": "qortal-ui", + "version": "2.1.1", + "description": "Qortal Project - decentralize the world - Data storage, communications, web hosting, decentralized trading, complete infrastructure for the future blockchain-based Internet", + "keywords": [ + "QORT", + "QORTAL", + "DECENTRALIZED" + ], + "main": "electron.js", + "repository": { + "type": "git", + "url": "git+https://github.com/Qortal/qortal-ui.git" + }, + "homepage": "https://qortal.org", + "author": "QORTAL ", + "license": "GPL-3.0", + "scripts": { + "install_link:all": "(cd qortal-ui-core && yarn install && yarn link) && (cd qortal-ui-plugins && yarn install && yarn link) && (cd qortal-ui-crypto && yarn install && yarn link) && (yarn link qortal-ui-core && yarn link qortal-ui-plugins && yarn link qortal-ui-crypto)", + "dev": "node server.js", + "prebuild": "node -p \"'export const UI_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > qortal-ui-core/src/redux/app/version.js", + "build-dev": "node build.js", + "build": "NODE_ENV=production node build.js", + "server": "NODE_ENV=production node server.js", + "watch": "node watch.js", + "watch-inline": "node watch-inline.js", + "start-electron": "NODE_ENV=production electron .", + "build-electron": "electron-builder build --publish never", + "deploy-electron": "electron-builder build --win --publish never", + "release": "NODE_ENV=production electron-builder build --publish never", + "update-package-json": "node update-package-json.js", + "publish": "electron-builder -p always" + }, + "dependencies": { + "electron-log": "4.4.8", + "electron-updater": "5.3.0" + }, + "devDependencies": { + "electron": "21.2.3", + "electron-builder": "23.6.0", + "electron-packager": "17.1.1", + "@electron/notarize": "1.2.3", + "shelljs": "^0.8.5" + }, + "engines": { + "node": ">=16.15.0" + } +} \ No newline at end of file diff --git a/qortal-ui-core/language/de.json b/qortal-ui-core/language/de.json index 06dbe884..64b4dba6 100644 --- a/qortal-ui-core/language/de.json +++ b/qortal-ui-core/language/de.json @@ -308,7 +308,20 @@ "tchange30":"Guthaben konnte nicht abgerufen werden. Versuchen Sie es nochmal!", "tchange31":"VERKAUFT", "tchange32":"GEKAUFT", - "tchange33":"Tauschrate" + "tchange33":"Durchschnitt", + "tchange34":"Betrag darf nicht 0 sein", + "tchange35":"Der Preis darf nicht 0 sein", + "tchange36":"AUSSTEHENDER AUTOKAUF", + "tchange37":"Keine automatische Kaufbestellung gefunden!", + "tchange38":"HINZUFÜGEN", + "tchange39":"AUTO KAUFAUFTRAG", + "tchange40":"Preis", + "tchange41":"Automatischer Kaufauftrag erfolgreich entfernt!", + "tchange42":"MARKET OFFENE VERKAUFSAUFTRÄGE", + "tchange43":"MEINE KAUFGESCHICHTE", + "tchange44":"Automatischer Kaufauftrag erfolgreich hinzugefügt!", + "tchange45":"AUTO KAUFEN MIT", + "tchange46":"AUTOKAUF" }, "rewardsharepage":{ "rchange1":"Belohnungsanteile", @@ -674,5 +687,14 @@ "schange19":"Sponsoring-Schlüssel kopieren", "schange20":"Beziehung schaffen", "schange21":"Sponsoring-Schlüssel entfernen" + }, + "explorerpage":{ + "exp1":"Adresse oder Name zum Suchen", + "exp2":"Kontostand", + "exp3":"Mehr Info", + "exp4":"Adresse oder Name nicht gefunden !", + "exp5":"Beachten Sie, dass bei registrierten Namen zwischen Groß- und Kleinschreibung unterschieden wird.", + "exp6":"Gründer", + "exp7":"Info" } } diff --git a/qortal-ui-core/language/es.json b/qortal-ui-core/language/es.json index 36b86d98..e77ff9ce 100644 --- a/qortal-ui-core/language/es.json +++ b/qortal-ui-core/language/es.json @@ -308,7 +308,20 @@ "tchange30":"Fallo en la obtención del saldo. Inténtelo de nuevo!", "tchange31":"VENDIDO", "tchange32":"COMPRADO", - "tchange33":"Tipo de cambio" + "tchange33":"Promedio", + "tchange34":"La cantidad no puede ser 0", + "tchange35":"El precio no puede ser 0", + "tchange36":"AUTO COMPRA PENDIENTE", + "tchange37":"No se ha encontrado ninguna orden de compra automática!", + "tchange38":"AGREGAR", + "tchange39":"AUTO ORDEN DE COMPRA", + "tchange40":"Precio", + "tchange41":"Orden de compra automática eliminada con éxito!", + "tchange42":"MERCADO ABIERTO ÓRDENES DE VENTA", + "tchange43":"MI HISTORIAL DE COMPRAS", + "tchange44":"Orden de compra automática agregada con éxito!", + "tchange45":"AUTO COMPRAR CON", + "tchange46":"COMPRA AUTOMÁTICA" }, "rewardsharepage":{ "rchange1":"Rewardshares", @@ -674,5 +687,14 @@ "schange19":"Copiar clave de patrocinio", "schange20":"Creando relación", "schange21":"Eliminar clave de patrocinio" + }, + "explorerpage":{ + "exp1":"Dirección o nombre a buscar", + "exp2":"Saldo de la cuenta", + "exp3":"Más información", + "exp4":"No se encontró la dirección o el nombre!", + "exp5":"Tenga en cuenta que los nombres registrados distinguen entre mayúsculas y minúsculas.", + "exp6":"Fundador", + "exp7":"Información" } } \ No newline at end of file diff --git a/qortal-ui-core/language/fr.json b/qortal-ui-core/language/fr.json index 0cbca9bd..51926f65 100644 --- a/qortal-ui-core/language/fr.json +++ b/qortal-ui-core/language/fr.json @@ -308,7 +308,20 @@ "tchange30":"Échec de la récupération du solde. Réessayez!", "tchange31":"VENDU", "tchange32":"ACHETE", - "tchange33":"Taux de change" + "tchange33":"Moyen", + "tchange34":"Le montant ne peut pas être 0", + "tchange35":"Le prix ne peut pas être 0", + "tchange36":"ACHAT AUTOMATIQUE EN ATTENTE", + "tchange37":"Aucune commande d'achat automatique trouvée !", + "tchange38":"AJOUTER", + "tchange39":"COMMANDE D'ACHAT AUTOMATIQUE", + "tchange40":"Prix", + "tchange41":"Commande d'achat automatique supprimée avec succès !", + "tchange42":"ORDRES DE VENTE À L'OUVERTURE DU MARCHÉ", + "tchange43":"MON HISTORIQUE D'ACHAT", + "tchange44":"Commande d'achat automatique ajoutée avec succès !", + "tchange45":"ACHAT AUTO AVEC", + "tchange46":"ACHAT AUTOMATIQUE" }, "rewardsharepage":{ "rchange1":"Récompenses", @@ -674,5 +687,14 @@ "schange19":"Copier la clé de parrainage", "schange20":"Créer une relation", "schange21":"Supprimer la clé de parrainage" + }, + "explorerpage":{ + "exp1":"Adresse ou nom à rechercher", + "exp2":"Solde du compte", + "exp3":"Plus d'informations", + "exp4":"Adresse ou nom introuvable !", + "exp5":"Notez que les noms enregistrés sont sensibles à la casse.", + "exp6":"Fondateur", + "exp7":"Info" } } \ No newline at end of file diff --git a/qortal-ui-core/language/hindi.json b/qortal-ui-core/language/hindi.json index 6c73abc1..ecb68c14 100644 --- a/qortal-ui-core/language/hindi.json +++ b/qortal-ui-core/language/hindi.json @@ -309,7 +309,20 @@ "tchange30":"शेष को लाने में विफल फिर से प्रयास करें!", "tchange31":"बेचा", "tchange32":"खरीद लिया", - "tchange33":"विनिमय दर" + "tchange33":"औसत", + "tchange34":"राशि 0 नहीं हो सकती", + "tchange35":"मूल्य 0 नहीं हो सकता", + "tchange36":"लंबित ऑटो खरीद", + "tchange37":"कोई ऑटो खरीद आदेश नहीं मिला!", + "tchange38":"जोड़ें", + "tchange39":"ऑटो खरीद आदेश", + "tchange40":"कीमत", + "tchange41":"स्वचालित खरीद आदेश सफलतापूर्वक निकाला गया!", + "tchange42":"मार्केट ओपन सेल ऑर्डर", + "tchange43":"मेरा इतिहास खरीदें", + "tchange44":"स्वतः खरीद आदेश सफलतापूर्वक जोड़ा गया!", + "tchange45":"ऑटो के साथ खरीदें", + "tchange46":"ऑटो खरीदें" }, "rewardsharepage":{ "rchange1":"रिवॉर्डशेयर", @@ -675,5 +688,14 @@ "schange19":"कॉपी प्रायोजन कुंजी", "schange20":"संबंध बनाना", "schange21":"प्रायोजन कुंजी हटाएं" + }, + "explorerpage":{ + "exp1":"खोजने के लिए पता या नाम", + "exp2":"खाते में शेष", + "exp3":"और जानकारी", + "exp4":"पता या नाम नहीं मिला !", + "exp5":"ध्यान दें कि पंजीकृत नाम केस-संवेदी होते हैं।", + "exp6":"संस्थापक", + "exp7":"जानकारी" } } \ No newline at end of file diff --git a/qortal-ui-core/language/hr.json b/qortal-ui-core/language/hr.json index 3c5d6594..2b75d2c0 100644 --- a/qortal-ui-core/language/hr.json +++ b/qortal-ui-core/language/hr.json @@ -308,7 +308,20 @@ "tchange30":"Neuspješno dohvatiti stanje. Pokušajte ponovno!", "tchange31":"PRODANO", "tchange32":"KUPLJENO", - "tchange33":"Tečaj" + "tchange33":"Prosjek", + "tchange34":"Iznos ne može biti 0", + "tchange35":"Cijena ne može biti 0", + "tchange36":"ČEKA SE AUTO KUPNJA", + "tchange37":"Nije pronađena automatska narudžba za kupnju!", + "tchange38":"DODATI", + "tchange39":"AUTOMATSKA NARUDŽBA", + "tchange40":"Cijena", + "tchange41":"Uspješno uklonjena automatska narudžba za kupnju!", + "tchange42":"TRŽIŠNI OTVORENI PRODAJNI NALOGI", + "tchange43":"MOJA POVIJEST KUPNJE", + "tchange44":"Uspješno dodana automatska narudžba za kupnju!", + "tchange45":"AUTO KUPITE SA", + "tchange46":"AUTO OTKUP" }, "rewardsharepage":{ "rchange1":"Nagradni udio (Rewardshares)", @@ -674,5 +687,14 @@ "schange19":"Kopiraj ključ sponzorstva", "schange20":"Stvaranje odnosa", "schange21":"Ukloni sponzorski ključ" + }, + "explorerpage":{ + "exp1":"Adresa ili ime za pretraživanje", + "exp2":"Stanje na računu", + "exp3":"Više informacija", + "exp4":"Adresa ili ime nisu pronađeni!", + "exp5":"Imajte na umu da su registrirana imena osjetljiva na velika i mala slova.", + "exp6":"Osnivač", + "exp7":"Info" } } \ No newline at end of file diff --git a/qortal-ui-core/language/hu.json b/qortal-ui-core/language/hu.json index 31e7d54a..6d1b137a 100644 --- a/qortal-ui-core/language/hu.json +++ b/qortal-ui-core/language/hu.json @@ -308,7 +308,20 @@ "tchange30":"Nem sikerült lekérni az egyenleget. Próbáld újra!", "tchange31":"ELADOTT", "tchange32":"VÁSÁROLT", - "tchange33":"Árfolyam" + "tchange33":"Átlagos", + "tchange34":"Az összeg nem lehet 0", + "tchange35":"Az ár nem lehet 0", + "tchange36":"FÜGGŐBEN AUTOMATIKUS VÁSÁRLÁS", + "tchange37":"Nem található automatikus vásárlási rendelés!", + "tchange38":"HOZZÁAD", + "tchange39":"AUTOMATIKUS VÁSÁRLÁSI MEGRENDELÉS", + "tchange40":"Ár", + "tchange41":"Az automatikus vásárlási rendelés sikeresen eltávolítva!", + "tchange42":"NYÍLT PIACI ELADÁSI MEGRENDELÉSEK", + "tchange43":"VÁSÁRLÁSI TÖRTÉNETEM", + "tchange44":"Az automatikus vásárlási rendelés sikeresen hozzáadva!", + "tchange45":"AUTOMATIKUS VÁSÁRLÁS", + "tchange46":"AUTOMATIKUS VÁSÁRLÁS" }, "rewardsharepage":{ "rchange1":"Jutalommegosztások", @@ -674,5 +687,14 @@ "schange19":"Szponzori kulcs másolása", "schange20":"Kapcsolatteremtés", "schange21":"Szponzori kulcs távolítsa" + }, + "explorerpage":{ + "exp1":"Keresendő cím vagy név", + "exp2":"Számlaegyenleg", + "exp3":"Több információ", + "exp4":"Cím vagy név nem található!", + "exp5":"Vegye figyelembe, hogy a regisztrált nevek megkülönböztetik a kis- és nagybetűket.", + "exp6":"Alapító", + "exp7":"Info" } -} +} \ No newline at end of file diff --git a/qortal-ui-core/language/it.json b/qortal-ui-core/language/it.json index 04cfa409..05533904 100644 --- a/qortal-ui-core/language/it.json +++ b/qortal-ui-core/language/it.json @@ -308,7 +308,20 @@ "tchange30":"Impossibile recuperare il saldo. Riprova!", "tchange31":"VENDUTO", "tchange32":"ACQUISTATO", - "tchange33":"Tasso di cambio" + "tchange33":"Media", + "tchange34":"L'importo non può essere 0", + "tchange35":"Il prezzo non può essere 0", + "tchange36":"ACQUISTO AUTO IN ATTESA", + "tchange37":"Nessun ordine di acquisto automatico trovato!", + "tchange38":"INSERISCI", + "tchange39":"ORDINE DI ACQUISTO AUTOMATICO", + "tchange40":"Prezzo", + "tchange41":"Ordine di acquisto automatico rimosso con successo!", + "tchange42":"MERCATO APERTO ORDINI DI VENDITA", + "tchange43":"LA MIA STORIA DI ACQUISTO", + "tchange44":"Ordine di acquisto automatico aggiunto con successo!", + "tchange45":"ACQUISTA AUTO CON", + "tchange46":"ACQUISTO AUTO" }, "rewardsharepage":{ "rchange1":"Quote di ricompensa", @@ -674,5 +687,14 @@ "schange19":"Copia la chiave di sponsorizzazione", "schange20":"Creare relazione", "schange21":"Rimuovi la chiave di sponsorizzazione" + }, + "explorerpage":{ + "exp1":"Indirizzo o nome da cercare", + "exp2":"Saldo del conto", + "exp3":"Ulteriori informazioni", + "exp4":"Indirizzo o nome non trovato!", + "exp5":"Si noti che i nomi registrati fanno distinzione tra maiuscole e minuscole.", + "exp6":"Fondatore", + "exp7":"Informazioni" } } \ No newline at end of file diff --git a/qortal-ui-core/language/ko.json b/qortal-ui-core/language/ko.json index 987f978c..d6f69bd8 100644 --- a/qortal-ui-core/language/ko.json +++ b/qortal-ui-core/language/ko.json @@ -308,7 +308,20 @@ "tchange30":"밸런스를 가져오지 못했습니다. 다시 시도하십시오.!", "tchange31":"판매된", "tchange32":"구매된", - "tchange33":"환율" + "tchange33":"평균", + "tchange34":"금액은 0일 수 없습니다.", + "tchange35":"가격은 0일 수 없습니다.", + "tchange36":"대기 중인 자동 구매", + "tchange37":"자동 구매 주문이 없습니다!", + "tchange38":"추가하다", + "tchange39":"자동 구매 주문", + "tchange40":"가격", + "tchange41":"자동 구매 주문을 성공적으로 제거했습니다!", + "tchange42":"시장 오픈 매도 주문", + "tchange43":"내 구매 내역", + "tchange44":"자동 구매 주문을 성공적으로 추가했습니다!", + "tchange45":"자동 구매", + "tchange46":"자동 구매" }, "rewardsharepage":{ "rchange1":"보상 공유", @@ -674,5 +687,14 @@ "schange19":"후원 키 복사", "schange20":"관계 만들기", "schange21":"후원 키 제거" + }, + "explorerpage":{ + "exp1":"검색할 주소 또는 이름", + "exp2":"계정 잔액", + "exp3":"더 많은 정보", + "exp4":"주소 또는 이름을 찾을 수 없습니다!", + "exp5":"등록된 이름은 대소문자를 구분합니다.", + "exp6":"설립자", + "exp7":"정보" } } \ No newline at end of file diff --git a/qortal-ui-core/language/no.json b/qortal-ui-core/language/no.json index 04aaddf3..3c67e75a 100644 --- a/qortal-ui-core/language/no.json +++ b/qortal-ui-core/language/no.json @@ -308,7 +308,20 @@ "tchange30":"Kunne ikke hente saldoen. Prøv igjen!", "tchange31":"SOLGT", "tchange32":"KJØPT", - "tchange33":"Vekslingskurs" + "tchange33":"Gjennomsnitt", + "tchange34":"Beløpet kan ikke være 0", + "tchange35":"Prisen kan ikke være 0", + "tchange36":"VENTENDE AUTOKJØP", + "tchange37":"Ingen autokjøpsordre funnet!", + "tchange38":"LEGGE TIL", + "tchange39":"AUTO KJØP ORDRE", + "tchange40":"Pris", + "tchange41":"Fjernet automatisk kjøpsordre!", + "tchange42":"MARKED ÅPNE SALGSORDRE", + "tchange43":"MIN KJØPSHISTORIE", + "tchange44":"Autokjøpsordre er lagt til!", + "tchange45":"AUTOKJØP MED", + "tchange46":"AUTOKJØP" }, "rewardsharepage":{ "rchange1":"Belønningsdel", @@ -674,5 +687,14 @@ "schange19":"Kopier sponsornøkkel", "schange20":"Skaper forhold", "schange21":"Fjern sponsornøkkel" + }, + "explorerpage":{ + "exp1":"Adresse eller navn for å søke", + "exp2":"Saldo", + "exp3":"Mer informasjon", + "exp4":"Finner ikke adresse eller navn!", + "exp5":"Merk at registrerte navn skiller mellom store og små bokstaver.", + "exp6":"Grunnlegger", + "exp7":"Info" } -} +} \ No newline at end of file diff --git a/qortal-ui-core/language/pl.json b/qortal-ui-core/language/pl.json index c5e0b57f..c5ede25d 100644 --- a/qortal-ui-core/language/pl.json +++ b/qortal-ui-core/language/pl.json @@ -308,7 +308,20 @@ "tchange30":"Nie udało się pobrać salda. Spróbuj ponownie!", "tchange31":"SPRZEDANY", "tchange32":"KUPIONY", - "tchange33":"Kurs wymiany" + "tchange33":"Przeciętny", + "tchange34":"Kwota nie może wynosić 0", + "tchange35":"Cena nie może wynosić 0", + "tchange36":"OCZEKUJĄCY NA AUTOMATYCZNY KUP", + "tchange37":"Nie znaleziono automatycznego zamówienia zakupu!", + "tchange38":"DODAJ", + "tchange39":"AUTOMATYCZNE ZAMÓWIENIE", + "tchange40":"Cena £", + "tchange41":"Pomyślnie usunięto automatyczne zamówienie zakupu!", + "tchange42":"RYNEK OTWARTYCH ZLECEŃ SPRZEDAŻY", + "tchange43":"MOJA HISTORIA ZAKUPÓW", + "tchange44":"Pomyślnie dodano automatyczne zamówienie zakupu!", + "tchange45":"AUTO KUP Z", + "tchange46":"AUTO KUP" }, "rewardsharepage":{ "rchange1":"Podział nagród", @@ -674,5 +687,14 @@ "schange19":"Kopiuj klucz sponsorski", "schange20":"Tworzenie relacji", "schange21":"Usuń klucz sponsorsk" + }, + "explorerpage":{ + "exp1":"Adres lub nazwa do wyszukania", + "exp2":"Bilans konta", + "exp3":"Więcej informacji", + "exp4":"Nie znaleziono adresu lub nazwy!", + "exp5":"Należy pamiętać, że w zarejestrowanych nazwach rozróżniana jest wielkość liter.", + "exp6":"Założyciel", + "exp7":"Informacje" } } \ No newline at end of file diff --git a/qortal-ui-core/language/pt.json b/qortal-ui-core/language/pt.json index d3463b86..821abb4c 100644 --- a/qortal-ui-core/language/pt.json +++ b/qortal-ui-core/language/pt.json @@ -308,7 +308,20 @@ "tchange30":"Falha buscando Saldo. Tente novamente!", "tchange31":"VENDIDO", "tchange32":"COMPRADO", - "tchange33":"Taxa de câmbio" + "tchange33":"Média", + "tchange34":"O valor nao pode ser 0", + "tchange35":"O preço nao pode ser 0", + "tchange36":"COMPRA AUTOMÁTICA PENDENTE", + "tchange37":"Nenhuma ordem de compra automática encontrada!", + "tchange38":"ADICIONAR", + "tchange39":"ORDEM DE COMPRA AUTOMÁTICA", + "tchange40":"Preço", + "tchange41":"Pedido de compra automática removido com sucesso!", + "tchange42":"ORDENS DE VENDA ABERTA AO MERCADO", + "tchange43":"MEU HISTÓRICO DE COMPRAS", + "tchange44":"Pedido de compra automática adicionado com sucesso!", + "tchange45":"COMPRA AUTOMÁTICA COM", + "tchange46":"COMPRA AUTOMÁTICA" }, "rewardsharepage":{ "rchange1":"Ações de recompensa", @@ -674,5 +687,14 @@ "schange19":"Copiar chave de patrocínio", "schange20":"Criando relacionamento", "schange21":"Remover chave de patrocínio" + }, + "explorerpage":{ + "exp1":"Endereço ou nome para pesquisar", + "exp2":"Saldo da conta", + "exp3":"Mais informações", + "exp4":"Endereço ou nome não encontrado!", + "exp5":"Observe que os nomes registrados diferenciam maiúsculas de minúsculas.", + "exp6":"Fundador", + "exp7":"Informações" } } \ No newline at end of file diff --git a/qortal-ui-core/language/ro.json b/qortal-ui-core/language/ro.json index 286aab56..cfb054e0 100644 --- a/qortal-ui-core/language/ro.json +++ b/qortal-ui-core/language/ro.json @@ -308,7 +308,20 @@ "tchange30":"Nu s-a reusit actualizarea soldului. Mai incearca odata!", "tchange31":"VANDUT", "tchange32":"CUMPARAT", - "tchange33":"Rata de schimb" + "tchange33":"In medie", + "tchange34":"Suma nu poate fi 0", + "tchange35":"Pretul nu poate fi 0", + "tchange36":"CUMPARARE AUTOMATĂ in așteptare", + "tchange37":"Nu a fost gasită nicio comandă de cumparare automata!", + "tchange38":"ADAUGA", + "tchange39":"COMANDA DE CUMPARARE AUTOMATA", + "tchange40":"Pret", + "tchange41":"Comanda de cumparare automata a fost eliminata cu succes!", + "tchange42":"PIATA DESCHISA COMENZI DE VANZARE", + "tchange43":"ISTORICUL MEU DE CUMPARARI", + "tchange44":"Comanda de cumparare automata a fost adaugata cu succes!", + "tchange45":"CUMPARA AUTOMATA CU", + "tchange46":"CUMPARARE AUTOMATA" }, "rewardsharepage":{ "rchange1":"Cote de recompensa", @@ -674,5 +687,14 @@ "schange19":"Copiati cheia de sponsorizare", "schange20":"Crearea unei relatii", "schange21":"Eliminati cheia de sponsorizare" + }, + "explorerpage":{ + "exp1":"Adresa sau numele de căutat", + "exp2":"Soldul contului", + "exp3":"Mai multe informatii", + "exp4":"Adresa sau Numele nu a fost găsit!", + "exp5":"Retineti că numele înregistrate sunt sensibile la majuscule.", + "exp6":"Fondator", + "exp7":"Info" } -} +} \ No newline at end of file diff --git a/qortal-ui-core/language/rs.json b/qortal-ui-core/language/rs.json index 8b82c8b6..d67b977f 100644 --- a/qortal-ui-core/language/rs.json +++ b/qortal-ui-core/language/rs.json @@ -308,7 +308,20 @@ "tchange30":"Neuspešna provera salda. Pokušajte ponovo!", "tchange31":"PRODATO", "tchange32":"KUPLJENO", - "tchange33":"Kurs" + "tchange33":"Prosek", + "tchange34":"Iznos ne može biti 0", + "tchange35":"Cena ne može biti 0", + "tchange36":"NA ČEKANJU AUTOMATSKA KUPOVINA", + "tchange37":"Nije pronađena narudžbina za automatsku kupovinu!", + "tchange38":"DODATI", + "tchange39":"AUTO KUPOVINA", + "tchange40":"Cena", + "tchange41":"Narudžbina za automatsku kupovinu je uspešno uklonjena!", + "tchange42":"TRŽIŠTE OTVORENI NALOG ZA PRODAJU", + "tchange43":"MOJA ISTORIJA KUPOVINE", + "tchange44":"Narudžbina za automatsku kupovinu je uspešno dodata!", + "tchange45":"AUTO KUPI SA", + "tchange46":"AUTO BUI" }, "rewardsharepage":{ "rchange1":"Udeo nagrade", @@ -674,5 +687,14 @@ "schange19":"Kopiraj sponzorski ključ", "schange20":"Stvaranje odnosa", "schange21":"Uklonite sponzorski ključ" + }, + "explorerpage":{ + "exp1":"Adresa ili ime za pretragu", + "exp2":"Stanje na računu", + "exp3":"Više informacija", + "exp4":"Adresa ili ime nisu pronađeni!", + "exp5":"Imajte na umu da registrovana imena razlikuju velika i mala slova.", + "exp6":"Osnivač", + "exp7":"Info" } -} +} \ No newline at end of file diff --git a/qortal-ui-core/language/ru.json b/qortal-ui-core/language/ru.json index f2b49c40..897c0621 100644 --- a/qortal-ui-core/language/ru.json +++ b/qortal-ui-core/language/ru.json @@ -308,7 +308,20 @@ "tchange30":"Не удалось получить баланс. Повторите попытку!", "tchange31":"ПРОДАНО", "tchange32":"КУПИЛ", - "tchange33":"Обменный курс" + "tchange33":"Средний", + "tchange34":"Сумма не может быть 0", + "tchange35":"Цена не может быть 0", + "tchange36":"ОЖИДАНИЕ ПОКУПКИ АВТО", + "tchange37":"Заказ на автоматическую покупку не найден!", + "tchange38":"ДОБАВЛЯТЬ", + "tchange39":"АВТО КУПИТЬ ЗАКАЗ", + "tchange40":"Цена", + "tchange41":"Заказ на автоматическую покупку успешно удален!", + "tchange42":"РЫНОК ОТКРЫТЫЕ ЗАКАЗЫ НА ПРОДАЖУ", + "tchange43":"МОЯ ИСТОРИЯ ПОКУПОК", + "tchange44":"Заказ на автоматическую покупку успешно добавлен!", + "tchange45":"АВТО КУПИТЬ С", + "tchange46":"АВТО КУПИТЬ" }, "rewardsharepage":{ "rchange1":"Вознаграждения", @@ -674,5 +687,14 @@ "schange19":"Копировать спонсорский ключ", "schange20":"Создание отношений", "schange21":"Удалить спонсорский ключ" + }, + "explorerpage":{ + "exp1":"Адрес или имя для поиска", + "exp2":"Баланс", + "exp3":"Больше информации", + "exp4":"Адрес или имя не найдено!", + "exp5":"Обратите внимание, что зарегистрированные имена вводятся с учетом регистра.", + "exp6":"Основатель", + "exp7":"Информация" } -} +} \ No newline at end of file diff --git a/qortal-ui-core/language/us.json b/qortal-ui-core/language/us.json index 4ae2aa3b..022472d5 100644 --- a/qortal-ui-core/language/us.json +++ b/qortal-ui-core/language/us.json @@ -308,7 +308,20 @@ "tchange30":"Failed to Fetch Balance. Try again!", "tchange31":"SOLD", "tchange32":"BOUGHT", - "tchange33":"Exchange Rate" + "tchange33":"Average", + "tchange34":"Amount can not be 0", + "tchange35":"Price can not be 0", + "tchange36":"PENDING AUTO BUY", + "tchange37":"No auto buy order found !", + "tchange38":"ADD", + "tchange39":"AUTO BUY ORDER", + "tchange40":"Price", + "tchange41":"Successfully removed auto buy order!", + "tchange42":"MARKET OPEN SELL ORDERS", + "tchange43":"MY BUY HISTORY", + "tchange44":"Successfully added auto buy order!", + "tchange45":"AUTO BUY WITH", + "tchange46":"AUTO BUY" }, "rewardsharepage":{ "rchange1":"Rewardshares", @@ -674,5 +687,14 @@ "schange19":"Copy Sponsorship Key", "schange20":"Creating relationship", "schange21":"Remove Sponsorship Key" + }, + "explorerpage":{ + "exp1":"Address or name to search", + "exp2":"Account Balance", + "exp3":"More Info", + "exp4":"Address or Name not found !", + "exp5":"Note that registered names are case-sensitive.", + "exp6":"Founder", + "exp7":"Info" } } diff --git a/qortal-ui-core/language/zhc.json b/qortal-ui-core/language/zhc.json index ffe1f5d7..7d10ae01 100644 --- a/qortal-ui-core/language/zhc.json +++ b/qortal-ui-core/language/zhc.json @@ -308,7 +308,20 @@ "tchange30":"加载余额失败。请重新尝试!", "tchange31":"已卖出", "tchange32":"已买入", - "tchange33":"汇率" + "tchange33":"平均", + "tchange34":"金额不能为0", + "tchange35":"价格不能为0", + "tchange36":"待定汽车购买", + "tchange37":"未找到自动购买订单!", + "tchange38":"添加", + "tchange39":"自动购买订单", + "tchange40":"价格", + "tchange41":"成功删除自动购买订单!", + "tchange42":"开市卖单", + "tchange43":"我的购买历史", + "tchange44":"成功添加自动买单!", + "tchange45":"自动购买", + "tchange46":"自动购买" }, "rewardsharepage":{ "rchange1":"铸币密钥", @@ -674,5 +687,14 @@ "schange19":"复制赞助密钥", "schange20":"建立关系", "schange21":"删除赞助密钥" + }, + "explorerpage":{ + "exp1":"要搜索的地址或名称", + "exp2":"账户余额", + "exp3":"更多信息", + "exp4":"找不到地址或姓名!", + "exp5":"请注意,注册名称区分大小写。", + "exp6":"创始人", + "exp7":"信息" } -} +} \ No newline at end of file diff --git a/qortal-ui-core/language/zht.json b/qortal-ui-core/language/zht.json index ae241740..dc6d1a76 100644 --- a/qortal-ui-core/language/zht.json +++ b/qortal-ui-core/language/zht.json @@ -308,7 +308,20 @@ "tchange30":"加載餘額失敗。請重新嘗試!", "tchange31":"已賣出", "tchange32":"已買入", - "tchange33":"匯率" + "tchange33":"平均", + "tchange34":"金額不能為0", + "tchange35":"價格不能為0", + "tchange36":"待定汽車購買", + "tchange37":"未找到自動購買訂單!", + "tchange38":"添加", + "tchange39":"自動購買訂單", + "tchange40":"價格", + "tchange41":"成功刪除自動購買訂單!", + "tchange42":"開市賣單", + "tchange43":"我的購買歷史", + "tchange44":"成功添加自動買單!", + "tchange45":"自動購買", + "tchange46":"自動購買" }, "rewardsharepage":{ "rchange1":"鑄幣密鑰", @@ -674,5 +687,14 @@ "schange19":"複製贊助密鑰", "schange20":"建立關係", "schange21":"刪除贊助密鑰" + }, + "explorerpage":{ + "exp1":"要搜索的地址或名稱", + "exp2":"賬戶餘額", + "exp3":"更多信息", + "exp4":"找不到地址或姓名!", + "exp5":"請注意,註冊名稱區分大小寫。", + "exp6":"創始人", + "exp7":"信息" } -} +} \ No newline at end of file diff --git a/qortal-ui-core/package.json b/qortal-ui-core/package.json index b293210f..0e14cc33 100644 --- a/qortal-ui-core/package.json +++ b/qortal-ui-core/package.json @@ -1,6 +1,6 @@ { "name": "qortal-ui-core", - "version": "2.2.3", + "version": "2.2.4", "description": "QORTAL-UI Core", "keywords": [ "QORT", @@ -17,12 +17,12 @@ "author": "QORTAL ", "license": "GPL-3.0", "dependencies": { - "@hapi/hapi": "20.2.2", + "@hapi/hapi": "21.0.0", "@hapi/inert": "7.0.0", - "sass": "1.55.0" + "sass": "1.56.1" }, "devDependencies": { - "@babel/core": "7.19.3", + "@babel/core": "7.20.2", "@material/mwc-button": "0.27.0", "@material/mwc-checkbox": "0.27.0", "@material/mwc-dialog": "0.27.0", @@ -53,25 +53,25 @@ "@polymer/paper-spinner": "3.0.2", "@polymer/paper-toast": "3.0.1", "@polymer/paper-tooltip": "3.0.1", - "@rollup/plugin-alias": "4.0.0", - "@rollup/plugin-babel": "6.0.0", - "@rollup/plugin-commonjs": "23.0.0", - "@rollup/plugin-node-resolve": "15.0.0", - "@rollup/plugin-replace": "5.0.0", - "@vaadin/grid": "23.2.5", - "@vaadin/icons": "23.2.5", - "@vaadin/password-field": "23.2.5", + "@rollup/plugin-alias": "4.0.2", + "@rollup/plugin-babel": "6.0.2", + "@rollup/plugin-commonjs": "23.0.2", + "@rollup/plugin-node-resolve": "15.0.1", + "@rollup/plugin-replace": "5.0.1", + "@vaadin/grid": "23.2.8", + "@vaadin/icons": "23.2.8", + "@vaadin/password-field": "23.2.8", "asmcrypto.js": "2.3.2", "bcryptjs": "2.4.3", "epml": "0.3.3", "file-saver": "2.0.5", - "lit": "2.4.0", + "lit": "2.4.1", "lit-translate": "2.0.1", "postcss": "8.4.18", "pwa-helpers": "0.9.1", "random-sentence-generator": "0.0.8", "redux": "4.2.0", - "redux-thunk": "2.4.1", + "redux-thunk": "2.4.2", "rollup": "2.79.1", "rollup-plugin-node-globals": "1.4.0", "rollup-plugin-postcss": "4.0.2", diff --git a/qortal-ui-core/src/components/app-info.js b/qortal-ui-core/src/components/app-info.js index 06bedc8a..861f1fd6 100644 --- a/qortal-ui-core/src/components/app-info.js +++ b/qortal-ui-core/src/components/app-info.js @@ -114,7 +114,7 @@ class AppInfo extends connect(store)(LitElement) { setInterval(() => { this.getNodeInfo() this.getCoreInfo() - }, 60000) + }, 30000) } async getNodeInfo() { diff --git a/qortal-ui-core/src/components/app-view.js b/qortal-ui-core/src/components/app-view.js index 4010f1b0..43d962f0 100644 --- a/qortal-ui-core/src/components/app-view.js +++ b/qortal-ui-core/src/components/app-view.js @@ -7,6 +7,9 @@ import '@polymer/paper-icon-button/paper-icon-button.js' import '@polymer/iron-icons/iron-icons.js' import '@polymer/app-layout/app-layout.js' import '@polymer/paper-ripple' +import '@vaadin/icon' +import '@vaadin/icons' +import '@vaadin/text-field' import './wallet-profile.js' import './app-info.js' @@ -16,12 +19,14 @@ import './qort-theme-toggle.js' import './language-selector.js' import './settings-view/user-settings.js' import './logout-view/logout-view.js' +import './user-info-view/user-info-view.js' class AppView extends connect(store)(LitElement) { static get properties() { return { config: { type: Object }, - theme: { type: String, reflect: true } + theme: { type: String, reflect: true }, + searchContentString: { type: String } } } @@ -29,9 +34,17 @@ class AppView extends connect(store)(LitElement) { return [ css` * { - --mdc-theme-primary: rgb(3, 169, 244); - --mdc-theme-secondary: var(--mdc-theme-primary); - --mdc-theme-error: rgb(255, 89, 89); + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-secondary: var(--mdc-theme-primary); + --mdc-theme-error: rgb(255, 89, 89); + --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); } :host { @@ -57,6 +70,12 @@ class AppView extends connect(store)(LitElement) { border-top: var(--border); } + .search { + display: inline; + width: 50%; + align-items: center; + } + #sideBar { height: 100vh; display: flex; @@ -89,6 +108,7 @@ class AppView extends connect(store)(LitElement) { constructor() { super() + this.searchContentString = '' this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' } @@ -115,6 +135,19 @@ class AppView extends connect(store)(LitElement) { +
@@ -138,19 +171,36 @@ class AppView extends connect(store)(LitElement) { + ` } firstUpdated() { - // ... } stateChanged(state) { this.config = state.config } + searchKeyListener(e) { + if (e.key === 'Enter') { + this.openUserInfo() + } + } + + clearSearchField() { + this.shadowRoot.getElementById('searchContent').value = this.searchContentString + } + + openUserInfo() { + let sendInfoAddress = this.shadowRoot.getElementById('searchContent').value + const infoDialog = document.getElementById('main-app').shadowRoot.querySelector('app-view').shadowRoot.querySelector('user-info-view') + infoDialog.openUserInfo(sendInfoAddress) + this.clearSearchField() + } + openSettings() { const settingsDialog = document.getElementById('main-app').shadowRoot.querySelector('app-view').shadowRoot.querySelector('user-settings') settingsDialog.openSettings() diff --git a/qortal-ui-core/src/components/sidenav-menu.js b/qortal-ui-core/src/components/sidenav-menu.js index 75ee8a75..f36f0796 100644 --- a/qortal-ui-core/src/components/sidenav-menu.js +++ b/qortal-ui-core/src/components/sidenav-menu.js @@ -168,6 +168,18 @@ class SidenavMenu extends connect(store)(LitElement) { > + + + + + + + + + + + + ma.recipientAccount === address && ma.mintingAccount !== address); - - - const removeMintingAccount = async (publicKey) => { const url = `${nodeUrl}/admin/mintingaccounts?apiKey=${myNode.apiKey}`; return await fetch(url, { @@ -495,8 +491,6 @@ class StartMinting extends connect(store)(LitElement) { ` : '' }
- - ` : ""} diff --git a/qortal-ui-core/src/components/user-info-view/user-info-view.js b/qortal-ui-core/src/components/user-info-view/user-info-view.js new file mode 100644 index 00000000..0e22dc95 --- /dev/null +++ b/qortal-ui-core/src/components/user-info-view/user-info-view.js @@ -0,0 +1,320 @@ +import { LitElement, html, css } from 'lit' +import { connect } from 'pwa-helpers' +import { store } from '../../store.js' +import { doLogout } from '../../redux/app/app-actions.js' +import { translate, translateUnsafeHTML } from 'lit-translate' + +import '@polymer/paper-dialog/paper-dialog.js' +import '@material/mwc-button' + +class UserInfoView extends connect(store)(LitElement) { + static get properties() { + return { + theme: { type: String, reflect: true }, + infoAccountName: { type: String }, + imageUrl: { type: String }, + addressResult: { type: Array }, + nameAddressResult: { type: Array }, + displayAddress: { type: String }, + displayLevel: { type: String }, + displayBalance: { type: String } + } + } + + static get styles() { + return css` + * { + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-secondary: var(--mdc-theme-primary); + --mdc-theme-surface: var(--white); + --mdc-dialog-content-ink-color: var(--black); + box-sizing: border-box; + } + + h2 { + margin: 10px 0; + } + + h4 { + margin: 5px 0; + } + + p { + font-size: 14px; + line-height: 21px; + } + + .card-body { + background-color: var(--white); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + min-height: 100vh; + margin: 0; + } + + .card-container { + background-color: var(--white); + border-radius: 5px; + color: var(--black); + padding-top: 30px; + position: relative; + width: 350px; + max-width: 100%; + text-align: center; + } + + .card-container .level { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + left: 30px; + } + + .card-container .founder { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + right: 30px; + } + + .card-container .round { + width: 96px; + height: 96px; + border: 1px solid #03a9f4; + border-radius: 50%; + padding: 2px; + } + + .card-container .badge { + width: 200px; + height: 135px; + border: 1px solid transparent; + border-radius: 10%; + padding: 2px; + } + + .userdata { + background-color: #1F1A36; + text-align: left; + padding: 15px; + margin-top: 30px; + } + + .userdata ul { + list-style-type: none; + margin: 0; + padding: 0; + } + + .userdata ul li { + border: 1px solid #2D2747; + border-radius: 2px; + display: inline-block; + font-size: 12px; + margin: 0 7px 7px 0; + padding: 7px; + } + + .decline { + --mdc-theme-primary: var(--mdc-theme-error) + } + + .buttons { + text-align:right; + } + + ` + } + + constructor() { + super() + this.infoAccountName = '' + this.imageUrl = '' + this.addressResult = [] + this.nameAddressResult = [] + this.displayAddress = '' + this.displayLevel = '' + this.displayBalance = '' + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + } + + render() { + return html` + +
+ ${translate("mintingpage.mchange27")} ${this.displayLevel} + ${this.founderBadge()} + ${this.avatarImage()} +

${this.infoAccountName}

+

${this.displayAddress}

+

${translate("explorerpage.exp2")}: ${this.displayBalance} QORT

+
+
+ this.openMoreInfoDialog()}>${translate("explorerpage.exp3")} + this.closeInfoDialog()} dialog-dismiss>${translate("general.close")} +
+
+ + +
+ +

${translate("explorerpage.exp4")}

+

${translate("explorerpage.exp5")}

+
+
+ this.closeErrorDialog()} dialog-dismiss>${translate("general.close")} +
+
+ ` + } + + openUserInfo(userData) { + if (userData.startsWith('Q') && userData.length == 34) { + this.getAddressUserResult(userData) + } else { + this.getNameUserResult(userData) + } + } + + async getAddressUserResult(resultAddress) { + const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const addressUrl = `${nodeUrl}/addresses/${resultAddress}` + + await fetch(addressUrl).then(res => { + if (res.status === 400) { + this.shadowRoot.getElementById('userErrorDialog').open() + } else { + this.getAllWithAddress(resultAddress) + } + }) + } + + async getNameUserResult(resultName) { + const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const nameUrl = `${nodeUrl}/names/${resultName}` + + await fetch(nameUrl).then(res => { + if (res.status === 404) { + this.shadowRoot.getElementById('userErrorDialog').open() + } else { + this.getAddressFromName(resultName) + } + }) + } + + async getAddressFromName(fromName) { + const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const fromNameUrl = `${nodeUrl}/names/${fromName}` + + const qortalNameInfo = await fetch(fromNameUrl).then(response => { + return response.json() + }) + + this.nameAddressResult = qortalNameInfo + const nameAddress = this.nameAddressResult.owner + this.getAllWithAddress(nameAddress) + } + + async getAllWithAddress(myAddress) { + await this.getAddressUserInfo(myAddress) + await this.getAddressUserAvatar(myAddress) + await this.getAddressUserBalance(myAddress) + this.displayAddress = this.addressResult.address + this.displayLevel = this.addressResult.level + this.shadowRoot.getElementById('userInfoDialog').open() + } + + async getAddressUserInfo(infoAddress) { + const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const infoAddressUrl = `${nodeUrl}/addresses/${infoAddress}` + + const qortalAddressInfo = await fetch(infoAddressUrl).then(response => { + return response.json() + }) + + this.addressResult = qortalAddressInfo + } + + async getAddressUserAvatar(avatarAddress) { + const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const nameUrl = `${nodeUrl}/names/address/${avatarAddress}?limit=0&reverse=true` + + await fetch(nameUrl).then(res => { + return res.json() + }).then(jsonRes => { + if(jsonRes.length) { + jsonRes.map (item => { + this.infoAccountName = item.name + this.imageName = item.name + }) + } else { + this.infoAccountName = "No registered name" + this.imageName = avatarAddress + } + }) + + const myImageUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.imageName}/qortal_avatar?async=true&apiKey=${this.getApiKey()}` + this.imageUrl = myImageUrl + } + + async getAddressUserBalance(balanceAddress) { + const myNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const balanceAddressUrl = `${nodeUrl}/addresses/balance/${balanceAddress}` + + const qortalBalanceInfo = await fetch(balanceAddressUrl).then(res => { + return res.json() + }) + + this.displayBalance = qortalBalanceInfo + } + + avatarImage() { + return html`` + } + + founderBadge() { + if (this.addressResult.flags === 1) { + return html`${translate("explorerpage.exp6")}` + } else { + return html`` + } + } + + openMoreInfoDialog() { + this.shadowRoot.getElementById('userErrorDialog').open() + } + + closeInfoDialog() { + this.shadowRoot.getElementById('userInfoDialog').close() + } + + closeErrorDialog() { + this.shadowRoot.getElementById('userErrorDialog').close() + } + + getApiKey() { + const apiNode = store.getState().app.nodeConfig.knownNodes[store.getState().app.nodeConfig.node]; + let apiKey = apiNode.apiKey; + return apiKey; + } +} + +window.customElements.define('user-info-view', UserInfoView) \ No newline at end of file diff --git a/qortal-ui-crypto/package.json b/qortal-ui-crypto/package.json index b1814e00..a382e896 100644 --- a/qortal-ui-crypto/package.json +++ b/qortal-ui-crypto/package.json @@ -1,6 +1,6 @@ { "name": "qortal-ui-crypto", - "version": "2.2.3", + "version": "2.2.4", "description": "QORTAL-UI Crypto", "keywords": [ "QORT", diff --git a/qortal-ui-plugins/build-config.js b/qortal-ui-plugins/build-config.js index 2949988b..5595e9db 100644 --- a/qortal-ui-plugins/build-config.js +++ b/qortal-ui-plugins/build-config.js @@ -69,6 +69,18 @@ const generateForPlugins = () => { in: 'plugins/core/trade-portal/trade-portal.src.js', out: 'plugins/core/trade-portal/trade-portal.js', }, + { + in: 'plugins/core/trade-bot/trade-bot-btc/trade-bot-btc.src.js', + out: 'plugins/core/trade-bot/trade-bot-btc/trade-bot-btc.js', + }, + { + in: 'plugins/core/trade-bot/trade-bot-ltc/trade-bot-ltc.src.js', + out: 'plugins/core/trade-bot/trade-bot-ltc/trade-bot-ltc.js', + }, + { + in: 'plugins/core/trade-bot/trade-bot-doge/trade-bot-doge.src.js', + out: 'plugins/core/trade-bot/trade-bot-doge/trade-bot-doge.js', + }, { in: 'plugins/core/wallet/wallet-app.src.js', out: 'plugins/core/wallet/wallet-app.js', diff --git a/qortal-ui-plugins/package.json b/qortal-ui-plugins/package.json index 39a9a740..ce8ec663 100644 --- a/qortal-ui-plugins/package.json +++ b/qortal-ui-plugins/package.json @@ -1,6 +1,6 @@ { "name": "qortal-ui-plugins", - "version": "2.2.3", + "version": "2.2.4", "description": "QORTAL-UI Plugins", "keywords": [ "QORT", @@ -22,7 +22,7 @@ "emoji-picker-js": "https://github.com/Qortal/emoji-picker-js" }, "devDependencies": { - "@babel/core": "7.19.3", + "@babel/core": "7.20.2", "@github/time-elements": "3.1.2", "@material/mwc-button": "0.27.0", "@material/mwc-checkbox": "0.27.0", @@ -41,18 +41,18 @@ "@polymer/paper-slider": "3.0.1", "@polymer/paper-spinner": "3.0.2", "@polymer/paper-tooltip": "3.0.1", - "@rollup/plugin-alias": "4.0.0", - "@rollup/plugin-babel": "6.0.0", - "@rollup/plugin-commonjs": "23.0.0", - "@rollup/plugin-node-resolve": "15.0.0", - "@rollup/plugin-replace": "5.0.0", - "@vaadin/button": "23.2.5", - "@vaadin/grid": "23.2.5", - "@vaadin/icons": "23.2.5", + "@rollup/plugin-alias": "4.0.2", + "@rollup/plugin-babel": "6.0.2", + "@rollup/plugin-commonjs": "23.0.2", + "@rollup/plugin-node-resolve": "15.0.1", + "@rollup/plugin-replace": "5.0.1", + "@vaadin/button": "23.2.8", + "@vaadin/grid": "23.2.8", + "@vaadin/icons": "23.2.8", "epml": "0.3.3", "file-saver": "2.0.5", "html-escaper": "3.0.3", - "lit": "2.4.0", + "lit": "2.4.1", "lit-translate": "2.0.1", "rollup": "2.79.1", "rollup-plugin-node-globals": "1.4.0", diff --git a/qortal-ui-plugins/plugins/core/components/ChatHead.js b/qortal-ui-plugins/plugins/core/components/ChatHead.js index 4ec0fab2..418f9cb1 100644 --- a/qortal-ui-plugins/plugins/core/components/ChatHead.js +++ b/qortal-ui-plugins/plugins/core/components/ChatHead.js @@ -1,4 +1,5 @@ import { LitElement, html, css } from 'lit' +import { render } from 'lit/html.js' import { Epml } from '../../../epml.js' import '@material/mwc-icon' diff --git a/qortal-ui-plugins/plugins/core/main.src.js b/qortal-ui-plugins/plugins/core/main.src.js index 7c554a84..e30efd76 100644 --- a/qortal-ui-plugins/plugins/core/main.src.js +++ b/qortal-ui-plugins/plugins/core/main.src.js @@ -52,6 +52,33 @@ parentEpml.ready().then(() => { menus: [], parent: false, }, + { + url: 'trade-bot-btc', + domain: 'core', + page: 'trade-bot/trade-bot-btc/index.html', + title: 'Auto Buy With BTC', + icon: 'vaadin:automation', + menus: [], + parent: false, + }, + { + url: 'trade-bot-ltc', + domain: 'core', + page: 'trade-bot/trade-bot-ltc/index.html', + title: 'Auto Buy With LTC', + icon: 'vaadin:automation', + menus: [], + parent: false, + }, + { + url: 'trade-bot-doge', + domain: 'core', + page: 'trade-bot/trade-bot-doge/index.html', + title: 'Auto Buy With DOGE', + icon: 'vaadin:automation', + menus: [], + parent: false, + }, { url: 'reward-share', domain: 'core', diff --git a/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-btc/index.html b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-btc/index.html new file mode 100644 index 00000000..de1e0b41 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-btc/index.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-btc/trade-bot-btc.src.js b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-btc/trade-bot-btc.src.js new file mode 100644 index 00000000..e91c0e71 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-btc/trade-bot-btc.src.js @@ -0,0 +1,2155 @@ +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 '@material/mwc-button' +import '@material/mwc-textfield' +import '@material/mwc-icon' +import '@material/mwc-icon-button' +import '@material/mwc-dialog' +import '@material/mwc-tab-bar' +import '@material/mwc-tab' +import '@material/mwc-list/mwc-list-item' +import '@material/mwc-select' +import '@polymer/iron-icons/iron-icons.js' +import '@polymer/paper-icon-button/paper-icon-button.js' +import '@polymer/paper-spinner/paper-spinner-lite.js' +import '@vaadin/grid' +import '@vaadin/grid/vaadin-grid-sorter' + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +let workers = new Map() + +class TradeBotBTC extends LitElement { + static get properties() { + return { + selectedAddress: { type: Object }, + config: { type: Object }, + listedCoins: { type: Map }, + sellBtnDisable: { type: Boolean }, + isSellLoading: { type: Boolean }, + isBuyLoading: { type: Boolean }, + buyBtnDisable: { type: Boolean }, + autoBuyWarning: { type: Boolean }, + autoBuyBtnDisable: { type: Boolean }, + autoBuyBotDisable: { type: Boolean }, + initialAmount: { type: Number }, + cancelBtnDisable: { type: Boolean }, + cancelStuckOfferBtnDisable: { type: Boolean }, + selectedCoin: { type: String }, + isLoadingHistoricTrades: { type: Boolean }, + isLoadingOpenTrades: { type: Boolean }, + isLoadingMyOpenOrders: { type: Boolean }, + showGetWalletBance: { type: Boolean }, + showAddAutoBuy: { type: Boolean }, + theme: { type: String, reflect: true }, + btcWallet: { type: String }, + qortbtc: { type: Number }, + btcqort: { type: Number }, + tradeBotBtcBook: { type: Array }, + tradesBtcQortal: { type: Array }, + doneTradesBtcQortal: { type: Array }, + tradesAvailableBtcQortal: { type: Array }, + tradeBotBtcQortal: { type: Array }, + tradeBotAvailableBtcQortal: { type: Array }, + checkAlice: { type: String }, + botBuyAtAddress: { type: String }, + reAddAmount: { type: Number }, + reAddPrice: { type: Number } + } + } + + static get styles() { + return css` + * { + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-secondary: var(--mdc-theme-primary); + --mdc-theme-error: rgb(255, 89, 89); + --mdc-text-field-outlined-idle-border-color: var(--txtfieldborder); + --mdc-text-field-outlined-hover-border-color: var(--txtfieldhoverborder); + --mdc-text-field-label-ink-color: var(--black); + --mdc-text-field-ink-color: var(--black); + --mdc-select-outlined-idle-border-color: var(--txtfieldborder); + --mdc-select-outlined-hover-border-color: var(--txtfieldhoverborder); + --mdc-select-label-ink-color: var(--black); + --mdc-select-ink-color: var(--black); + --mdc-theme-surface: var(--white); + --mdc-dialog-content-ink-color: var(--black); + --mdc-dialog-shape-radius: 25px; + --paper-input-container-focus-color: var(--mdc-theme-primary); + --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); + --_lumo-grid-border-color: var(--border); + --_lumo-grid-secondary-border-color: var(--border2); + } + paper-spinner-lite { + height: 30px; + width: 30px; + --paper-spinner-color: var(--mdc-theme-primary); + --paper-spinner-stroke-width: 3px; + } + mwc-tab-bar { + --mdc-text-transform: none; + --mdc-tab-color-default: var(--black); + --mdc-tab-text-label-color-default: var(--black); + } + #tabs-1 { + --mdc-tab-height: 42px; + border-left: 1px solid var(--tradeborder); + border-top: 1px solid var(--tradeborder); + border-right: 1px solid var(--tradeborder); + color: var(--black); + } + #tab-buy[active] { + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + #tabs-1-content { + height: 100%; + padding-bottom: 10px; + } + #tabs-1-content > div { + height: 100%; + border: 1px solid var(--tradeborder); + } + #tabs-1-content .card { + border: none; + } + #tabs-1-content .btn-clear { + --mdc-icon-button-size: 32px; + color: var(--black); + } + .btn-clear-bot { + --mdc-icon-button-size: 32px; + color: var(--black); + float: right; + } + .btn-info { + color: var(--black); + --mdc-icon-size: 16px; + padding-top: 3px; + } + #tab-sell[active] { + --mdc-theme-primary: rgb(255, 89, 89); + } + #trade-portal-page { + background: var(--white); + padding: 12px 24px; + } + .divCard { + border: 1px solid var(--black); + padding: 1em; + box-shadow: 0 0.3px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 1px -1px rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.2); + } + h2 { + margin: 10px 0; + } + + h4 { + margin: 5px 0; + } + + p { + font-size: 14px; + line-height: 21px; + } + + .card-body { + background-color: var(--white); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + min-height: 100vh; + margin: 0; + } + + .card-container { + background-color: var(--white); + border-radius: 5px; + color: var(--black); + padding-top: 30px; + position: relative; + width: 350px; + max-width: 100%; + text-align: center; + } + + .card-container .level { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + left: 30px; + } + + .card-container .founder { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + right: 30px; + } + + .card-container .round { + width: 96px; + height: 96px; + border: 1px solid #03a9f4; + border-radius: 50%; + padding: 2px; + } + + .card-container .badge { + width: 200px; + height: 135px; + border: 1px solid transparent; + border-radius: 10%; + padding: 2px; + } + + .userdata { + background-color: #1F1A36; + text-align: left; + padding: 15px; + margin-top: 30px; + } + + .userdata ul { + list-style-type: none; + margin: 0; + padding: 0; + } + + .userdata ul li { + border: 1px solid #2D2747; + border-radius: 2px; + display: inline-block; + font-size: 12px; + margin: 0 7px 7px 0; + padding: 7px; + } + + h2, + h3, + h4, + h5 { + color: var(--black); + font-weight: 400; + } + header { + display: flex; + flex: 0 1 auto; + align-items: center; + justify-content: center; + padding: 0px 10px; + font-size: 16px; + color: var(--white); + background-color: var(--tradehead); + border-left: 1px solid var(--tradeborder); + border-top: 1px solid var(--tradeborder); + border-right: 1px solid var(--tradeborder); + min-height: 40px; + } + p { + margin-bottom: 12px; + } + #trade-portal { + max-width: 100vw; + margin-left: auto; + margin-right: auto; + } + .box { + margin: 0; + padding: 0; + display: flex; + flex-flow: column; + height: 100%; + } + .box-bot { + margin: 0; + padding: 0; + display: flex; + flex-flow: column; + height: 150px; + } + #first-trade-section { + margin-bottom: 10px; + } + #first-trade-section > div { + } + #second-trade-section { + margin-bottom: 10px; + } + #second-trade-section > div { + } + #third-trade-section { + margin-bottom: 10px; + } + #third-trade-section > div { + } + .trade-chart { + background-color: var(--white); + border: 2px #ddd solid; + text-align: center; + } + .open-trades { + text-align: center; + } + .open-market-container { + text-align: center; + } + .trade-bot-container { + text-align: center; + } + .no-last-seen { + background: rgb(255, 89, 89); + padding: 9px 1.3px; + border-radius: 50%; + width: 1rem; + margin: 0 auto; + } + .card { + padding: 1em; + border: 1px var(--tradeborder) solid; + flex: 1 1 auto; + display: flex; + flex-flow: column; + justify-content: space-evenly; + min-height: inherit; + } + .card-bot { + padding: 1em; + flex: 1 1 auto; + display: flex; + flex-flow: column; + justify-content: space-evenly; + width: 350px; + min-height: inherit; + } + .cancel { + --mdc-theme-primary: rgb(255, 89, 89); + } + .border-wrapper { + border: 1px var(--tradeborder) solid; + overflow: hidden; + } + .amt-text { + color: var(--tradehave); + font-size: 15px; + margin-top: 5px; + margin-bottom: 12px; + } + .exchange { + color: var(--black); + font-size: 18px; + font-weight: bold; + margin-top: 5px; + margin-bottom: 10px; + } + .clear-button { + display: inline; + float: right; + margin-bottom: 5px; + } + .exhcnage-text { + display: inline; + float: left; + margin-bottom: 5px; + } + .balance-text { + display: inline; + float: right; + margin-bottom: 5px; + } + .fee-text { + display: inline; + float: left; + margin-bottom: 5px; + } + .tab-text { + color: var(--tradehave); + font-size: 12px; + text-align: left; + margin-top: 2px; + margin-bottom: -12px; + } + .historic-trades { + text-align: center; + } + .my-open-orders { + text-align: center; + } + .my-historic-trades { + text-align: center; + } + .buttons { + width: auto !important; + } + .buy-button { + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + .sell-button { + --mdc-theme-primary: rgb(255, 89, 89); + } + .trade-bot-button { + margin-top: 20px; + margin-bottom: 20px; + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + .full-width { + background-color: var(--white); + border: 2px var(--black); + height: 200px; + text-align: center; + } + vaading-grid { + font-size: .8em; + } + vaadin-grid-column { + flex-grow: 1; + } + .loadingContainer { + height: 100%; + width: 100%; + } + .loading, + .loading:after { + border-radius: 50%; + width: 5em; + height: 5em; + } + .loading { + margin: 10px auto; + border-width: .6em; + border-style: solid; + border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2) rgb(3, 169, 244); + font-size: 10px; + position: relative; + text-indent: -9999em; + transform: translateZ(0px); + animation: 1.1s linear 0s infinite normal none running loadingAnimation; + } + mwc-select#coinSelectionMenu { + font-size: 24px; + width:220px; + } + mwc-select#coinSelectionMenu mwc-list-item { + line-height: 30px; + } + .coinName::before { + content: ""; + display: inline-block; + height: 26px; + width: 45px; + position: absolute; + background-repeat: no-repeat; + background-size: cover; + left: 10px; + top: 10px; + } + .coinName { + display: inline-block; + height: 26px; + padding-left: 45px; + } + .warning-text { + animation: blinker 1.5s linear infinite; + display: inline; + float: left; + margin-bottom: 5px; + color: rgb(255, 89, 89); + } + .warning-bot-text { + animation: blinker 1.5s linear infinite; + display: inline; + text-align: center; + color: rgb(255, 89, 89); + } + .red { + --mdc-theme-primary: #F44336; + } + @-webkit-keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + @keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + @keyframes blinker { + 50% { + opacity: 0; + } + } + @media (min-width: 701px) { + * { + } + #trade-portal {} + #first-trade-section { + display: grid; + grid-template-columns:1fr 4fr 1fr; + grid-auto-rows: max(350px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #second-trade-section { + display: grid; + grid-template-columns:1fr 4fr 1fr; + grid-auto-rows: max(350px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #third-trade-section { + display: grid; + grid-template-columns: 1fr 4fr 1fr; + grid-auto-rows: max(150px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #fourth-trade-section { + display: grid; + grid-template-columns: 1fr 4fr 1fr; + grid-auto-rows: max(150px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + } + ` + } + + constructor() { + super() + let qortal = { + name: "QORTAL", + balance: "0", + coinCode: "QORT", + coinAmount: this.amountString, + tradeFee: "0.002" + } + + let bitcoin = { + name: "BITCOIN", + balance: "0", + coinCode: "BTC", + openOrders: [], + openFilteredOrders: [], + historicTrades: [], + myOrders: [], + myHistoricTrades: [], + myOfferingOrders: [], + openTradeOrders: null, + tradeOffersSocketCounter: 1, + coinAmount: this.amountString, + tradeFee: "~0.0001" + } + + this.listedCoins = new Map() + this.listedCoins.set("QORTAL", qortal) + this.listedCoins.set("BITCOIN", bitcoin) + + workers.set("QORTAL", { + tradesConnectedWorker: null, + handleStuckTradesConnectedWorker: null + }) + + workers.set("BITCOIN", { + tradesConnectedWorker: null, + handleStuckTradesConnectedWorker: null + }) + + this.selectedCoin = "BITCOIN" + this.selectedAddress = {} + this.config = {} + this.sellBtnDisable = false + this.isSellLoading = false + this.buyBtnDisable = true + this.autoBuyWarning = false + this.autoBuyBtnDisable = true + this.autoBuyBotDisable = false + this.isBuyLoading = false + this.initialAmount = 0 + this.cancelBtnDisable = false + this.cancelStuckOfferBtnDisable = false + this.isLoadingHistoricTrades = true + this.isLoadingOpenTrades = true + this.isLoadingMyOpenOrders = false + this.showGetWalletBance = true + this.showAddAutoBuy = false + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.btcWallet = '' + this.qortbtc = 0 + this.btcqort = 0 + this.tradeBotBtcBook = [] + this.tradesBtcQortal = [] + this.doneTradesBtcQortal = [] + this.tradesAvailableBtcQortal = [] + this.tradeBotBtcQortal = [] + this.tradeBotAvailableBtcQortal = [] + this.checkAlice = '' + this.botBuyAtAddress = '' + this.reAddAmount = 0 + this.reAddPrice = 0 + } + + openTradesTemplate() { + return html` +
+
+
${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange42")}
+
+
${translate("login.loading")}
+ + { + render(html`${this.round(data.item.qortAmount)}`, root) + }} + > + + { + render(html`${this.round(data.item.price)}`, root) + }} + > + + { + render(html`${data.item.foreignAmount}`, root) + }} + > + + { + render(html`${data.item.qortalCreator}`, root) + }} + > + + +
+
+
+ ` + } + + myOpenOrdersTemplate() { + return html` +
+
+
${translate("tradepage.tchange36")}
+
+
${translate("login.loading")}
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + render(html` ${data.item._tradeState} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + + + +
+
+
+ ` + } + + myDoneTradesTemplate() { + return html` +
+
+
${translate("tradepage.tchange43")} (${this.listedCoins.get(this.selectedCoin).coinCode})
+
+
${translate("login.loading")}
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + return render(html` ${translate("tradepage.tchange32")} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + { + render(html` ${data.item.foreignAmount} `, root) + }} + > + + +
+
+
+ ` + } + + myHistoricTradesTemplate() { + return html` +
+
+
${translate("tradepage.tchange4")}
+
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + if (data.item.mode === 'SOLD') return render(html` ${translate("tradepage.tchange31")} `, root) + if (data.item.mode === 'BOUGHT') return render(html` ${translate("tradepage.tchange32")} `, root) + return render(html` ${data.item.mode} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + { + render(html` ${data.item.foreignAmount} `, root) + }} + > + + +
+
+
+ ` + } + + tradeBotBTCTemplate() { + return html` +
+
+
${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT
+
+ + { + render(html`${data.item.botBtcQortAmount}`, root) + }} + > + + { + render(html`${data.item.botBtcPrice}`, root) + }} + > + + { + const totalCoins = this.round(parseFloat(data.item.botBtcQortAmount) * parseFloat(data.item.botBtcPrice)) + const myBotFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myBotFunds) > Number(totalCoins)) { + this.autoBuyBotDisable = false + render(html`${totalCoins}`, root) + } else { + this.autoBuyBotDisable = true + render(html`${totalCoins}`, root) + } + }} + > + + { + render(html` this.removeBTCTradebook()}>delete ${translate("nodepage.nchange12")}`, root) + }} + > + + + ${this.isEmptyArray(this.tradeBotBtcBook) ? html` + + ${translate("tradepage.tchange38")} ${translate("tradepage.tchange39")} + + `: ''} +
+ ${this.autoBuyBotDisable ? html` +
${this.renderBotWarning()}
+ `: ''} +
+
+ ` + } + + render() { + return html` +
+
+
+
+ ${this.openTradesTemplate()} +
+
+
+
+ ${this.myDoneTradesTemplate()} +
+
+
+
+ ${this.myOpenOrdersTemplate()} +
+
+
+
+ ${this.showAutoBuyGrid()} +
+
+
+

${translate("tradepage.tchange33")} ${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange40")}

+

1 QORT = ${this.exchangeRateQort()} ${this.listedCoins.get(this.selectedCoin).coinCode}

+
+
+
+ + +
+

${this.renderFetchText()}

+
+
+
+

${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT

+
+
+
+
${this.renderWarning()}
+ +
+ ${translate("tradepage.tchange8")} (QORT)* +

+ + +

+ ${translate("tradepage.tchange14")} (${this.listedCoins.get(this.selectedCoin).coinCode})* +

+ + +

+ ${translate("tradepage.tchange10")} (${this.listedCoins.get(this.selectedCoin).coinCode})* +

+ + +

+ + ${translate("tradepage.tchange16")}: ${this.listedCoins.get(this.selectedCoin).balance} ${this.listedCoins.get(this.selectedCoin).coinCode} + +
+
+ + ${translate("tradepage.tchange38")} ${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange39")} + +
+
+
+ ${translate("general.close")} +
+ ` + } + + async firstUpdated() { + + let _this = this + + this.changeTheme() + this.changeLanguage() + await this.updateWalletBalance() + + this._openOrdersGrid = this.shadowRoot.getElementById('openOrdersGrid') + + this._openOrdersGrid.querySelector('#priceColumn').headerRenderer = function (root) { + const priceString = get("tradepage.tchange9") + root.innerHTML = '' + priceString + ' (' + _this.listedCoins.get(_this.selectedCoin).coinCode + ')' + } + + this._openOrdersGrid.querySelector('#qortAmountColumn').headerRenderer = function (root) { + const amountString = get("tradepage.tchange8") + root.innerHTML = '' + amountString + ' (QORT)' + } + + this._myOrdersGrid = this.shadowRoot.getElementById('myOrdersGrid') + this._myHistoricTradesGrid = this.shadowRoot.getElementById('myHistoricTradesGrid') + + const delay = ms => new Promise(res => setTimeout(res, ms)) + + const getQortBtcPrice = () => { + parentEpml.request("apiCall", { url: `/crosschain/price/BITCOIN?inverse=true` }).then((res) => { + setTimeout(() => { this.qortbtc = (Number(res) / 1e8).toFixed(8) }, 1) + }) + setTimeout(getQortBtcPrice, 300000) + } + + const filterMyPriceTradesBTC = async () => { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const tradeBotBtcQortalUrl = `${nodeUrl}/crosschain/tradebot?foreignBlockchain=BITCOIN&apiKey=${this.getApiKey()}` + const waitFor = ms => new Promise(res => setTimeout(res, ms)) + let timer + + const tradeBotBtcQortal = await fetch(tradeBotBtcQortalUrl).then(response => { + return response.json() + }) + + this.tradeBotBtcQortal = tradeBotBtcQortal + + await waitFor(1000) + + if (this.isEmptyArray(this.tradeBotBtcBook) === true) { + clearTimeout(timer) + timer = setTimeout(filterMyPriceTradesBTC, 180000) + return + } else { + clearTimeout(timer) + timer = setTimeout(filterMyPriceTradesBTC, 180000) + + this.tradeBotAvailableBtcQortal = this.listedCoins.get(this.selectedCoin).openFilteredOrders.map(item => { + const listprice = parseFloat(item.price) + const listamount = parseFloat(item.qortAmount) + const checkprice = parseFloat(this.tradeBotBtcBook[0].botBtcPrice) + const checkamount = parseFloat(this.tradeBotBtcBook[0].botBtcQortAmount) + if (Number(listprice) <= Number(checkprice) && Number(listamount) <= Number(checkamount)) { + return { + qortAmount: item.qortAmount, + price: item.price, + foreignAmount: item.foreignAmount, + qortalCreator: item.qortalCreator, + qortalAtAddress: item.qortalAtAddress + } + } + }).filter(item => !!item) + + this.tradeBotAvailableBtcQortal.sort((a, b) => parseFloat(a.price) - parseFloat(b.price)) + + if (this.isEmptyArray(this.tradeBotAvailableBtcQortal) === true) { + return + } else { + this.checkAlice = this.tradeBotAvailableBtcQortal[0].qortalAtAddress + } + + await waitFor(1000) + + if (this.tradeBotBtcQortal.some(item => item.atAddress === this.checkAlice)) { + return + } else { + this.tradesAvailableBtcQortal = this.tradeBotAvailableBtcQortal + } + } + + await waitFor(1000) + + if (this.isEmptyArray(this.tradesAvailableBtcQortal) === true) { + return + } else { + const botprice = this.round(parseFloat(this.tradeBotBtcBook[0].botBtcPrice)) + const changeamount = parseFloat(this.tradeBotBtcBook[0].botBtcQortAmount) + const reduceamount = parseFloat(this.tradesAvailableBtcQortal[0].qortAmount) + const tradeataddress = this.tradesAvailableBtcQortal[0].qortalAtAddress + const newamount = this.round(parseFloat(changeamount - reduceamount)) + + this.reAddAmount = this.round(parseFloat(this.tradeBotBtcBook[0].botBtcQortAmount)) + this.reAddPrice = this.round(parseFloat(this.tradeBotBtcBook[0].botBtcPrice)) + + localStorage.removeItem(this.btcWallet) + localStorage.setItem(this.btcWallet, "") + + var oldBtcTradebook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + + const newBtcTradebookItem = { + botBtcQortAmount: newamount, + botBtcPrice: botprice + } + + oldBtcTradebook.push(newBtcTradebookItem) + + localStorage.setItem(this.btcWallet, JSON.stringify(oldBtcTradebook)) + + this.tradeBotBtcBook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + + await waitFor(1000) + + this.botBuyAtAddress = tradeataddress + + this.buyAction() + + if (this.isEmptyArray(this.tradeBotBtcBook) === true) { + return + } else { + const botamount = parseFloat(this.tradeBotBtcBook[0].botBtcQortAmount) + + if (Number(botamount) === 0) { + this.removeBTCTradebook() + } else { + this.tradeBotBtcBook = this.tradeBotBtcBook + } + } + + if (this.isEmptyArray(this.tradeBotBtcBook) === true) { + return + } else { + const checkBotFunds = this.round(parseFloat(this.tradeBotBtcBook[0].botBtcQortAmount) * parseFloat(this.tradeBotBtcBook[0].botBtcPrice)) + const myBotFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + + if (Number(myBotFunds) < Number(checkBotFunds)) { + this.removeBTCTradebook() + } else { + this.tradeBotBtcBook = this.tradeBotBtcBook + } + } + } + } + + const getDoneTradeBTC = async () => { + this.isLoadingDoneTrades = true + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const doneBtcQortalUrl = `${nodeUrl}/crosschain/trades?foreignBlockchain=BITCOIN&minimumTimestamp=1597310000000&limit=0&reverse=true` + const myAddress = window.parent.reduxStore.getState().app.selectedAddress.address + + const doneBtcQortal = await fetch(doneBtcQortalUrl).then(response => { + return response.json() + }) + + this.doneTradesBtcQortal = doneBtcQortal.map(item => { + const searchAddress = item.buyerReceivingAddress + if (searchAddress == myAddress) { + return { + timestamp: item.tradeTimestamp, + foreignAmount: item.foreignAmount, + qortAmount: item.qortAmount + } + } + }).filter(item => !!item) + + this.isLoadingDoneTrades = false + setTimeout(getDoneTradeBTC, 600000) + } + + window.addEventListener('contextmenu', (event) => { + event.preventDefault() + this._textMenu(event)}, + { passive: true } + ) + + window.addEventListener('click', () => { + parentEpml.request('closeCopyTextMenu', null)}, + { passive: true } + ) + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + if (checkTheme === 'dark') { + this.theme = 'dark' + } else { + this.theme = 'light' + } + document.querySelector('html').setAttribute('theme', this.theme) + }) + + window.onkeyup = (e) => { + if (e.keyCode === 27) parentEpml.request('closeCopyTextMenu', null) + } + + this.btcWallet = window.parent.reduxStore.getState().app.selectedAddress.btcWallet.address + + let configLoaded = false + + parentEpml.ready().then(() => { + parentEpml.subscribe('selected_address', async (selectedAddress) => { + this.selectedAddress = {} + selectedAddress = JSON.parse(selectedAddress) + if (!selectedAddress || Object.entries(selectedAddress).length === 0) return + this.selectedAddress = selectedAddress + this.btcWwallet = window.parent.reduxStore.getState().app.selectedAddress.btcWallet.address + await this.updateAccountBalance() + await this.btcTradebook() + }) + + parentEpml.subscribe('config', (c) => { + if (!configLoaded) { + setTimeout(getQortBtcPrice, 1) + setTimeout(getDoneTradeBTC, 1) + configLoaded = true + } + this.config = JSON.parse(c) + }) + + parentEpml.subscribe('copy_menu_switch', async (value) => { + if (value === 'false' && window.getSelection().toString().length !== 0) this.clearSelection() + }) + this.setForeignCoin() + }) + parentEpml.imReady() + + setTimeout(() => this.shadowRoot.querySelector('[slot="vaadin-grid-cell-content-3"]').setAttribute('title', 'Last Seen'), 3000) + + this.btcTradebook() + await delay(3000) + filterMyPriceTradesBTC() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + if (checkTheme === 'dark') { + this.theme = 'dark'; + } else { + this.theme = '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) + } + } + + renderFetchText() { + return html`${translate("walletpage.wchange1")}` + } + + renderWarning() { + return html`NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode}` + } + + renderBotWarning() { + return html`NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode} ! AUTO BUY BOT IS STOPPED` + } + + exchangeRateQort() { + return html`${this.qortbtc}` + } + + showAutoBuyGrid() { + return html`${this.tradeBotBTCTemplate()}` + } + + showAddToAutoBuyStore() { + this.addToBTCTradebook() + } + + btcTradebook() { + if (localStorage.getItem(this.btcWallet) === null) { + localStorage.setItem(this.btcWallet, "") + } else { + this.tradeBotBtcBook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + } + } + + addToBTCTradebook() { + const addBotBtcQortAmount = this.shadowRoot.getElementById('autoBuyBTCQortAmountInput').value + const addBotBtcPrice = this.shadowRoot.getElementById('autoBuyBTCPriceInput').value + const addBtcQortAmount = this.round(parseFloat(addBotBtcQortAmount)) + const addBtcPrice = this.round(parseFloat(addBotBtcPrice)) + + var oldBtcTradebook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + + const newBtcTradebookItem = { + botBtcQortAmount: addBtcQortAmount, + botBtcPrice: addBtcPrice + } + + oldBtcTradebook.push(newBtcTradebookItem) + + localStorage.setItem(this.btcWallet, JSON.stringify(oldBtcTradebook)) + + let btctradebookstring = get("tradepage.tchange44") + parentEpml.request('showSnackBar', `${btctradebookstring}`) + + this.closeBTCTradebookDialog() + this.tradeBotBtcBook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + this.firstUpdated() + } + + closeBTCTradebookDialog() { + this.shadowRoot.querySelector('#tradeBotBTCAddDialog').close() + this.clearTradeBotForm() + } + + removeBTCTradebook() { + localStorage.removeItem(this.btcWallet) + localStorage.setItem(this.btcWallet, "") + this.tradeBotBtcBook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + + this.autoBuyBotDisable = false + + let btcstring = get("tradepage.tchange41") + parentEpml.request('showSnackBar', `${btcstring}`) + this.tradesAvailableBtcQortal = [] + } + + exchangeRateForeign() { + parentEpml.request('apiCall', { + url: `/crosschain/price/BITCOIN?inverse=false` + }).then((res) => { + this.btcqort = (Number(res) / 1e8).toFixed(8) + }) + return html`${this.btcqort}` + } + + async updateWalletBalance() { + let _url = `/crosschain/btc/walletbalance?apiKey=${this.getApiKey()}` + let _body = window.parent.reduxStore.getState().app.selectedAddress.btcWallet.derivedMasterPublicKey + + this.showGetWalletBance = true + this.showAddAutoBuy = false + + await parentEpml.request('apiCall', { + url: _url, + method: 'POST', + body: _body, + }).then((res) => { + if (isNaN(Number(res))) { + let snack1string = get("tradepage.tchange30") + parentEpml.request('showSnackBar', `${snack1string}`) + } else { + this.listedCoins.get(this.selectedCoin).balance = (Number(res) / 1e8).toFixed(8) + } + }) + + this.showGetWalletBance = false + this.showAddAutoBuy = true + } + + async setForeignCoin() { + let _this = this + this.selectedCoin = "BITCOIN" + + this.isLoadingOpenTrades = true + await this.createConnection() + + this._openOrdersGrid.querySelector('#priceColumn').headerRenderer = function (root) { + const priceString2 = get("tradepage.tchange9") + root.innerHTML = '' + priceString2 + ' (' + _this.listedCoins.get(_this.selectedCoin).coinCode + ')' + } + + this.autoBuyBotDisable = false + await this.updateWalletBalance() + } + + async reRenderOpenFilteredOrders() { + this.requestUpdate() + await this.updateComplete + this.isLoadingOpenTrades = false + } + + async reRenderMyOpenOrders() { + this.requestUpdate() + await this.updateComplete + this.isLoadingMyOpenOrders = false + } + + addAutoBuyAction() { + this.autoBuyWarning = false + this.clearTradeBotForm() + this.shadowRoot.querySelector('#tradeBot' + this.listedCoins.get(this.selectedCoin).coinCode + 'AddDialog').show() + } + + checkTradeBotValues() { + const checkTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const checkTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkAmount = this.round(parseFloat(checkTradeBotAmountInput)) + const checkPrice = this.round(parseFloat(checkTradeBotPriceInput)) + + if (Number(checkAmount) === 0) { + let amountString = get("tradepage.tchange34") + parentEpml.request('showSnackBar', `${amountString}`) + } else if (Number(checkPrice) === 0) { + let priceString = get("tradepage.tchange35") + parentEpml.request('showSnackBar', `${priceString}`) + } else { + this.showAddToAutoBuyStore() + } + } + + processOfferingTrade(offer) { + try { + if(this.listedCoins.get(offer.foreignBlockchain).name!='') { + const offerItem = { + ...offer, + qortAmount: parseFloat(offer.qortAmount), + price: parseFloat(offer.foreignAmount) / parseFloat(offer.qortAmount), + } + const addOffer = () => { + this.listedCoins.get(offer.foreignBlockchain).openOrders.unshift(offerItem) + } + const initOffer = () => { + this.listedCoins.get(offer.foreignBlockchain).openOrders.push(offerItem) + } + this.listedCoins.get(offer.foreignBlockchain).openOrders.length === 0 ? initOffer() : addOffer() + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + } catch(e) { + console.log("Error adding offer from "+offer.foreignBlockchain) + } + } + + processRedeemedTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + const offerItem = { + ...offer, + mode: 'SOLD', + } + this._myHistoricTradesGrid.items.unshift(offerItem) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } else if (offer.partnerQortalReceivingAddress === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + const offerItem = { + ...offer, + mode: 'BOUGHT', + } + this._myHistoricTradesGrid.items.unshift(offerItem) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + const addNewHistoricTrade = () => { + this._historicTradesGrid.items.unshift(offer) + this._historicTradesGrid.clearCache() + } + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? addNewHistoricTrade() : null + + } + } catch(e) { + console.log("Error processing redeemed trade offer from "+offer.foreignBlockchain) + } + } + + processTradingTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address && this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._openOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._openOrdersGrid.items.splice(index, 1) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + }) + this.listedCoins.get(offer.foreignBlockchain).openOrders = this.listedCoins.get(offer.foreignBlockchain).openOrders.filter((order) => order.qortalAtAddress !== offer.qortalAtAddress) + } + } catch(e) { + console.log("Error processing trading trade offer from "+offer.foreignBlockchain) + } + } + + processRefundedTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._myHistoricTradesGrid.items.unshift(offer) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + + } + } catch(e) { + console.log("Error processing refunded trade offer from "+offer.foreignBlockchain) + } + } + + processCancelledTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._myHistoricTradesGrid.items.unshift(offer) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + this._openOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._openOrdersGrid.items.splice(index, 1) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + }) + this.listedCoins.get(offer.foreignBlockchain).openOrders = this.listedCoins.get(offer.foreignBlockchain).openOrders.filter((order) => order.qortalAtAddress !== offer.qortalAtAddress) + this._stuckOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._stuckOrdersGrid.items.splice(index, 1) + this._stuckOrdersGrid.clearCache() + } + }) + } + } catch(e) { + console.log("Error processing cancelled trade offer from "+offer.foreignBlockchain) + } + } + + /** + * TRADE OFFER STATES or MODE + * - OFFERING + */ + + processTradeOffers(offers) { + offers.forEach((offer) => { + if (offer.mode === 'OFFERING') { + this.processOfferingTrade(offer) + } + }) + } + + processTradeBotStates(tradeStates) { + + const BitcoinACCTv1 = (states) => { + states.reverse() + states.forEach((state) => { + if (state.creatorAddress === this.selectedAddress.address) { + if (state.tradeState == 'ALICE_WAITING_FOR_AT_LOCK') { + this.changeTradeBotState(state, 'BUYING') + } else if (state.tradeState == 'ALICE_DONE') { + this.handleCompletedState(state) + } else if (state.tradeState == 'ALICE_REFUNDING_A') { + this.changeTradeBotState(state, 'REFUNDING') + } else if (state.tradeState == 'ALICE_REFUNDED') { + this.handleCompletedState(state) + } + } + }) + } + + switch (this.selectedCoin) { + case 'BITCOIN': + BitcoinACCTv1(tradeStates) + break + default: + break + } + } + + changeTradeBotState(state, tradeState) { + this.isLoadingMyOpenOrders = true + const stateItem = { + ...state, + _tradeState: tradeState, + } + const item = this._myOrdersGrid.querySelector(`#${state.atAddress}`) + const addStateItem = () => { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.unshift(stateItem) + this._myOrdersGrid.clearCache() + } + const updateStateItem = () => { + this._myOrdersGrid.items.forEach((item, index) => { + if (item.atAddress === state.atAddress) { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.splice(index, 1) + this._myOrdersGrid.items.unshift(stateItem) + this._myOrdersGrid.clearCache() + } + }) + } + item ? updateStateItem() : addStateItem() + } + + handleCompletedState(state) { + this._myOrdersGrid.items.forEach((item, index) => { + if (item.atAddress === state.atAddress) { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.splice(index, 1) + this._myOrdersGrid.clearCache() + } + }) + } + + initSocket() { + let _relatedCoin = "" + let tradePresenceTxns = null + let offeringTrades = null + + self.addEventListener('message', function (event) { + switch (event.data.type) { + case 'open_orders': + offeringTrades = event.data.content + processOffersWithPresence() + break + case 'set_coin': + _relatedCoin = event.data.content + break + default: + break + } + }) + + const lessThanThirtyMinsAgo = (timestamp) => { + const THIRTYMINS = 1000 * 60 * 30 + const thirtyMinsAgo = Date.now() - THIRTYMINS + return timestamp > thirtyMinsAgo + } + + const filterOffersUsingTradePresence = (offeringTrade) => { + return offeringTrade.tradePresenceExpiry > Date.now(); + } + + const processOffersWithPresence = () => { + if (offeringTrades === null) return + + async function asyncForEach(array, callback) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array) + } + } + + const startOfferPresenceMapping = async () => { + + if (tradePresenceTxns !== null) { + await asyncForEach(tradePresenceTxns, async (tradePresence) => { + let offerIndex = offeringTrades.findIndex((offeringTrade) => offeringTrade.qortalCreatorTradeAddress === tradePresence.tradeAddress) + offerIndex !== -1 ? (offeringTrades[offerIndex].tradePresenceExpiry = tradePresence.timestamp) : null + }) + } + + let filteredOffers = offeringTrades.filter((offeringTrade) => filterOffersUsingTradePresence(offeringTrade)) + self.postMessage({ type: 'PRESENCE', data: { offers: offeringTrades, filteredOffers: filteredOffers, relatedCoin: _relatedCoin } }) + } + + startOfferPresenceMapping() + } + + const initTradeOffersWebSocket = (restarted = false) => { + let tradeOffersSocketCounter = 0 + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradeoffers?foreignBlockchain=FOREIGN_BLOCKCHAIN&includeHistoric=true` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + tradeOffersSocketCounter += 1 + } + socket.onmessage = (e) => { + e.relatedCoin = _relatedCoin + self.postMessage({ + type: 'TRADE_OFFERS', + data: e.data, + counter: tradeOffersSocketCounter, + isRestarted: restarted, + }) + tradeOffersSocketCounter += 1 + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradeOffersWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const initTradeBotWebSocket = (restarted = false) => { + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradebot?foreignBlockchain=FOREIGN_BLOCKCHAIN` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + } + socket.onmessage = (e) => { + e.relatedCoin = _relatedCoin + self.postMessage({ + type: 'TRADE_BOT', + data: e.data, + isRestarted: restarted, + }) + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradeBotWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const initTradePresenceWebSocket = (restarted = false) => { + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradepresence` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + } + socket.onmessage = (e) => { + tradePresenceTxns = JSON.parse(e.data) + processOffersWithPresence() + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradePresenceWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const restartTradePresenceWebSocket = () => { + setTimeout(() => initTradePresenceWebSocket(true), 50) + } + + const restartTradeOffersWebSocket = () => { + setTimeout(() => initTradeOffersWebSocket(true), 50) + } + + const restartTradeBotWebSocket = () => { + setTimeout(() => initTradeBotWebSocket(true), 50) + } + + initTradeOffersWebSocket() + initTradePresenceWebSocket() + initTradeBotWebSocket() + } + + async buyAction() { + const qortalAtAddress = this.botBuyAtAddress + let _foreignKey = this.selectedAddress.btcWallet.derivedMasterPrivateKey + + const makeRequest = async () => { + const response = await parentEpml.request('tradeBotRespondRequest', { + atAddress: qortalAtAddress, + foreignKey: _foreignKey, + receivingAddress: this.selectedAddress.address, + }) + return response + } + + const manageResponse = (response) => { + if (response === true) { + this.isBuyLoading = false + this.buyBtnDisable = true + let snack5string = get("tradepage.tchange23") + parentEpml.request('showSnackBar', `${snack5string}`) + } else if (response === false) { + this.isBuyLoading = false + this.buyBtnDisable = false + + localStorage.removeItem(this.btcWallet) + localStorage.setItem(this.btcWallet, "") + + var oldBtcTradebook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + + const newBtcTradebookItem = { + botBtcQortAmount: this.reAddAmount, + botBtcPrice: this.reAddPrice + } + + oldBtcTradebook.push(newBtcTradebookItem) + + localStorage.setItem(this.btcWallet, JSON.stringify(oldBtcTradebook)) + + this.tradeBotBtcBook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + + let snack6string = get("tradepage.tchange24") + parentEpml.request('showSnackBar', `${snack6string}`) + } else { + this.isBuyLoading = false + this.buyBtnDisable = false + + localStorage.removeItem(this.btcWallet) + localStorage.setItem(this.btcWallet, "") + + var oldBtcTradebook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + + const newBtcTradebookItem = { + botBtcQortAmount: this.reAddAmount, + botBtcPrice: this.reAddPrice + } + + oldBtcTradebook.push(newBtcTradebookItem) + + localStorage.setItem(this.btcWallet, JSON.stringify(oldBtcTradebook)) + + this.tradeBotBtcBook = JSON.parse(localStorage.getItem(this.btcWallet) || "[]") + + let snack7string = get("tradepage.tchange25") + parentEpml.request('showSnackBar', `${snack7string}: ${response.message}`) + } + } + const res = await makeRequest() + manageResponse(res) + } + + updateAccountBalance() { + clearTimeout(this.updateAccountBalanceTimeout) + parentEpml.request('apiCall', { + url: `/addresses/balance/${this.selectedAddress.address}?apiKey=${this.getApiKey()}`, + }).then((res) => { + this.listedCoins.get("QORTAL").balance = res + this.updateAccountBalanceTimeout = setTimeout(() => this.updateAccountBalance(), 10000) + }) + } + + _checkBuyAmount(e) { + const targetAmount = e.target.value + const target = e.target + + if (targetAmount.length === 0) { + this.isValidAmount = false + this.sellBtnDisable = true + e.target.blur() + e.target.focus() + e.target.invalid = true + } else { + this.buyBtnDisable = false + } + + e.target.blur() + e.target.focus() + + e.target.validityTransform = (newValue, nativeValidity) => { + if (newValue.includes('-') === true) { + this.buyBtnDisable = true + return { + valid: false, + } + } else if (!nativeValidity.valid) { + if (newValue.includes('.') === true) { + let myAmount = newValue.split('.') + if (myAmount[1].length > 8) { + this.buyBtnDisable = true + } else { + this.buyBtnDisable = false + return { + valid: true, + } + } + } + } else { + this.buyBtnDisable = false + } + } + } + + checkTradeBotAmount(e) { + const targetAmount = e.target.value + const target = e.target + this.autoBuyWarning = false + + if (targetAmount.length === 0) { + this.isValidAmount = false + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + e.target.blur() + e.target.focus() + e.target.invalid = true + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + } + + e.target.blur() + e.target.focus() + + e.target.validityTransform = (newValue, nativeValidity) => { + if (newValue.includes('-') === true) { + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + return { + valid: false, + } + } else if (!nativeValidity.valid) { + if (newValue.includes('.') === true) { + let myAmount = newValue.split('.') + if (myAmount[1].length > 8) { + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + return { + valid: true, + } + } + } + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + } + } + } + + getApiKey() { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; + let apiKey = myNode.apiKey; + return apiKey; + } + + clearSelection() { + window.getSelection().removeAllRanges() + window.parent.getSelection().removeAllRanges() + } + + _textMenu(event) { + const getSelectedText = () => { + var text = '' + if (typeof window.getSelection != 'undefined') { + text = window.getSelection().toString() + } else if (typeof this.shadowRoot.selection != 'undefined' && this.shadowRoot.selection.type == 'Text') { + text = this.shadowRoot.selection.createRange().text + } + return text + } + + const checkSelectedTextAndShowMenu = () => { + let selectedText = getSelectedText() + if (selectedText && typeof selectedText === 'string') { + let _eve = { + pageX: event.pageX, + pageY: event.pageY, + clientX: event.clientX, + clientY: event.clientY, + } + let textMenuObject = { + selectedText: selectedText, + eventObject: _eve, + isFrame: true, + } + parentEpml.request('openCopyTextMenu', textMenuObject) + } + } + checkSelectedTextAndShowMenu() + } + + clearTradeBotForm() { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value = this.initialBotAmount + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value = this.initialAmount + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.initialBotAmount + this.autoBuyBtnDisable = true + } + + isEmptyArray(arr) { + if (!arr) { + return true + } + return arr.length === 0 + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } + + inlineWorker(passedFunction, modifiers) { + let parsedFunction = `` + + modifiers.forEach((modifier) => { + let regex = new RegExp(modifier.searchValue, 'g') + parsedFunction = parsedFunction.length === 0 ? `(function ${passedFunction.toString().trim().replace(regex, modifier.replaceValue)})()` : parsedFunction.toString().trim().replace(regex, modifier.replaceValue) + }) + + const workerUrl = URL.createObjectURL(new Blob([parsedFunction], { type: 'text/javascript' })) + const worker = new Worker(workerUrl) + URL.revokeObjectURL(workerUrl) + return worker + } + + clearPaneCache() { + this._openOrdersGrid.clearCache() + this._myOrdersGrid.clearCache() + } + + createConnection() { + if (workers.get(this.selectedCoin).tradesConnectedWorker !== null) { + this.isLoadingOpenTrades = false + return + } + + const handleMessage = (message) => { + switch (message.type) { + case 'TRADE_OFFERS': + if (!message.isRestarted) { + this.listedCoins.get(this.selectedCoin).tradeOffersSocketCounter = message.counter + this.processTradeOffers(JSON.parse(message.data)) + this.listedCoins.get(this.selectedCoin).tradeOffersSocketCounter === 1 ? this.clearPaneCache() : null + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage(this.listedCoins.get(this.selectedCoin).openOrders) + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage({ type: "open_orders", content: this.listedCoins.get(this.selectedCoin).openOrders }) + } + return null + case 'TRADE_BOT': + if (!message.isRestarted) this.processTradeBotStates(JSON.parse(message.data)) + return null + case 'PRESENCE': + this.listedCoins.get(message.data.relatedCoin).openOrders = message.data.offers + this.listedCoins.get(message.data.relatedCoin).openFilteredOrders = message.data.filteredOffers + this.reRenderOpenFilteredOrders() + return null + default: + break + } + } + + let myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + let nodeUrl = myNode.domain + ':' + myNode.port + + const modifiers = [ + { searchValue: 'NODEURL', replaceValue: nodeUrl }, + { searchValue: 'FOREIGN_BLOCKCHAIN', replaceValue: this.selectedCoin }, + ] + + workers.get(this.selectedCoin).tradesConnectedWorker = this.inlineWorker(this.initSocket, modifiers) + + workers.get(this.selectedCoin).tradesConnectedWorker.addEventListener('message', function (event) { handleMessage(event.data) }, { passive: true }) + + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage({ type: "set_coin", content: this.selectedCoin }) + + } +} + +window.customElements.define('trade-bot-btc', TradeBotBTC) \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-doge/index.html b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-doge/index.html new file mode 100644 index 00000000..a02e4a70 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-doge/index.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-doge/trade-bot-doge.src.js b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-doge/trade-bot-doge.src.js new file mode 100644 index 00000000..d76e1d4f --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-doge/trade-bot-doge.src.js @@ -0,0 +1,2155 @@ +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 '@material/mwc-button' +import '@material/mwc-textfield' +import '@material/mwc-icon' +import '@material/mwc-icon-button' +import '@material/mwc-dialog' +import '@material/mwc-tab-bar' +import '@material/mwc-tab' +import '@material/mwc-list/mwc-list-item' +import '@material/mwc-select' +import '@polymer/iron-icons/iron-icons.js' +import '@polymer/paper-icon-button/paper-icon-button.js' +import '@polymer/paper-spinner/paper-spinner-lite.js' +import '@vaadin/grid' +import '@vaadin/grid/vaadin-grid-sorter' + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +let workers = new Map() + +class TradeBotDOGE extends LitElement { + static get properties() { + return { + selectedAddress: { type: Object }, + config: { type: Object }, + listedCoins: { type: Map }, + sellBtnDisable: { type: Boolean }, + isSellLoading: { type: Boolean }, + isBuyLoading: { type: Boolean }, + buyBtnDisable: { type: Boolean }, + autoBuyWarning: { type: Boolean }, + autoBuyBtnDisable: { type: Boolean }, + autoBuyBotDisable: { type: Boolean }, + initialAmount: { type: Number }, + cancelBtnDisable: { type: Boolean }, + cancelStuckOfferBtnDisable: { type: Boolean }, + selectedCoin: { type: String }, + isLoadingHistoricTrades: { type: Boolean }, + isLoadingOpenTrades: { type: Boolean }, + isLoadingMyOpenOrders: { type: Boolean }, + showGetWalletBance: { type: Boolean }, + showAddAutoBuy: { type: Boolean }, + theme: { type: String, reflect: true }, + dogeWallet: { type: String }, + qortdoge: { type: Number }, + dogeqort: { type: Number }, + tradeBotDogeBook: { type: Array }, + tradesDogeQortal: { type: Array }, + doneTradesDogeQortal: { type: Array }, + tradesAvailableDogeQortal: { type: Array }, + tradeBotDogeQortal: { type: Array }, + tradeBotAvailableDogeQortal: { type: Array }, + checkAlice: { type: String }, + botBuyAtAddress: { type: String }, + reAddAmount: { type: Number }, + reAddPrice: { type: Number } + } + } + + static get styles() { + return css` + * { + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-secondary: var(--mdc-theme-primary); + --mdc-theme-error: rgb(255, 89, 89); + --mdc-text-field-outlined-idle-border-color: var(--txtfieldborder); + --mdc-text-field-outlined-hover-border-color: var(--txtfieldhoverborder); + --mdc-text-field-label-ink-color: var(--black); + --mdc-text-field-ink-color: var(--black); + --mdc-select-outlined-idle-border-color: var(--txtfieldborder); + --mdc-select-outlined-hover-border-color: var(--txtfieldhoverborder); + --mdc-select-label-ink-color: var(--black); + --mdc-select-ink-color: var(--black); + --mdc-theme-surface: var(--white); + --mdc-dialog-content-ink-color: var(--black); + --mdc-dialog-shape-radius: 25px; + --paper-input-container-focus-color: var(--mdc-theme-primary); + --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); + --_lumo-grid-border-color: var(--border); + --_lumo-grid-secondary-border-color: var(--border2); + } + paper-spinner-lite { + height: 30px; + width: 30px; + --paper-spinner-color: var(--mdc-theme-primary); + --paper-spinner-stroke-width: 3px; + } + mwc-tab-bar { + --mdc-text-transform: none; + --mdc-tab-color-default: var(--black); + --mdc-tab-text-label-color-default: var(--black); + } + #tabs-1 { + --mdc-tab-height: 42px; + border-left: 1px solid var(--tradeborder); + border-top: 1px solid var(--tradeborder); + border-right: 1px solid var(--tradeborder); + color: var(--black); + } + #tab-buy[active] { + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + #tabs-1-content { + height: 100%; + padding-bottom: 10px; + } + #tabs-1-content > div { + height: 100%; + border: 1px solid var(--tradeborder); + } + #tabs-1-content .card { + border: none; + } + #tabs-1-content .btn-clear { + --mdc-icon-button-size: 32px; + color: var(--black); + } + .btn-clear-bot { + --mdc-icon-button-size: 32px; + color: var(--black); + float: right; + } + .btn-info { + color: var(--black); + --mdc-icon-size: 16px; + padding-top: 3px; + } + #tab-sell[active] { + --mdc-theme-primary: rgb(255, 89, 89); + } + #trade-portal-page { + background: var(--white); + padding: 12px 24px; + } + .divCard { + border: 1px solid var(--black); + padding: 1em; + box-shadow: 0 0.3px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 1px -1px rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.2); + } + h2 { + margin: 10px 0; + } + + h4 { + margin: 5px 0; + } + + p { + font-size: 14px; + line-height: 21px; + } + + .card-body { + background-color: var(--white); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + min-height: 100vh; + margin: 0; + } + + .card-container { + background-color: var(--white); + border-radius: 5px; + color: var(--black); + padding-top: 30px; + position: relative; + width: 350px; + max-width: 100%; + text-align: center; + } + + .card-container .level { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + left: 30px; + } + + .card-container .founder { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + right: 30px; + } + + .card-container .round { + width: 96px; + height: 96px; + border: 1px solid #03a9f4; + border-radius: 50%; + padding: 2px; + } + + .card-container .badge { + width: 200px; + height: 135px; + border: 1px solid transparent; + border-radius: 10%; + padding: 2px; + } + + .userdata { + background-color: #1F1A36; + text-align: left; + padding: 15px; + margin-top: 30px; + } + + .userdata ul { + list-style-type: none; + margin: 0; + padding: 0; + } + + .userdata ul li { + border: 1px solid #2D2747; + border-radius: 2px; + display: inline-block; + font-size: 12px; + margin: 0 7px 7px 0; + padding: 7px; + } + + h2, + h3, + h4, + h5 { + color: var(--black); + font-weight: 400; + } + header { + display: flex; + flex: 0 1 auto; + align-items: center; + justify-content: center; + padding: 0px 10px; + font-size: 16px; + color: var(--white); + background-color: var(--tradehead); + border-left: 1px solid var(--tradeborder); + border-top: 1px solid var(--tradeborder); + border-right: 1px solid var(--tradeborder); + min-height: 40px; + } + p { + margin-bottom: 12px; + } + #trade-portal { + max-width: 100vw; + margin-left: auto; + margin-right: auto; + } + .box { + margin: 0; + padding: 0; + display: flex; + flex-flow: column; + height: 100%; + } + .box-bot { + margin: 0; + padding: 0; + display: flex; + flex-flow: column; + height: 150px; + } + #first-trade-section { + margin-bottom: 10px; + } + #first-trade-section > div { + } + #second-trade-section { + margin-bottom: 10px; + } + #second-trade-section > div { + } + #third-trade-section { + margin-bottom: 10px; + } + #third-trade-section > div { + } + .trade-chart { + background-color: var(--white); + border: 2px #ddd solid; + text-align: center; + } + .open-trades { + text-align: center; + } + .open-market-container { + text-align: center; + } + .trade-bot-container { + text-align: center; + } + .no-last-seen { + background: rgb(255, 89, 89); + padding: 9px 1.3px; + border-radius: 50%; + width: 1rem; + margin: 0 auto; + } + .card { + padding: 1em; + border: 1px var(--tradeborder) solid; + flex: 1 1 auto; + display: flex; + flex-flow: column; + justify-content: space-evenly; + min-height: inherit; + } + .card-bot { + padding: 1em; + flex: 1 1 auto; + display: flex; + flex-flow: column; + justify-content: space-evenly; + width: 350px; + min-height: inherit; + } + .cancel { + --mdc-theme-primary: rgb(255, 89, 89); + } + .border-wrapper { + border: 1px var(--tradeborder) solid; + overflow: hidden; + } + .amt-text { + color: var(--tradehave); + font-size: 15px; + margin-top: 5px; + margin-bottom: 12px; + } + .exchange { + color: var(--black); + font-size: 18px; + font-weight: bold; + margin-top: 5px; + margin-bottom: 10px; + } + .clear-button { + display: inline; + float: right; + margin-bottom: 5px; + } + .exhcnage-text { + display: inline; + float: left; + margin-bottom: 5px; + } + .balance-text { + display: inline; + float: right; + margin-bottom: 5px; + } + .fee-text { + display: inline; + float: left; + margin-bottom: 5px; + } + .tab-text { + color: var(--tradehave); + font-size: 12px; + text-align: left; + margin-top: 2px; + margin-bottom: -12px; + } + .historic-trades { + text-align: center; + } + .my-open-orders { + text-align: center; + } + .my-historic-trades { + text-align: center; + } + .buttons { + width: auto !important; + } + .buy-button { + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + .sell-button { + --mdc-theme-primary: rgb(255, 89, 89); + } + .trade-bot-button { + margin-top: 20px; + margin-bottom: 20px; + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + .full-width { + background-color: var(--white); + border: 2px var(--black); + height: 200px; + text-align: center; + } + vaading-grid { + font-size: .8em; + } + vaadin-grid-column { + flex-grow: 1; + } + .loadingContainer { + height: 100%; + width: 100%; + } + .loading, + .loading:after { + border-radius: 50%; + width: 5em; + height: 5em; + } + .loading { + margin: 10px auto; + border-width: .6em; + border-style: solid; + border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2) rgb(3, 169, 244); + font-size: 10px; + position: relative; + text-indent: -9999em; + transform: translateZ(0px); + animation: 1.1s linear 0s infinite normal none running loadingAnimation; + } + mwc-select#coinSelectionMenu { + font-size: 24px; + width:220px; + } + mwc-select#coinSelectionMenu mwc-list-item { + line-height: 30px; + } + .coinName::before { + content: ""; + display: inline-block; + height: 26px; + width: 45px; + position: absolute; + background-repeat: no-repeat; + background-size: cover; + left: 10px; + top: 10px; + } + .coinName { + display: inline-block; + height: 26px; + padding-left: 45px; + } + .warning-text { + animation: blinker 1.5s linear infinite; + display: inline; + float: left; + margin-bottom: 5px; + color: rgb(255, 89, 89); + } + .warning-bot-text { + animation: blinker 1.5s linear infinite; + display: inline; + text-align: center; + color: rgb(255, 89, 89); + } + .red { + --mdc-theme-primary: #F44336; + } + @-webkit-keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + @keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + @keyframes blinker { + 50% { + opacity: 0; + } + } + @media (min-width: 701px) { + * { + } + #trade-portal {} + #first-trade-section { + display: grid; + grid-template-columns:1fr 4fr 1fr; + grid-auto-rows: max(350px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #second-trade-section { + display: grid; + grid-template-columns:1fr 4fr 1fr; + grid-auto-rows: max(350px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #third-trade-section { + display: grid; + grid-template-columns: 1fr 4fr 1fr; + grid-auto-rows: max(150px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #fourth-trade-section { + display: grid; + grid-template-columns: 1fr 4fr 1fr; + grid-auto-rows: max(150px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + } + ` + } + + constructor() { + super() + let qortal = { + name: "QORTAL", + balance: "0", + coinCode: "QORT", + coinAmount: this.amountString, + tradeFee: "0.002" + } + + let dogecoin = { + name: "DOGECOIN", + balance: "0", + coinCode: "DOGE", + openOrders: [], + openFilteredOrders: [], + historicTrades: [], + myOrders: [], + myHistoricTrades: [], + myOfferingOrders: [], + openTradeOrders: null, + tradeOffersSocketCounter: 1, + coinAmount: this.amountString, + tradeFee: "~0.005" + } + + this.listedCoins = new Map() + this.listedCoins.set("QORTAL", qortal) + this.listedCoins.set("DOGECOIN", dogecoin) + + workers.set("QORTAL", { + tradesConnectedWorker: null, + handleStuckTradesConnectedWorker: null + }) + + workers.set("DOGECOIN", { + tradesConnectedWorker: null, + handleStuckTradesConnectedWorker: null + }) + + this.selectedCoin = "DOGECOIN" + this.selectedAddress = {} + this.config = {} + this.sellBtnDisable = false + this.isSellLoading = false + this.buyBtnDisable = true + this.autoBuyWarning = false + this.autoBuyBtnDisable = true + this.autoBuyBotDisable = false + this.isBuyLoading = false + this.initialAmount = 0 + this.cancelBtnDisable = false + this.cancelStuckOfferBtnDisable = false + this.isLoadingHistoricTrades = true + this.isLoadingOpenTrades = true + this.isLoadingMyOpenOrders = false + this.showGetWalletBance = true + this.showAddAutoBuy = false + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.dogeWallet = '' + this.qortdoge = 0 + this.dogeqort = 0 + this.tradeBotDogeBook = [] + this.tradesDogeQortal = [] + this.doneTradesDogeQortal = [] + this.tradesAvailableDogeQortal = [] + this.tradeBotDogeQortal = [] + this.tradeBotAvailableDogeQortal = [] + this.checkAlice = '' + this.botBuyAtAddress = '' + this.reAddAmount = 0 + this.reAddPrice = 0 + } + + openTradesTemplate() { + return html` +
+
+
${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange42")}
+
+
${translate("login.loading")}
+ + { + render(html`${this.round(data.item.qortAmount)}`, root) + }} + > + + { + render(html`${this.round(data.item.price)}`, root) + }} + > + + { + render(html`${data.item.foreignAmount}`, root) + }} + > + + { + render(html`${data.item.qortalCreator}`, root) + }} + > + + +
+
+
+ ` + } + + myOpenOrdersTemplate() { + return html` +
+
+
${translate("tradepage.tchange36")}
+
+
${translate("login.loading")}
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + render(html` ${data.item._tradeState} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + + + +
+
+
+ ` + } + + myDoneTradesTemplate() { + return html` +
+
+
${translate("tradepage.tchange43")} (${this.listedCoins.get(this.selectedCoin).coinCode})
+
+
${translate("login.loading")}
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + return render(html` ${translate("tradepage.tchange32")} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + { + render(html` ${data.item.foreignAmount} `, root) + }} + > + + +
+
+
+ ` + } + + myHistoricTradesTemplate() { + return html` +
+
+
${translate("tradepage.tchange4")}
+
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + if (data.item.mode === 'SOLD') return render(html` ${translate("tradepage.tchange31")} `, root) + if (data.item.mode === 'BOUGHT') return render(html` ${translate("tradepage.tchange32")} `, root) + return render(html` ${data.item.mode} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + { + render(html` ${data.item.foreignAmount} `, root) + }} + > + + +
+
+
+ ` + } + + tradeBotDOGETemplate() { + return html` +
+
+
${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT
+
+ + { + render(html`${data.item.botDogeQortAmount}`, root) + }} + > + + { + render(html`${data.item.botDogePrice}`, root) + }} + > + + { + const totalCoins = this.round(parseFloat(data.item.botDogeQortAmount) * parseFloat(data.item.botDogePrice)) + const myBotFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myBotFunds) > Number(totalCoins)) { + this.autoBuyBotDisable = false + render(html`${totalCoins}`, root) + } else { + this.autoBuyBotDisable = true + render(html`${totalCoins}`, root) + } + }} + > + + { + render(html` this.removeDOGETradebook()}>delete ${translate("nodepage.nchange12")}`, root) + }} + > + + + ${this.isEmptyArray(this.tradeBotDogeBook) ? html` + + ${translate("tradepage.tchange38")} ${translate("tradepage.tchange39")} + + `: ''} +
+ ${this.autoBuyBotDisable ? html` +
${this.renderBotWarning()}
+ `: ''} +
+
+ ` + } + + render() { + return html` +
+
+
+
+ ${this.openTradesTemplate()} +
+
+
+
+ ${this.myDoneTradesTemplate()} +
+
+
+
+ ${this.myOpenOrdersTemplate()} +
+
+
+
+ ${this.showAutoBuyGrid()} +
+
+
+

${translate("tradepage.tchange33")} ${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange40")}

+

1 QORT = ${this.exchangeRateQort()} ${this.listedCoins.get(this.selectedCoin).coinCode}

+
+
+
+ + +
+

${this.renderFetchText()}

+
+
+
+

${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT

+
+
+
+
${this.renderWarning()}
+ +
+ ${translate("tradepage.tchange8")} (QORT)* +

+ + +

+ ${translate("tradepage.tchange14")} (${this.listedCoins.get(this.selectedCoin).coinCode})* +

+ + +

+ ${translate("tradepage.tchange10")} (${this.listedCoins.get(this.selectedCoin).coinCode})* +

+ + +

+ + ${translate("tradepage.tchange16")}: ${this.listedCoins.get(this.selectedCoin).balance} ${this.listedCoins.get(this.selectedCoin).coinCode} + +
+
+ + ${translate("tradepage.tchange38")} ${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange39")} + +
+
+
+ ${translate("general.close")} +
+ ` + } + + async firstUpdated() { + + let _this = this + + this.changeTheme() + this.changeLanguage() + await this.updateWalletBalance() + + this._openOrdersGrid = this.shadowRoot.getElementById('openOrdersGrid') + + this._openOrdersGrid.querySelector('#priceColumn').headerRenderer = function (root) { + const priceString = get("tradepage.tchange9") + root.innerHTML = '' + priceString + ' (' + _this.listedCoins.get(_this.selectedCoin).coinCode + ')' + } + + this._openOrdersGrid.querySelector('#qortAmountColumn').headerRenderer = function (root) { + const amountString = get("tradepage.tchange8") + root.innerHTML = '' + amountString + ' (QORT)' + } + + this._myOrdersGrid = this.shadowRoot.getElementById('myOrdersGrid') + this._myHistoricTradesGrid = this.shadowRoot.getElementById('myHistoricTradesGrid') + + const delay = ms => new Promise(res => setTimeout(res, ms)) + + const getQortDogePrice = () => { + parentEpml.request("apiCall", { url: `/crosschain/price/DOGECOIN?inverse=true` }).then((res) => { + setTimeout(() => { this.qortdoge = (Number(res) / 1e8).toFixed(8) }, 1) + }) + setTimeout(getQortDogePrice, 300000) + } + + const filterMyPriceTradesDOGE = async () => { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const tradeBotDogeQortalUrl = `${nodeUrl}/crosschain/tradebot?foreignBlockchain=DOGECOIN&apiKey=${this.getApiKey()}` + const waitFor = ms => new Promise(res => setTimeout(res, ms)) + let timer + + const tradeBotDogeQortal = await fetch(tradeBotDogeQortalUrl).then(response => { + return response.json() + }) + + this.tradeBotDogeQortal = tradeBotDogeQortal + + await waitFor(1000) + + if (this.isEmptyArray(this.tradeBotDogeBook) === true) { + clearTimeout(timer) + timer = setTimeout(filterMyPriceTradesDOGE, 180000) + return + } else { + clearTimeout(timer) + timer = setTimeout(filterMyPriceTradesDOGE, 180000) + + this.tradeBotAvailableDogeQortal = this.listedCoins.get(this.selectedCoin).openFilteredOrders.map(item => { + const listprice = parseFloat(item.price) + const listamount = parseFloat(item.qortAmount) + const checkprice = parseFloat(this.tradeBotDogeBook[0].botDogePrice) + const checkamount = parseFloat(this.tradeBotDogeBook[0].botDogeQortAmount) + if (Number(listprice) <= Number(checkprice) && Number(listamount) <= Number(checkamount)) { + return { + qortAmount: item.qortAmount, + price: item.price, + foreignAmount: item.foreignAmount, + qortalCreator: item.qortalCreator, + qortalAtAddress: item.qortalAtAddress + } + } + }).filter(item => !!item) + + this.tradeBotAvailableDogeQortal.sort((a, b) => parseFloat(a.price) - parseFloat(b.price)) + + if (this.isEmptyArray(this.tradeBotAvailableDogeQortal) === true) { + return + } else { + this.checkAlice = this.tradeBotAvailableDogeQortal[0].qortalAtAddress + } + + await waitFor(1000) + + if (this.tradeBotDogeQortal.some(item => item.atAddress === this.checkAlice)) { + return + } else { + this.tradesAvailableDogeQortal = this.tradeBotAvailableDogeQortal + } + } + + await waitFor(1000) + + if (this.isEmptyArray(this.tradesAvailableDogeQortal) === true) { + return + } else { + const botprice = this.round(parseFloat(this.tradeBotDogeBook[0].botDogePrice)) + const changeamount = parseFloat(this.tradeBotDogeBook[0].botDogeQortAmount) + const reduceamount = parseFloat(this.tradesAvailableDogeQortal[0].qortAmount) + const tradeataddress = this.tradesAvailableDogeQortal[0].qortalAtAddress + const newamount = this.round(parseFloat(changeamount - reduceamount)) + + this.reAddAmount = this.round(parseFloat(this.tradeBotDogeBook[0].botDogeQortAmount)) + this.reAddPrice = this.round(parseFloat(this.tradeBotDogeBook[0].botDogePrice)) + + localStorage.removeItem(this.dogeWallet) + localStorage.setItem(this.dogeWallet, "") + + var oldDogeTradebook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + + const newDogeTradebookItem = { + botDogeQortAmount: newamount, + botDogePrice: botprice + } + + oldDogeTradebook.push(newDogeTradebookItem) + + localStorage.setItem(this.dogeWallet, JSON.stringify(oldDogeTradebook)) + + this.tradeBotDogeBook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + + await waitFor(1000) + + this.botBuyAtAddress = tradeataddress + + this.buyAction() + + if (this.isEmptyArray(this.tradeBotDogeBook) === true) { + return + } else { + const botamount = parseFloat(this.tradeBotDogeBook[0].botDogeQortAmount) + + if (Number(botamount) === 0) { + this.removeDOGETradebook() + } else { + this.tradeBotDogeBook = this.tradeBotDogeBook + } + } + + if (this.isEmptyArray(this.tradeBotDogeBook) === true) { + return + } else { + const checkBotFunds = this.round(parseFloat(this.tradeBotDogeBook[0].botDogeQortAmount) * parseFloat(this.tradeBotDogeBook[0].botDogePrice)) + const myBotFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + + if (Number(myBotFunds) < Number(checkBotFunds)) { + this.removeDOGETradebook() + } else { + this.tradeBotDogeBook = this.tradeBotDogeBook + } + } + } + } + + const getDoneTradeDOGE = async () => { + this.isLoadingDoneTrades = true + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const doneDogeQortalUrl = `${nodeUrl}/crosschain/trades?foreignBlockchain=DOGECOIN&minimumTimestamp=1597310000000&limit=0&reverse=true` + const myAddress = window.parent.reduxStore.getState().app.selectedAddress.address + + const doneDogeQortal = await fetch(doneDogeQortalUrl).then(response => { + return response.json() + }) + + this.doneTradesDogeQortal = doneDogeQortal.map(item => { + const searchAddress = item.buyerReceivingAddress + if (searchAddress == myAddress) { + return { + timestamp: item.tradeTimestamp, + foreignAmount: item.foreignAmount, + qortAmount: item.qortAmount + } + } + }).filter(item => !!item) + + this.isLoadingDoneTrades = false + setTimeout(getDoneTradeDOGE, 600000) + } + + window.addEventListener('contextmenu', (event) => { + event.preventDefault() + this._textMenu(event)}, + { passive: true } + ) + + window.addEventListener('click', () => { + parentEpml.request('closeCopyTextMenu', null)}, + { passive: true } + ) + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + if (checkTheme === 'dark') { + this.theme = 'dark' + } else { + this.theme = 'light' + } + document.querySelector('html').setAttribute('theme', this.theme) + }) + + window.onkeyup = (e) => { + if (e.keyCode === 27) parentEpml.request('closeCopyTextMenu', null) + } + + this.dogeWallet = window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.address + + let configLoaded = false + + parentEpml.ready().then(() => { + parentEpml.subscribe('selected_address', async (selectedAddress) => { + this.selectedAddress = {} + selectedAddress = JSON.parse(selectedAddress) + if (!selectedAddress || Object.entries(selectedAddress).length === 0) return + this.selectedAddress = selectedAddress + this.dogeWwallet = window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.address + await this.updateAccountBalance() + await this.dogeTradebook() + }) + + parentEpml.subscribe('config', (c) => { + if (!configLoaded) { + setTimeout(getQortDogePrice, 1) + setTimeout(getDoneTradeDOGE, 1) + configLoaded = true + } + this.config = JSON.parse(c) + }) + + parentEpml.subscribe('copy_menu_switch', async (value) => { + if (value === 'false' && window.getSelection().toString().length !== 0) this.clearSelection() + }) + this.setForeignCoin() + }) + parentEpml.imReady() + + setTimeout(() => this.shadowRoot.querySelector('[slot="vaadin-grid-cell-content-3"]').setAttribute('title', 'Last Seen'), 3000) + + this.dogeTradebook() + await delay(3000) + filterMyPriceTradesDOGE() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + if (checkTheme === 'dark') { + this.theme = 'dark'; + } else { + this.theme = '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) + } + } + + renderFetchText() { + return html`${translate("walletpage.wchange1")}` + } + + renderWarning() { + return html`NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode}` + } + + renderBotWarning() { + return html`NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode} ! AUTO BUY BOT IS STOPPED` + } + + exchangeRateQort() { + return html`${this.qortdoge}` + } + + showAutoBuyGrid() { + return html`${this.tradeBotDOGETemplate()}` + } + + showAddToAutoBuyStore() { + this.addToDOGETradebook() + } + + dogeTradebook() { + if (localStorage.getItem(this.dogeWallet) === null) { + localStorage.setItem(this.dogeWallet, "") + } else { + this.tradeBotDogeBook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + } + } + + addToDOGETradebook() { + const addBotDogeQortAmount = this.shadowRoot.getElementById('autoBuyDOGEQortAmountInput').value + const addBotDogePrice = this.shadowRoot.getElementById('autoBuyDOGEPriceInput').value + const addDogeQortAmount = this.round(parseFloat(addBotDogeQortAmount)) + const addDogePrice = this.round(parseFloat(addBotDogePrice)) + + var oldDogeTradebook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + + const newDogeTradebookItem = { + botDogeQortAmount: addDogeQortAmount, + botDogePrice: addDogePrice + } + + oldDogeTradebook.push(newDogeTradebookItem) + + localStorage.setItem(this.dogeWallet, JSON.stringify(oldDogeTradebook)) + + let dogetradebookstring = get("tradepage.tchange44") + parentEpml.request('showSnackBar', `${dogetradebookstring}`) + + this.closeDOGETradebookDialog() + this.tradeBotDogeBook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + this.firstUpdated() + } + + closeDOGETradebookDialog() { + this.shadowRoot.querySelector('#tradeBotDOGEAddDialog').close() + this.clearTradeBotForm() + } + + removeDOGETradebook() { + localStorage.removeItem(this.dogeWallet) + localStorage.setItem(this.dogeWallet, "") + this.tradeBotDogeBook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + + this.autoBuyBotDisable = false + + let dogestring = get("tradepage.tchange41") + parentEpml.request('showSnackBar', `${dogestring}`) + this.tradesAvailableDogeQortal = [] + } + + exchangeRateForeign() { + parentEpml.request('apiCall', { + url: `/crosschain/price/DOGECOIN?inverse=false` + }).then((res) => { + this.dogeqort = (Number(res) / 1e8).toFixed(8) + }) + return html`${this.dogeqort}` + } + + async updateWalletBalance() { + let _url = `/crosschain/doge/walletbalance?apiKey=${this.getApiKey()}` + let _body = window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.derivedMasterPublicKey + + this.showGetWalletBance = true + this.showAddAutoBuy = false + + await parentEpml.request('apiCall', { + url: _url, + method: 'POST', + body: _body, + }).then((res) => { + if (isNaN(Number(res))) { + let snack1string = get("tradepage.tchange30") + parentEpml.request('showSnackBar', `${snack1string}`) + } else { + this.listedCoins.get(this.selectedCoin).balance = (Number(res) / 1e8).toFixed(8) + } + }) + + this.showGetWalletBance = false + this.showAddAutoBuy = true + } + + async setForeignCoin() { + let _this = this + this.selectedCoin = "DOGECOIN" + + this.isLoadingOpenTrades = true + await this.createConnection() + + this._openOrdersGrid.querySelector('#priceColumn').headerRenderer = function (root) { + const priceString2 = get("tradepage.tchange9") + root.innerHTML = '' + priceString2 + ' (' + _this.listedCoins.get(_this.selectedCoin).coinCode + ')' + } + + this.autoBuyBotDisable = false + await this.updateWalletBalance() + } + + async reRenderOpenFilteredOrders() { + this.requestUpdate() + await this.updateComplete + this.isLoadingOpenTrades = false + } + + async reRenderMyOpenOrders() { + this.requestUpdate() + await this.updateComplete + this.isLoadingMyOpenOrders = false + } + + addAutoBuyAction() { + this.autoBuyWarning = false + this.clearTradeBotForm() + this.shadowRoot.querySelector('#tradeBot' + this.listedCoins.get(this.selectedCoin).coinCode + 'AddDialog').show() + } + + checkTradeBotValues() { + const checkTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const checkTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkAmount = this.round(parseFloat(checkTradeBotAmountInput)) + const checkPrice = this.round(parseFloat(checkTradeBotPriceInput)) + + if (Number(checkAmount) === 0) { + let amountString = get("tradepage.tchange34") + parentEpml.request('showSnackBar', `${amountString}`) + } else if (Number(checkPrice) === 0) { + let priceString = get("tradepage.tchange35") + parentEpml.request('showSnackBar', `${priceString}`) + } else { + this.showAddToAutoBuyStore() + } + } + + processOfferingTrade(offer) { + try { + if(this.listedCoins.get(offer.foreignBlockchain).name!='') { + const offerItem = { + ...offer, + qortAmount: parseFloat(offer.qortAmount), + price: parseFloat(offer.foreignAmount) / parseFloat(offer.qortAmount), + } + const addOffer = () => { + this.listedCoins.get(offer.foreignBlockchain).openOrders.unshift(offerItem) + } + const initOffer = () => { + this.listedCoins.get(offer.foreignBlockchain).openOrders.push(offerItem) + } + this.listedCoins.get(offer.foreignBlockchain).openOrders.length === 0 ? initOffer() : addOffer() + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + } catch(e) { + console.log("Error adding offer from "+offer.foreignBlockchain) + } + } + + processRedeemedTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + const offerItem = { + ...offer, + mode: 'SOLD', + } + this._myHistoricTradesGrid.items.unshift(offerItem) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } else if (offer.partnerQortalReceivingAddress === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + const offerItem = { + ...offer, + mode: 'BOUGHT', + } + this._myHistoricTradesGrid.items.unshift(offerItem) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + const addNewHistoricTrade = () => { + this._historicTradesGrid.items.unshift(offer) + this._historicTradesGrid.clearCache() + } + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? addNewHistoricTrade() : null + + } + } catch(e) { + console.log("Error processing redeemed trade offer from "+offer.foreignBlockchain) + } + } + + processTradingTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address && this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._openOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._openOrdersGrid.items.splice(index, 1) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + }) + this.listedCoins.get(offer.foreignBlockchain).openOrders = this.listedCoins.get(offer.foreignBlockchain).openOrders.filter((order) => order.qortalAtAddress !== offer.qortalAtAddress) + } + } catch(e) { + console.log("Error processing trading trade offer from "+offer.foreignBlockchain) + } + } + + processRefundedTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._myHistoricTradesGrid.items.unshift(offer) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + + } + } catch(e) { + console.log("Error processing refunded trade offer from "+offer.foreignBlockchain) + } + } + + processCancelledTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._myHistoricTradesGrid.items.unshift(offer) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + this._openOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._openOrdersGrid.items.splice(index, 1) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + }) + this.listedCoins.get(offer.foreignBlockchain).openOrders = this.listedCoins.get(offer.foreignBlockchain).openOrders.filter((order) => order.qortalAtAddress !== offer.qortalAtAddress) + this._stuckOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._stuckOrdersGrid.items.splice(index, 1) + this._stuckOrdersGrid.clearCache() + } + }) + } + } catch(e) { + console.log("Error processing cancelled trade offer from "+offer.foreignBlockchain) + } + } + + /** + * TRADE OFFER STATES or MODE + * - OFFERING + */ + + processTradeOffers(offers) { + offers.forEach((offer) => { + if (offer.mode === 'OFFERING') { + this.processOfferingTrade(offer) + } + }) + } + + processTradeBotStates(tradeStates) { + + const DogecoinACCTv1 = (states) => { + states.reverse() + states.forEach((state) => { + if (state.creatorAddress === this.selectedAddress.address) { + if (state.tradeState == 'ALICE_WAITING_FOR_AT_LOCK') { + this.changeTradeBotState(state, 'BUYING') + } else if (state.tradeState == 'ALICE_DONE') { + this.handleCompletedState(state) + } else if (state.tradeState == 'ALICE_REFUNDING_A') { + this.changeTradeBotState(state, 'REFUNDING') + } else if (state.tradeState == 'ALICE_REFUNDED') { + this.handleCompletedState(state) + } + } + }) + } + + switch (this.selectedCoin) { + case 'DOGECOIN': + DogecoinACCTv1(tradeStates) + break + default: + break + } + } + + changeTradeBotState(state, tradeState) { + this.isLoadingMyOpenOrders = true + const stateItem = { + ...state, + _tradeState: tradeState, + } + const item = this._myOrdersGrid.querySelector(`#${state.atAddress}`) + const addStateItem = () => { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.unshift(stateItem) + this._myOrdersGrid.clearCache() + } + const updateStateItem = () => { + this._myOrdersGrid.items.forEach((item, index) => { + if (item.atAddress === state.atAddress) { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.splice(index, 1) + this._myOrdersGrid.items.unshift(stateItem) + this._myOrdersGrid.clearCache() + } + }) + } + item ? updateStateItem() : addStateItem() + } + + handleCompletedState(state) { + this._myOrdersGrid.items.forEach((item, index) => { + if (item.atAddress === state.atAddress) { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.splice(index, 1) + this._myOrdersGrid.clearCache() + } + }) + } + + initSocket() { + let _relatedCoin = "" + let tradePresenceTxns = null + let offeringTrades = null + + self.addEventListener('message', function (event) { + switch (event.data.type) { + case 'open_orders': + offeringTrades = event.data.content + processOffersWithPresence() + break + case 'set_coin': + _relatedCoin = event.data.content + break + default: + break + } + }) + + const lessThanThirtyMinsAgo = (timestamp) => { + const THIRTYMINS = 1000 * 60 * 30 + const thirtyMinsAgo = Date.now() - THIRTYMINS + return timestamp > thirtyMinsAgo + } + + const filterOffersUsingTradePresence = (offeringTrade) => { + return offeringTrade.tradePresenceExpiry > Date.now(); + } + + const processOffersWithPresence = () => { + if (offeringTrades === null) return + + async function asyncForEach(array, callback) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array) + } + } + + const startOfferPresenceMapping = async () => { + + if (tradePresenceTxns !== null) { + await asyncForEach(tradePresenceTxns, async (tradePresence) => { + let offerIndex = offeringTrades.findIndex((offeringTrade) => offeringTrade.qortalCreatorTradeAddress === tradePresence.tradeAddress) + offerIndex !== -1 ? (offeringTrades[offerIndex].tradePresenceExpiry = tradePresence.timestamp) : null + }) + } + + let filteredOffers = offeringTrades.filter((offeringTrade) => filterOffersUsingTradePresence(offeringTrade)) + self.postMessage({ type: 'PRESENCE', data: { offers: offeringTrades, filteredOffers: filteredOffers, relatedCoin: _relatedCoin } }) + } + + startOfferPresenceMapping() + } + + const initTradeOffersWebSocket = (restarted = false) => { + let tradeOffersSocketCounter = 0 + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradeoffers?foreignBlockchain=FOREIGN_BLOCKCHAIN&includeHistoric=true` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + tradeOffersSocketCounter += 1 + } + socket.onmessage = (e) => { + e.relatedCoin = _relatedCoin + self.postMessage({ + type: 'TRADE_OFFERS', + data: e.data, + counter: tradeOffersSocketCounter, + isRestarted: restarted, + }) + tradeOffersSocketCounter += 1 + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradeOffersWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const initTradeBotWebSocket = (restarted = false) => { + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradebot?foreignBlockchain=FOREIGN_BLOCKCHAIN` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + } + socket.onmessage = (e) => { + e.relatedCoin = _relatedCoin + self.postMessage({ + type: 'TRADE_BOT', + data: e.data, + isRestarted: restarted, + }) + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradeBotWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const initTradePresenceWebSocket = (restarted = false) => { + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradepresence` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + } + socket.onmessage = (e) => { + tradePresenceTxns = JSON.parse(e.data) + processOffersWithPresence() + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradePresenceWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const restartTradePresenceWebSocket = () => { + setTimeout(() => initTradePresenceWebSocket(true), 50) + } + + const restartTradeOffersWebSocket = () => { + setTimeout(() => initTradeOffersWebSocket(true), 50) + } + + const restartTradeBotWebSocket = () => { + setTimeout(() => initTradeBotWebSocket(true), 50) + } + + initTradeOffersWebSocket() + initTradePresenceWebSocket() + initTradeBotWebSocket() + } + + async buyAction() { + const qortalAtAddress = this.botBuyAtAddress + let _foreignKey = this.selectedAddress.dogeWallet.derivedMasterPrivateKey + + const makeRequest = async () => { + const response = await parentEpml.request('tradeBotRespondRequest', { + atAddress: qortalAtAddress, + foreignKey: _foreignKey, + receivingAddress: this.selectedAddress.address, + }) + return response + } + + const manageResponse = (response) => { + if (response === true) { + this.isBuyLoading = false + this.buyBtnDisable = true + let snack5string = get("tradepage.tchange23") + parentEpml.request('showSnackBar', `${snack5string}`) + } else if (response === false) { + this.isBuyLoading = false + this.buyBtnDisable = false + + localStorage.removeItem(this.dogeWallet) + localStorage.setItem(this.dogeWallet, "") + + var oldDogeTradebook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + + const newDogeTradebookItem = { + botDogeQortAmount: this.reAddAmount, + botDogePrice: this.reAddPrice + } + + oldDogeTradebook.push(newDogeTradebookItem) + + localStorage.setItem(this.dogeWallet, JSON.stringify(oldDogeTradebook)) + + this.tradeBotDogeBook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + + let snack6string = get("tradepage.tchange24") + parentEpml.request('showSnackBar', `${snack6string}`) + } else { + this.isBuyLoading = false + this.buyBtnDisable = false + + localStorage.removeItem(this.dogeWallet) + localStorage.setItem(this.dogeWallet, "") + + var oldDogeTradebook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + + const newDogeTradebookItem = { + botDogeQortAmount: this.reAddAmount, + botDogePrice: this.reAddPrice + } + + oldDogeTradebook.push(newDogeTradebookItem) + + localStorage.setItem(this.dogeWallet, JSON.stringify(oldDogeTradebook)) + + this.tradeBotDogeBook = JSON.parse(localStorage.getItem(this.dogeWallet) || "[]") + + let snack7string = get("tradepage.tchange25") + parentEpml.request('showSnackBar', `${snack7string}: ${response.message}`) + } + } + const res = await makeRequest() + manageResponse(res) + } + + updateAccountBalance() { + clearTimeout(this.updateAccountBalanceTimeout) + parentEpml.request('apiCall', { + url: `/addresses/balance/${this.selectedAddress.address}?apiKey=${this.getApiKey()}`, + }).then((res) => { + this.listedCoins.get("QORTAL").balance = res + this.updateAccountBalanceTimeout = setTimeout(() => this.updateAccountBalance(), 10000) + }) + } + + _checkBuyAmount(e) { + const targetAmount = e.target.value + const target = e.target + + if (targetAmount.length === 0) { + this.isValidAmount = false + this.sellBtnDisable = true + e.target.blur() + e.target.focus() + e.target.invalid = true + } else { + this.buyBtnDisable = false + } + + e.target.blur() + e.target.focus() + + e.target.validityTransform = (newValue, nativeValidity) => { + if (newValue.includes('-') === true) { + this.buyBtnDisable = true + return { + valid: false, + } + } else if (!nativeValidity.valid) { + if (newValue.includes('.') === true) { + let myAmount = newValue.split('.') + if (myAmount[1].length > 8) { + this.buyBtnDisable = true + } else { + this.buyBtnDisable = false + return { + valid: true, + } + } + } + } else { + this.buyBtnDisable = false + } + } + } + + checkTradeBotAmount(e) { + const targetAmount = e.target.value + const target = e.target + this.autoBuyWarning = false + + if (targetAmount.length === 0) { + this.isValidAmount = false + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + e.target.blur() + e.target.focus() + e.target.invalid = true + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + } + + e.target.blur() + e.target.focus() + + e.target.validityTransform = (newValue, nativeValidity) => { + if (newValue.includes('-') === true) { + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + return { + valid: false, + } + } else if (!nativeValidity.valid) { + if (newValue.includes('.') === true) { + let myAmount = newValue.split('.') + if (myAmount[1].length > 8) { + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + return { + valid: true, + } + } + } + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + } + } + } + + getApiKey() { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; + let apiKey = myNode.apiKey; + return apiKey; + } + + clearSelection() { + window.getSelection().removeAllRanges() + window.parent.getSelection().removeAllRanges() + } + + _textMenu(event) { + const getSelectedText = () => { + var text = '' + if (typeof window.getSelection != 'undefined') { + text = window.getSelection().toString() + } else if (typeof this.shadowRoot.selection != 'undefined' && this.shadowRoot.selection.type == 'Text') { + text = this.shadowRoot.selection.createRange().text + } + return text + } + + const checkSelectedTextAndShowMenu = () => { + let selectedText = getSelectedText() + if (selectedText && typeof selectedText === 'string') { + let _eve = { + pageX: event.pageX, + pageY: event.pageY, + clientX: event.clientX, + clientY: event.clientY, + } + let textMenuObject = { + selectedText: selectedText, + eventObject: _eve, + isFrame: true, + } + parentEpml.request('openCopyTextMenu', textMenuObject) + } + } + checkSelectedTextAndShowMenu() + } + + clearTradeBotForm() { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value = this.initialBotAmount + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value = this.initialAmount + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.initialBotAmount + this.autoBuyBtnDisable = true + } + + isEmptyArray(arr) { + if (!arr) { + return true + } + return arr.length === 0 + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } + + inlineWorker(passedFunction, modifiers) { + let parsedFunction = `` + + modifiers.forEach((modifier) => { + let regex = new RegExp(modifier.searchValue, 'g') + parsedFunction = parsedFunction.length === 0 ? `(function ${passedFunction.toString().trim().replace(regex, modifier.replaceValue)})()` : parsedFunction.toString().trim().replace(regex, modifier.replaceValue) + }) + + const workerUrl = URL.createObjectURL(new Blob([parsedFunction], { type: 'text/javascript' })) + const worker = new Worker(workerUrl) + URL.revokeObjectURL(workerUrl) + return worker + } + + clearPaneCache() { + this._openOrdersGrid.clearCache() + this._myOrdersGrid.clearCache() + } + + createConnection() { + if (workers.get(this.selectedCoin).tradesConnectedWorker !== null) { + this.isLoadingOpenTrades = false + return + } + + const handleMessage = (message) => { + switch (message.type) { + case 'TRADE_OFFERS': + if (!message.isRestarted) { + this.listedCoins.get(this.selectedCoin).tradeOffersSocketCounter = message.counter + this.processTradeOffers(JSON.parse(message.data)) + this.listedCoins.get(this.selectedCoin).tradeOffersSocketCounter === 1 ? this.clearPaneCache() : null + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage(this.listedCoins.get(this.selectedCoin).openOrders) + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage({ type: "open_orders", content: this.listedCoins.get(this.selectedCoin).openOrders }) + } + return null + case 'TRADE_BOT': + if (!message.isRestarted) this.processTradeBotStates(JSON.parse(message.data)) + return null + case 'PRESENCE': + this.listedCoins.get(message.data.relatedCoin).openOrders = message.data.offers + this.listedCoins.get(message.data.relatedCoin).openFilteredOrders = message.data.filteredOffers + this.reRenderOpenFilteredOrders() + return null + default: + break + } + } + + let myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + let nodeUrl = myNode.domain + ':' + myNode.port + + const modifiers = [ + { searchValue: 'NODEURL', replaceValue: nodeUrl }, + { searchValue: 'FOREIGN_BLOCKCHAIN', replaceValue: this.selectedCoin }, + ] + + workers.get(this.selectedCoin).tradesConnectedWorker = this.inlineWorker(this.initSocket, modifiers) + + workers.get(this.selectedCoin).tradesConnectedWorker.addEventListener('message', function (event) { handleMessage(event.data) }, { passive: true }) + + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage({ type: "set_coin", content: this.selectedCoin }) + + } +} + +window.customElements.define('trade-bot-doge', TradeBotDOGE) \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-ltc/index.html b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-ltc/index.html new file mode 100644 index 00000000..b9666aa2 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-ltc/index.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-ltc/trade-bot-ltc.src.js b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-ltc/trade-bot-ltc.src.js new file mode 100644 index 00000000..7955c5c0 --- /dev/null +++ b/qortal-ui-plugins/plugins/core/trade-bot/trade-bot-ltc/trade-bot-ltc.src.js @@ -0,0 +1,2155 @@ +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 '@material/mwc-button' +import '@material/mwc-textfield' +import '@material/mwc-icon' +import '@material/mwc-icon-button' +import '@material/mwc-dialog' +import '@material/mwc-tab-bar' +import '@material/mwc-tab' +import '@material/mwc-list/mwc-list-item' +import '@material/mwc-select' +import '@polymer/iron-icons/iron-icons.js' +import '@polymer/paper-icon-button/paper-icon-button.js' +import '@polymer/paper-spinner/paper-spinner-lite.js' +import '@vaadin/grid' +import '@vaadin/grid/vaadin-grid-sorter' + +const parentEpml = new Epml({ type: 'WINDOW', source: window.parent }) + +let workers = new Map() + +class TradeBotLTC extends LitElement { + static get properties() { + return { + selectedAddress: { type: Object }, + config: { type: Object }, + listedCoins: { type: Map }, + sellBtnDisable: { type: Boolean }, + isSellLoading: { type: Boolean }, + isBuyLoading: { type: Boolean }, + buyBtnDisable: { type: Boolean }, + autoBuyWarning: { type: Boolean }, + autoBuyBtnDisable: { type: Boolean }, + autoBuyBotDisable: { type: Boolean }, + initialAmount: { type: Number }, + cancelBtnDisable: { type: Boolean }, + cancelStuckOfferBtnDisable: { type: Boolean }, + selectedCoin: { type: String }, + isLoadingHistoricTrades: { type: Boolean }, + isLoadingOpenTrades: { type: Boolean }, + isLoadingMyOpenOrders: { type: Boolean }, + showGetWalletBance: { type: Boolean }, + showAddAutoBuy: { type: Boolean }, + theme: { type: String, reflect: true }, + ltcWallet: { type: String }, + qortltc: { type: Number }, + ltcqort: { type: Number }, + tradeBotLtcBook: { type: Array }, + tradesLtcQortal: { type: Array }, + doneTradesLtcQortal: { type: Array }, + tradesAvailableLtcQortal: { type: Array }, + tradeBotLtcQortal: { type: Array }, + tradeBotAvailableLtcQortal: { type: Array }, + checkAlice: { type: String }, + botBuyAtAddress: { type: String }, + reAddAmount: { type: Number }, + reAddPrice: { type: Number } + } + } + + static get styles() { + return css` + * { + --mdc-theme-primary: rgb(3, 169, 244); + --mdc-theme-secondary: var(--mdc-theme-primary); + --mdc-theme-error: rgb(255, 89, 89); + --mdc-text-field-outlined-idle-border-color: var(--txtfieldborder); + --mdc-text-field-outlined-hover-border-color: var(--txtfieldhoverborder); + --mdc-text-field-label-ink-color: var(--black); + --mdc-text-field-ink-color: var(--black); + --mdc-select-outlined-idle-border-color: var(--txtfieldborder); + --mdc-select-outlined-hover-border-color: var(--txtfieldhoverborder); + --mdc-select-label-ink-color: var(--black); + --mdc-select-ink-color: var(--black); + --mdc-theme-surface: var(--white); + --mdc-dialog-content-ink-color: var(--black); + --mdc-dialog-shape-radius: 25px; + --paper-input-container-focus-color: var(--mdc-theme-primary); + --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); + --_lumo-grid-border-color: var(--border); + --_lumo-grid-secondary-border-color: var(--border2); + } + paper-spinner-lite { + height: 30px; + width: 30px; + --paper-spinner-color: var(--mdc-theme-primary); + --paper-spinner-stroke-width: 3px; + } + mwc-tab-bar { + --mdc-text-transform: none; + --mdc-tab-color-default: var(--black); + --mdc-tab-text-label-color-default: var(--black); + } + #tabs-1 { + --mdc-tab-height: 42px; + border-left: 1px solid var(--tradeborder); + border-top: 1px solid var(--tradeborder); + border-right: 1px solid var(--tradeborder); + color: var(--black); + } + #tab-buy[active] { + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + #tabs-1-content { + height: 100%; + padding-bottom: 10px; + } + #tabs-1-content > div { + height: 100%; + border: 1px solid var(--tradeborder); + } + #tabs-1-content .card { + border: none; + } + #tabs-1-content .btn-clear { + --mdc-icon-button-size: 32px; + color: var(--black); + } + .btn-clear-bot { + --mdc-icon-button-size: 32px; + color: var(--black); + float: right; + } + .btn-info { + color: var(--black); + --mdc-icon-size: 16px; + padding-top: 3px; + } + #tab-sell[active] { + --mdc-theme-primary: rgb(255, 89, 89); + } + #trade-portal-page { + background: var(--white); + padding: 12px 24px; + } + .divCard { + border: 1px solid var(--black); + padding: 1em; + box-shadow: 0 0.3px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 1px -1px rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.2); + } + h2 { + margin: 10px 0; + } + + h4 { + margin: 5px 0; + } + + p { + font-size: 14px; + line-height: 21px; + } + + .card-body { + background-color: var(--white); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + min-height: 100vh; + margin: 0; + } + + .card-container { + background-color: var(--white); + border-radius: 5px; + color: var(--black); + padding-top: 30px; + position: relative; + width: 350px; + max-width: 100%; + text-align: center; + } + + .card-container .level { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + left: 30px; + } + + .card-container .founder { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + right: 30px; + } + + .card-container .round { + width: 96px; + height: 96px; + border: 1px solid #03a9f4; + border-radius: 50%; + padding: 2px; + } + + .card-container .badge { + width: 200px; + height: 135px; + border: 1px solid transparent; + border-radius: 10%; + padding: 2px; + } + + .userdata { + background-color: #1F1A36; + text-align: left; + padding: 15px; + margin-top: 30px; + } + + .userdata ul { + list-style-type: none; + margin: 0; + padding: 0; + } + + .userdata ul li { + border: 1px solid #2D2747; + border-radius: 2px; + display: inline-block; + font-size: 12px; + margin: 0 7px 7px 0; + padding: 7px; + } + + h2, + h3, + h4, + h5 { + color: var(--black); + font-weight: 400; + } + header { + display: flex; + flex: 0 1 auto; + align-items: center; + justify-content: center; + padding: 0px 10px; + font-size: 16px; + color: var(--white); + background-color: var(--tradehead); + border-left: 1px solid var(--tradeborder); + border-top: 1px solid var(--tradeborder); + border-right: 1px solid var(--tradeborder); + min-height: 40px; + } + p { + margin-bottom: 12px; + } + #trade-portal { + max-width: 100vw; + margin-left: auto; + margin-right: auto; + } + .box { + margin: 0; + padding: 0; + display: flex; + flex-flow: column; + height: 100%; + } + .box-bot { + margin: 0; + padding: 0; + display: flex; + flex-flow: column; + height: 150px; + } + #first-trade-section { + margin-bottom: 10px; + } + #first-trade-section > div { + } + #second-trade-section { + margin-bottom: 10px; + } + #second-trade-section > div { + } + #third-trade-section { + margin-bottom: 10px; + } + #third-trade-section > div { + } + .trade-chart { + background-color: var(--white); + border: 2px #ddd solid; + text-align: center; + } + .open-trades { + text-align: center; + } + .open-market-container { + text-align: center; + } + .trade-bot-container { + text-align: center; + } + .no-last-seen { + background: rgb(255, 89, 89); + padding: 9px 1.3px; + border-radius: 50%; + width: 1rem; + margin: 0 auto; + } + .card { + padding: 1em; + border: 1px var(--tradeborder) solid; + flex: 1 1 auto; + display: flex; + flex-flow: column; + justify-content: space-evenly; + min-height: inherit; + } + .card-bot { + padding: 1em; + flex: 1 1 auto; + display: flex; + flex-flow: column; + justify-content: space-evenly; + width: 350px; + min-height: inherit; + } + .cancel { + --mdc-theme-primary: rgb(255, 89, 89); + } + .border-wrapper { + border: 1px var(--tradeborder) solid; + overflow: hidden; + } + .amt-text { + color: var(--tradehave); + font-size: 15px; + margin-top: 5px; + margin-bottom: 12px; + } + .exchange { + color: var(--black); + font-size: 18px; + font-weight: bold; + margin-top: 5px; + margin-bottom: 10px; + } + .clear-button { + display: inline; + float: right; + margin-bottom: 5px; + } + .exhcnage-text { + display: inline; + float: left; + margin-bottom: 5px; + } + .balance-text { + display: inline; + float: right; + margin-bottom: 5px; + } + .fee-text { + display: inline; + float: left; + margin-bottom: 5px; + } + .tab-text { + color: var(--tradehave); + font-size: 12px; + text-align: left; + margin-top: 2px; + margin-bottom: -12px; + } + .historic-trades { + text-align: center; + } + .my-open-orders { + text-align: center; + } + .my-historic-trades { + text-align: center; + } + .buttons { + width: auto !important; + } + .buy-button { + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + .sell-button { + --mdc-theme-primary: rgb(255, 89, 89); + } + .trade-bot-button { + margin-top: 20px; + margin-bottom: 20px; + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } + .full-width { + background-color: var(--white); + border: 2px var(--black); + height: 200px; + text-align: center; + } + vaading-grid { + font-size: .8em; + } + vaadin-grid-column { + flex-grow: 1; + } + .loadingContainer { + height: 100%; + width: 100%; + } + .loading, + .loading:after { + border-radius: 50%; + width: 5em; + height: 5em; + } + .loading { + margin: 10px auto; + border-width: .6em; + border-style: solid; + border-color: rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2) rgba(3, 169, 244, 0.2) rgb(3, 169, 244); + font-size: 10px; + position: relative; + text-indent: -9999em; + transform: translateZ(0px); + animation: 1.1s linear 0s infinite normal none running loadingAnimation; + } + mwc-select#coinSelectionMenu { + font-size: 24px; + width:220px; + } + mwc-select#coinSelectionMenu mwc-list-item { + line-height: 30px; + } + .coinName::before { + content: ""; + display: inline-block; + height: 26px; + width: 45px; + position: absolute; + background-repeat: no-repeat; + background-size: cover; + left: 10px; + top: 10px; + } + .coinName { + display: inline-block; + height: 26px; + padding-left: 45px; + } + .warning-text { + animation: blinker 1.5s linear infinite; + display: inline; + float: left; + margin-bottom: 5px; + color: rgb(255, 89, 89); + } + .warning-bot-text { + animation: blinker 1.5s linear infinite; + display: inline; + text-align: center; + color: rgb(255, 89, 89); + } + .red { + --mdc-theme-primary: #F44336; + } + @-webkit-keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + @keyframes loadingAnimation { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + @keyframes blinker { + 50% { + opacity: 0; + } + } + @media (min-width: 701px) { + * { + } + #trade-portal {} + #first-trade-section { + display: grid; + grid-template-columns:1fr 4fr 1fr; + grid-auto-rows: max(350px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #second-trade-section { + display: grid; + grid-template-columns:1fr 4fr 1fr; + grid-auto-rows: max(350px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #third-trade-section { + display: grid; + grid-template-columns: 1fr 4fr 1fr; + grid-auto-rows: max(150px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + #fourth-trade-section { + display: grid; + grid-template-columns: 1fr 4fr 1fr; + grid-auto-rows: max(150px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } + } + ` + } + + constructor() { + super() + let qortal = { + name: "QORTAL", + balance: "0", + coinCode: "QORT", + coinAmount: this.amountString, + tradeFee: "0.002" + } + + let litecoin = { + name: "LITECOIN", + balance: "0", + coinCode: "LTC", + openOrders: [], + openFilteredOrders: [], + historicTrades: [], + myOrders: [], + myHistoricTrades: [], + myOfferingOrders: [], + openTradeOrders: null, + tradeOffersSocketCounter: 1, + coinAmount: this.amountString, + tradeFee: "~0.00005" + } + + this.listedCoins = new Map() + this.listedCoins.set("QORTAL", qortal) + this.listedCoins.set("LITECOIN", litecoin) + + workers.set("QORTAL", { + tradesConnectedWorker: null, + handleStuckTradesConnectedWorker: null + }) + + workers.set("LITECOIN", { + tradesConnectedWorker: null, + handleStuckTradesConnectedWorker: null + }) + + this.selectedCoin = "LITECOIN" + this.selectedAddress = {} + this.config = {} + this.sellBtnDisable = false + this.isSellLoading = false + this.buyBtnDisable = true + this.autoBuyWarning = false + this.autoBuyBtnDisable = true + this.autoBuyBotDisable = false + this.isBuyLoading = false + this.initialAmount = 0 + this.cancelBtnDisable = false + this.cancelStuckOfferBtnDisable = false + this.isLoadingHistoricTrades = true + this.isLoadingOpenTrades = true + this.isLoadingMyOpenOrders = false + this.showGetWalletBance = true + this.showAddAutoBuy = false + this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.ltcWallet = '' + this.qortltc = 0 + this.ltcqort = 0 + this.tradeBotLtcBook = [] + this.tradesLtcQortal = [] + this.doneTradesLtcQortal = [] + this.tradesAvailableLtcQortal = [] + this.tradeBotLtcQortal = [] + this.tradeBotAvailableLtcQortal = [] + this.checkAlice = '' + this.botBuyAtAddress = '' + this.reAddAmount = 0 + this.reAddPrice = 0 + } + + openTradesTemplate() { + return html` +
+
+
${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange42")}
+
+
${translate("login.loading")}
+ + { + render(html`${this.round(data.item.qortAmount)}`, root) + }} + > + + { + render(html`${this.round(data.item.price)}`, root) + }} + > + + { + render(html`${data.item.foreignAmount}`, root) + }} + > + + { + render(html`${data.item.qortalCreator}`, root) + }} + > + + +
+
+
+ ` + } + + myOpenOrdersTemplate() { + return html` +
+
+
${translate("tradepage.tchange36")}
+
+
${translate("login.loading")}
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + render(html` ${data.item._tradeState} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + + + +
+
+
+ ` + } + + myDoneTradesTemplate() { + return html` +
+
+
${translate("tradepage.tchange43")} (${this.listedCoins.get(this.selectedCoin).coinCode})
+
+
${translate("login.loading")}
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + return render(html` ${translate("tradepage.tchange32")} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + { + render(html` ${data.item.foreignAmount} `, root) + }} + > + + +
+
+
+ ` + } + + myHistoricTradesTemplate() { + return html` +
+
+
${translate("tradepage.tchange4")}
+
+ + { + const dateString = new Date(data.item.timestamp).toLocaleString() + render(html`${dateString}`, root) + }} + > + + { + if (data.item.mode === 'SOLD') return render(html` ${translate("tradepage.tchange31")} `, root) + if (data.item.mode === 'BOUGHT') return render(html` ${translate("tradepage.tchange32")} `, root) + return render(html` ${data.item.mode} `, root) + }} + > + + { + const price = this.round(parseFloat(data.item.foreignAmount) / parseFloat(data.item.qortAmount)) + render(html`${price}`, root) + }} + > + + + + { + render(html` ${data.item.foreignAmount} `, root) + }} + > + + +
+
+
+ ` + } + + tradeBotLTCTemplate() { + return html` +
+
+
${translate("tradepage.tchange39")} ${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT
+
+ + { + render(html`${data.item.botLtcQortAmount}`, root) + }} + > + + { + render(html`${data.item.botLtcPrice}`, root) + }} + > + + { + const totalCoins = this.round(parseFloat(data.item.botLtcQortAmount) * parseFloat(data.item.botLtcPrice)) + const myBotFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myBotFunds) > Number(totalCoins)) { + this.autoBuyBotDisable = false + render(html`${totalCoins}`, root) + } else { + this.autoBuyBotDisable = true + render(html`${totalCoins}`, root) + } + }} + > + + { + render(html` this.removeLTCTradebook()}>delete ${translate("nodepage.nchange12")}`, root) + }} + > + + + ${this.isEmptyArray(this.tradeBotLtcBook) ? html` + + ${translate("tradepage.tchange38")} ${translate("tradepage.tchange39")} + + `: ''} +
+ ${this.autoBuyBotDisable ? html` +
${this.renderBotWarning()}
+ `: ''} +
+
+ ` + } + + render() { + return html` +
+
+
+
+ ${this.openTradesTemplate()} +
+
+
+
+ ${this.myDoneTradesTemplate()} +
+
+
+
+ ${this.myOpenOrdersTemplate()} +
+
+
+
+ ${this.showAutoBuyGrid()} +
+
+
+

${translate("tradepage.tchange33")} ${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange40")}

+

1 QORT = ${this.exchangeRateQort()} ${this.listedCoins.get(this.selectedCoin).coinCode}

+
+
+
+ + +
+

${this.renderFetchText()}

+
+
+
+

${this.listedCoins.get(this.selectedCoin).coinCode} ==> QORT

+
+
+
+
${this.renderWarning()}
+ +
+ ${translate("tradepage.tchange8")} (QORT)* +

+ + +

+ ${translate("tradepage.tchange14")} (${this.listedCoins.get(this.selectedCoin).coinCode})* +

+ + +

+ ${translate("tradepage.tchange10")} (${this.listedCoins.get(this.selectedCoin).coinCode})* +

+ + +

+ + ${translate("tradepage.tchange16")}: ${this.listedCoins.get(this.selectedCoin).balance} ${this.listedCoins.get(this.selectedCoin).coinCode} + +
+
+ + ${translate("tradepage.tchange38")} ${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange39")} + +
+
+
+ ${translate("general.close")} +
+ ` + } + + async firstUpdated() { + + let _this = this + + this.changeTheme() + this.changeLanguage() + await this.updateWalletBalance() + + this._openOrdersGrid = this.shadowRoot.getElementById('openOrdersGrid') + + this._openOrdersGrid.querySelector('#priceColumn').headerRenderer = function (root) { + const priceString = get("tradepage.tchange9") + root.innerHTML = '' + priceString + ' (' + _this.listedCoins.get(_this.selectedCoin).coinCode + ')' + } + + this._openOrdersGrid.querySelector('#qortAmountColumn').headerRenderer = function (root) { + const amountString = get("tradepage.tchange8") + root.innerHTML = '' + amountString + ' (QORT)' + } + + this._myOrdersGrid = this.shadowRoot.getElementById('myOrdersGrid') + this._myHistoricTradesGrid = this.shadowRoot.getElementById('myHistoricTradesGrid') + + const delay = ms => new Promise(res => setTimeout(res, ms)) + + const getQortLtcPrice = () => { + parentEpml.request("apiCall", { url: `/crosschain/price/LITECOIN?inverse=true` }).then((res) => { + setTimeout(() => { this.qortltc = (Number(res) / 1e8).toFixed(8) }, 1) + }) + setTimeout(getQortLtcPrice, 300000) + } + + const filterMyPriceTradesLTC = async () => { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const tradeBotLtcQortalUrl = `${nodeUrl}/crosschain/tradebot?foreignBlockchain=LITECOIN&apiKey=${this.getApiKey()}` + const waitFor = ms => new Promise(res => setTimeout(res, ms)) + let timer + + const tradeBotLtcQortal = await fetch(tradeBotLtcQortalUrl).then(response => { + return response.json() + }) + + this.tradeBotLtcQortal = tradeBotLtcQortal + + await waitFor(1000) + + if (this.isEmptyArray(this.tradeBotLtcBook) === true) { + clearTimeout(timer) + timer = setTimeout(filterMyPriceTradesLTC, 180000) + return + } else { + clearTimeout(timer) + timer = setTimeout(filterMyPriceTradesLTC, 180000) + + this.tradeBotAvailableLtcQortal = this.listedCoins.get(this.selectedCoin).openFilteredOrders.map(item => { + const listprice = parseFloat(item.price) + const listamount = parseFloat(item.qortAmount) + const checkprice = parseFloat(this.tradeBotLtcBook[0].botLtcPrice) + const checkamount = parseFloat(this.tradeBotLtcBook[0].botLtcQortAmount) + if (Number(listprice) <= Number(checkprice) && Number(listamount) <= Number(checkamount)) { + return { + qortAmount: item.qortAmount, + price: item.price, + foreignAmount: item.foreignAmount, + qortalCreator: item.qortalCreator, + qortalAtAddress: item.qortalAtAddress + } + } + }).filter(item => !!item) + + this.tradeBotAvailableLtcQortal.sort((a, b) => parseFloat(a.price) - parseFloat(b.price)) + + if (this.isEmptyArray(this.tradeBotAvailableLtcQortal) === true) { + return + } else { + this.checkAlice = this.tradeBotAvailableLtcQortal[0].qortalAtAddress + } + + await waitFor(1000) + + if (this.tradeBotLtcQortal.some(item => item.atAddress === this.checkAlice)) { + return + } else { + this.tradesAvailableLtcQortal = this.tradeBotAvailableLtcQortal + } + } + + await waitFor(1000) + + if (this.isEmptyArray(this.tradesAvailableLtcQortal) === true) { + return + } else { + const botprice = this.round(parseFloat(this.tradeBotLtcBook[0].botLtcPrice)) + const changeamount = parseFloat(this.tradeBotLtcBook[0].botLtcQortAmount) + const reduceamount = parseFloat(this.tradesAvailableLtcQortal[0].qortAmount) + const tradeataddress = this.tradesAvailableLtcQortal[0].qortalAtAddress + const newamount = this.round(parseFloat(changeamount - reduceamount)) + + this.reAddAmount = this.round(parseFloat(this.tradeBotLtcBook[0].botLtcQortAmount)) + this.reAddPrice = this.round(parseFloat(this.tradeBotLtcBook[0].botLtcPrice)) + + localStorage.removeItem(this.ltcWallet) + localStorage.setItem(this.ltcWallet, "") + + var oldLtcTradebook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + + const newLtcTradebookItem = { + botLtcQortAmount: newamount, + botLtcPrice: botprice + } + + oldLtcTradebook.push(newLtcTradebookItem) + + localStorage.setItem(this.ltcWallet, JSON.stringify(oldLtcTradebook)) + + this.tradeBotLtcBook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + + await waitFor(1000) + + this.botBuyAtAddress = tradeataddress + + this.buyAction() + + if (this.isEmptyArray(this.tradeBotLtcBook) === true) { + return + } else { + const botamount = parseFloat(this.tradeBotLtcBook[0].botLtcQortAmount) + + if (Number(botamount) === 0) { + this.removeLTCTradebook() + } else { + this.tradeBotLtcBook = this.tradeBotLtcBook + } + } + + if (this.isEmptyArray(this.tradeBotLtcBook) === true) { + return + } else { + const checkBotFunds = this.round(parseFloat(this.tradeBotLtcBook[0].botLtcQortAmount) * parseFloat(this.tradeBotLtcBook[0].botLtcPrice)) + const myBotFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + + if (Number(myBotFunds) < Number(checkBotFunds)) { + this.removeLTCTradebook() + } else { + this.tradeBotLtcBook = this.tradeBotLtcBook + } + } + } + } + + const getDoneTradeLTC = async () => { + this.isLoadingDoneTrades = true + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const doneLtcQortalUrl = `${nodeUrl}/crosschain/trades?foreignBlockchain=LITECOIN&minimumTimestamp=1597310000000&limit=0&reverse=true` + const myAddress = window.parent.reduxStore.getState().app.selectedAddress.address + + const doneLtcQortal = await fetch(doneLtcQortalUrl).then(response => { + return response.json() + }) + + this.doneTradesLtcQortal = doneLtcQortal.map(item => { + const searchAddress = item.buyerReceivingAddress + if (searchAddress == myAddress) { + return { + timestamp: item.tradeTimestamp, + foreignAmount: item.foreignAmount, + qortAmount: item.qortAmount + } + } + }).filter(item => !!item) + + this.isLoadingDoneTrades = false + setTimeout(getDoneTradeLTC, 600000) + } + + window.addEventListener('contextmenu', (event) => { + event.preventDefault() + this._textMenu(event)}, + { passive: true } + ) + + window.addEventListener('click', () => { + parentEpml.request('closeCopyTextMenu', null)}, + { passive: true } + ) + + window.addEventListener('storage', () => { + const checkLanguage = localStorage.getItem('qortalLanguage') + const checkTheme = localStorage.getItem('qortalTheme') + + use(checkLanguage) + + if (checkTheme === 'dark') { + this.theme = 'dark' + } else { + this.theme = 'light' + } + document.querySelector('html').setAttribute('theme', this.theme) + }) + + window.onkeyup = (e) => { + if (e.keyCode === 27) parentEpml.request('closeCopyTextMenu', null) + } + + this.ltcWallet = window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.address + + let configLoaded = false + + parentEpml.ready().then(() => { + parentEpml.subscribe('selected_address', async (selectedAddress) => { + this.selectedAddress = {} + selectedAddress = JSON.parse(selectedAddress) + if (!selectedAddress || Object.entries(selectedAddress).length === 0) return + this.selectedAddress = selectedAddress + this.ltcWwallet = window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.address + await this.updateAccountBalance() + await this.ltcTradebook() + }) + + parentEpml.subscribe('config', (c) => { + if (!configLoaded) { + setTimeout(getQortLtcPrice, 1) + setTimeout(getDoneTradeLTC, 1) + configLoaded = true + } + this.config = JSON.parse(c) + }) + + parentEpml.subscribe('copy_menu_switch', async (value) => { + if (value === 'false' && window.getSelection().toString().length !== 0) this.clearSelection() + }) + this.setForeignCoin() + }) + parentEpml.imReady() + + setTimeout(() => this.shadowRoot.querySelector('[slot="vaadin-grid-cell-content-3"]').setAttribute('title', 'Last Seen'), 3000) + + this.ltcTradebook() + await delay(3000) + filterMyPriceTradesLTC() + } + + changeTheme() { + const checkTheme = localStorage.getItem('qortalTheme') + if (checkTheme === 'dark') { + this.theme = 'dark'; + } else { + this.theme = '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) + } + } + + renderFetchText() { + return html`${translate("walletpage.wchange1")}` + } + + renderWarning() { + return html`NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode}` + } + + renderBotWarning() { + return html`NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode} ! AUTO BUY BOT IS STOPPED` + } + + exchangeRateQort() { + return html`${this.qortltc}` + } + + showAutoBuyGrid() { + return html`${this.tradeBotLTCTemplate()}` + } + + showAddToAutoBuyStore() { + this.addToLTCTradebook() + } + + ltcTradebook() { + if (localStorage.getItem(this.ltcWallet) === null) { + localStorage.setItem(this.ltcWallet, "") + } else { + this.tradeBotLtcBook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + } + } + + addToLTCTradebook() { + const addBotLtcQortAmount = this.shadowRoot.getElementById('autoBuyLTCQortAmountInput').value + const addBotLtcPrice = this.shadowRoot.getElementById('autoBuyLTCPriceInput').value + const addLtcQortAmount = this.round(parseFloat(addBotLtcQortAmount)) + const addLtcPrice = this.round(parseFloat(addBotLtcPrice)) + + var oldLtcTradebook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + + const newLtcTradebookItem = { + botLtcQortAmount: addLtcQortAmount, + botLtcPrice: addLtcPrice + } + + oldLtcTradebook.push(newLtcTradebookItem) + + localStorage.setItem(this.ltcWallet, JSON.stringify(oldLtcTradebook)) + + let ltctradebookstring = get("tradepage.tchange44") + parentEpml.request('showSnackBar', `${ltctradebookstring}`) + + this.closeLTCTradebookDialog() + this.tradeBotLtcBook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + this.firstUpdated() + } + + closeLTCTradebookDialog() { + this.shadowRoot.querySelector('#tradeBotLTCAddDialog').close() + this.clearTradeBotForm() + } + + removeLTCTradebook() { + localStorage.removeItem(this.ltcWallet) + localStorage.setItem(this.ltcWallet, "") + this.tradeBotLtcBook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + + this.autoBuyBotDisable = false + + let ltcstring = get("tradepage.tchange41") + parentEpml.request('showSnackBar', `${ltcstring}`) + this.tradesAvailableLtcQortal = [] + } + + exchangeRateForeign() { + parentEpml.request('apiCall', { + url: `/crosschain/price/LITECOIN?inverse=false` + }).then((res) => { + this.ltcqort = (Number(res) / 1e8).toFixed(8) + }) + return html`${this.ltcqort}` + } + + async updateWalletBalance() { + let _url = `/crosschain/ltc/walletbalance?apiKey=${this.getApiKey()}` + let _body = window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.derivedMasterPublicKey + + this.showGetWalletBance = true + this.showAddAutoBuy = false + + await parentEpml.request('apiCall', { + url: _url, + method: 'POST', + body: _body, + }).then((res) => { + if (isNaN(Number(res))) { + let snack1string = get("tradepage.tchange30") + parentEpml.request('showSnackBar', `${snack1string}`) + } else { + this.listedCoins.get(this.selectedCoin).balance = (Number(res) / 1e8).toFixed(8) + } + }) + + this.showGetWalletBance = false + this.showAddAutoBuy = true + } + + async setForeignCoin() { + let _this = this + this.selectedCoin = "LITECOIN" + + this.isLoadingOpenTrades = true + await this.createConnection() + + this._openOrdersGrid.querySelector('#priceColumn').headerRenderer = function (root) { + const priceString2 = get("tradepage.tchange9") + root.innerHTML = '' + priceString2 + ' (' + _this.listedCoins.get(_this.selectedCoin).coinCode + ')' + } + + this.autoBuyBotDisable = false + await this.updateWalletBalance() + } + + async reRenderOpenFilteredOrders() { + this.requestUpdate() + await this.updateComplete + this.isLoadingOpenTrades = false + } + + async reRenderMyOpenOrders() { + this.requestUpdate() + await this.updateComplete + this.isLoadingMyOpenOrders = false + } + + addAutoBuyAction() { + this.autoBuyWarning = false + this.clearTradeBotForm() + this.shadowRoot.querySelector('#tradeBot' + this.listedCoins.get(this.selectedCoin).coinCode + 'AddDialog').show() + } + + checkTradeBotValues() { + const checkTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const checkTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkAmount = this.round(parseFloat(checkTradeBotAmountInput)) + const checkPrice = this.round(parseFloat(checkTradeBotPriceInput)) + + if (Number(checkAmount) === 0) { + let amountString = get("tradepage.tchange34") + parentEpml.request('showSnackBar', `${amountString}`) + } else if (Number(checkPrice) === 0) { + let priceString = get("tradepage.tchange35") + parentEpml.request('showSnackBar', `${priceString}`) + } else { + this.showAddToAutoBuyStore() + } + } + + processOfferingTrade(offer) { + try { + if(this.listedCoins.get(offer.foreignBlockchain).name!='') { + const offerItem = { + ...offer, + qortAmount: parseFloat(offer.qortAmount), + price: parseFloat(offer.foreignAmount) / parseFloat(offer.qortAmount), + } + const addOffer = () => { + this.listedCoins.get(offer.foreignBlockchain).openOrders.unshift(offerItem) + } + const initOffer = () => { + this.listedCoins.get(offer.foreignBlockchain).openOrders.push(offerItem) + } + this.listedCoins.get(offer.foreignBlockchain).openOrders.length === 0 ? initOffer() : addOffer() + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + } catch(e) { + console.log("Error adding offer from "+offer.foreignBlockchain) + } + } + + processRedeemedTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + const offerItem = { + ...offer, + mode: 'SOLD', + } + this._myHistoricTradesGrid.items.unshift(offerItem) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } else if (offer.partnerQortalReceivingAddress === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + const offerItem = { + ...offer, + mode: 'BOUGHT', + } + this._myHistoricTradesGrid.items.unshift(offerItem) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + const addNewHistoricTrade = () => { + this._historicTradesGrid.items.unshift(offer) + this._historicTradesGrid.clearCache() + } + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? addNewHistoricTrade() : null + + } + } catch(e) { + console.log("Error processing redeemed trade offer from "+offer.foreignBlockchain) + } + } + + processTradingTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address && this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._openOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._openOrdersGrid.items.splice(index, 1) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + }) + this.listedCoins.get(offer.foreignBlockchain).openOrders = this.listedCoins.get(offer.foreignBlockchain).openOrders.filter((order) => order.qortalAtAddress !== offer.qortalAtAddress) + } + } catch(e) { + console.log("Error processing trading trade offer from "+offer.foreignBlockchain) + } + } + + processRefundedTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._myHistoricTradesGrid.items.unshift(offer) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + + } + } catch(e) { + console.log("Error processing refunded trade offer from "+offer.foreignBlockchain) + } + } + + processCancelledTrade(offer) { + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + + if (offer.qortalCreator === this.selectedAddress.address) { + if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { + this.updateWalletBalance() + } + this._myHistoricTradesGrid.items.unshift(offer) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null + } + this._openOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._openOrdersGrid.items.splice(index, 1) + this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null + } + }) + this.listedCoins.get(offer.foreignBlockchain).openOrders = this.listedCoins.get(offer.foreignBlockchain).openOrders.filter((order) => order.qortalAtAddress !== offer.qortalAtAddress) + this._stuckOrdersGrid.items.forEach((item, index) => { + if (item.qortalAtAddress === offer.qortalAtAddress) { + this._stuckOrdersGrid.items.splice(index, 1) + this._stuckOrdersGrid.clearCache() + } + }) + } + } catch(e) { + console.log("Error processing cancelled trade offer from "+offer.foreignBlockchain) + } + } + + /** + * TRADE OFFER STATES or MODE + * - OFFERING + */ + + processTradeOffers(offers) { + offers.forEach((offer) => { + if (offer.mode === 'OFFERING') { + this.processOfferingTrade(offer) + } + }) + } + + processTradeBotStates(tradeStates) { + + const LitecoinACCTv1 = (states) => { + states.reverse() + states.forEach((state) => { + if (state.creatorAddress === this.selectedAddress.address) { + if (state.tradeState == 'ALICE_WAITING_FOR_AT_LOCK') { + this.changeTradeBotState(state, 'BUYING') + } else if (state.tradeState == 'ALICE_DONE') { + this.handleCompletedState(state) + } else if (state.tradeState == 'ALICE_REFUNDING_A') { + this.changeTradeBotState(state, 'REFUNDING') + } else if (state.tradeState == 'ALICE_REFUNDED') { + this.handleCompletedState(state) + } + } + }) + } + + switch (this.selectedCoin) { + case 'LITECOIN': + LitecoinACCTv1(tradeStates) + break + default: + break + } + } + + changeTradeBotState(state, tradeState) { + this.isLoadingMyOpenOrders = true + const stateItem = { + ...state, + _tradeState: tradeState, + } + const item = this._myOrdersGrid.querySelector(`#${state.atAddress}`) + const addStateItem = () => { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.unshift(stateItem) + this._myOrdersGrid.clearCache() + } + const updateStateItem = () => { + this._myOrdersGrid.items.forEach((item, index) => { + if (item.atAddress === state.atAddress) { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.splice(index, 1) + this._myOrdersGrid.items.unshift(stateItem) + this._myOrdersGrid.clearCache() + } + }) + } + item ? updateStateItem() : addStateItem() + } + + handleCompletedState(state) { + this._myOrdersGrid.items.forEach((item, index) => { + if (item.atAddress === state.atAddress) { + this.reRenderMyOpenOrders() + this._myOrdersGrid.items.splice(index, 1) + this._myOrdersGrid.clearCache() + } + }) + } + + initSocket() { + let _relatedCoin = "" + let tradePresenceTxns = null + let offeringTrades = null + + self.addEventListener('message', function (event) { + switch (event.data.type) { + case 'open_orders': + offeringTrades = event.data.content + processOffersWithPresence() + break + case 'set_coin': + _relatedCoin = event.data.content + break + default: + break + } + }) + + const lessThanThirtyMinsAgo = (timestamp) => { + const THIRTYMINS = 1000 * 60 * 30 + const thirtyMinsAgo = Date.now() - THIRTYMINS + return timestamp > thirtyMinsAgo + } + + const filterOffersUsingTradePresence = (offeringTrade) => { + return offeringTrade.tradePresenceExpiry > Date.now(); + } + + const processOffersWithPresence = () => { + if (offeringTrades === null) return + + async function asyncForEach(array, callback) { + for (let index = 0; index < array.length; index++) { + await callback(array[index], index, array) + } + } + + const startOfferPresenceMapping = async () => { + + if (tradePresenceTxns !== null) { + await asyncForEach(tradePresenceTxns, async (tradePresence) => { + let offerIndex = offeringTrades.findIndex((offeringTrade) => offeringTrade.qortalCreatorTradeAddress === tradePresence.tradeAddress) + offerIndex !== -1 ? (offeringTrades[offerIndex].tradePresenceExpiry = tradePresence.timestamp) : null + }) + } + + let filteredOffers = offeringTrades.filter((offeringTrade) => filterOffersUsingTradePresence(offeringTrade)) + self.postMessage({ type: 'PRESENCE', data: { offers: offeringTrades, filteredOffers: filteredOffers, relatedCoin: _relatedCoin } }) + } + + startOfferPresenceMapping() + } + + const initTradeOffersWebSocket = (restarted = false) => { + let tradeOffersSocketCounter = 0 + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradeoffers?foreignBlockchain=FOREIGN_BLOCKCHAIN&includeHistoric=true` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + tradeOffersSocketCounter += 1 + } + socket.onmessage = (e) => { + e.relatedCoin = _relatedCoin + self.postMessage({ + type: 'TRADE_OFFERS', + data: e.data, + counter: tradeOffersSocketCounter, + isRestarted: restarted, + }) + tradeOffersSocketCounter += 1 + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradeOffersWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const initTradeBotWebSocket = (restarted = false) => { + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradebot?foreignBlockchain=FOREIGN_BLOCKCHAIN` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + } + socket.onmessage = (e) => { + e.relatedCoin = _relatedCoin + self.postMessage({ + type: 'TRADE_BOT', + data: e.data, + isRestarted: restarted, + }) + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradeBotWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const initTradePresenceWebSocket = (restarted = false) => { + let socketTimeout + let socketLink = `ws://NODEURL/websockets/crosschain/tradepresence` + const socket = new WebSocket(socketLink) + socket.onopen = () => { + setTimeout(pingSocket, 50) + } + socket.onmessage = (e) => { + tradePresenceTxns = JSON.parse(e.data) + processOffersWithPresence() + restarted = false + } + socket.onclose = () => { + clearTimeout(socketTimeout) + restartTradePresenceWebSocket() + } + socket.onerror = (e) => { + clearTimeout(socketTimeout) + } + const pingSocket = () => { + socket.send('ping') + socketTimeout = setTimeout(pingSocket, 150000) + } + } + + const restartTradePresenceWebSocket = () => { + setTimeout(() => initTradePresenceWebSocket(true), 50) + } + + const restartTradeOffersWebSocket = () => { + setTimeout(() => initTradeOffersWebSocket(true), 50) + } + + const restartTradeBotWebSocket = () => { + setTimeout(() => initTradeBotWebSocket(true), 50) + } + + initTradeOffersWebSocket() + initTradePresenceWebSocket() + initTradeBotWebSocket() + } + + async buyAction() { + const qortalAtAddress = this.botBuyAtAddress + let _foreignKey = this.selectedAddress.ltcWallet.derivedMasterPrivateKey + + const makeRequest = async () => { + const response = await parentEpml.request('tradeBotRespondRequest', { + atAddress: qortalAtAddress, + foreignKey: _foreignKey, + receivingAddress: this.selectedAddress.address, + }) + return response + } + + const manageResponse = (response) => { + if (response === true) { + this.isBuyLoading = false + this.buyBtnDisable = true + let snack5string = get("tradepage.tchange23") + parentEpml.request('showSnackBar', `${snack5string}`) + } else if (response === false) { + this.isBuyLoading = false + this.buyBtnDisable = false + + localStorage.removeItem(this.ltcWallet) + localStorage.setItem(this.ltcWallet, "") + + var oldLtcTradebook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + + const newLtcTradebookItem = { + botLtcQortAmount: this.reAddAmount, + botLtcPrice: this.reAddPrice + } + + oldLtcTradebook.push(newLtcTradebookItem) + + localStorage.setItem(this.ltcWallet, JSON.stringify(oldLtcTradebook)) + + this.tradeBotLtcBook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + + let snack6string = get("tradepage.tchange24") + parentEpml.request('showSnackBar', `${snack6string}`) + } else { + this.isBuyLoading = false + this.buyBtnDisable = false + + localStorage.removeItem(this.ltcWallet) + localStorage.setItem(this.ltcWallet, "") + + var oldLtcTradebook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + + const newLtcTradebookItem = { + botLtcQortAmount: this.reAddAmount, + botLtcPrice: this.reAddPrice + } + + oldLtcTradebook.push(newLtcTradebookItem) + + localStorage.setItem(this.ltcWallet, JSON.stringify(oldLtcTradebook)) + + this.tradeBotLtcBook = JSON.parse(localStorage.getItem(this.ltcWallet) || "[]") + + let snack7string = get("tradepage.tchange25") + parentEpml.request('showSnackBar', `${snack7string}: ${response.message}`) + } + } + const res = await makeRequest() + manageResponse(res) + } + + updateAccountBalance() { + clearTimeout(this.updateAccountBalanceTimeout) + parentEpml.request('apiCall', { + url: `/addresses/balance/${this.selectedAddress.address}?apiKey=${this.getApiKey()}`, + }).then((res) => { + this.listedCoins.get("QORTAL").balance = res + this.updateAccountBalanceTimeout = setTimeout(() => this.updateAccountBalance(), 10000) + }) + } + + _checkBuyAmount(e) { + const targetAmount = e.target.value + const target = e.target + + if (targetAmount.length === 0) { + this.isValidAmount = false + this.sellBtnDisable = true + e.target.blur() + e.target.focus() + e.target.invalid = true + } else { + this.buyBtnDisable = false + } + + e.target.blur() + e.target.focus() + + e.target.validityTransform = (newValue, nativeValidity) => { + if (newValue.includes('-') === true) { + this.buyBtnDisable = true + return { + valid: false, + } + } else if (!nativeValidity.valid) { + if (newValue.includes('.') === true) { + let myAmount = newValue.split('.') + if (myAmount[1].length > 8) { + this.buyBtnDisable = true + } else { + this.buyBtnDisable = false + return { + valid: true, + } + } + } + } else { + this.buyBtnDisable = false + } + } + } + + checkTradeBotAmount(e) { + const targetAmount = e.target.value + const target = e.target + this.autoBuyWarning = false + + if (targetAmount.length === 0) { + this.isValidAmount = false + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + e.target.blur() + e.target.focus() + e.target.invalid = true + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + } + + e.target.blur() + e.target.focus() + + e.target.validityTransform = (newValue, nativeValidity) => { + if (newValue.includes('-') === true) { + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + return { + valid: false, + } + } else if (!nativeValidity.valid) { + if (newValue.includes('.') === true) { + let myAmount = newValue.split('.') + if (myAmount[1].length > 8) { + this.autoBuyBtnDisable = true + this.autoBuyWarning = false + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + return { + valid: true, + } + } + } + } else { + const buyTradeBotAmountInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value + const buyTradeBotPriceInput = this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value + const checkFunds = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + const myFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(myFunds) > Number(checkFunds)) { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = false + this.autoBuyWarning = false + } else { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.round(parseFloat(buyTradeBotAmountInput) * parseFloat(buyTradeBotPriceInput)) + this.autoBuyBtnDisable = true + this.autoBuyWarning = true + } + } + } + } + + getApiKey() { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node]; + let apiKey = myNode.apiKey; + return apiKey; + } + + clearSelection() { + window.getSelection().removeAllRanges() + window.parent.getSelection().removeAllRanges() + } + + _textMenu(event) { + const getSelectedText = () => { + var text = '' + if (typeof window.getSelection != 'undefined') { + text = window.getSelection().toString() + } else if (typeof this.shadowRoot.selection != 'undefined' && this.shadowRoot.selection.type == 'Text') { + text = this.shadowRoot.selection.createRange().text + } + return text + } + + const checkSelectedTextAndShowMenu = () => { + let selectedText = getSelectedText() + if (selectedText && typeof selectedText === 'string') { + let _eve = { + pageX: event.pageX, + pageY: event.pageY, + clientX: event.clientX, + clientY: event.clientY, + } + let textMenuObject = { + selectedText: selectedText, + eventObject: _eve, + isFrame: true, + } + parentEpml.request('openCopyTextMenu', textMenuObject) + } + } + checkSelectedTextAndShowMenu() + } + + clearTradeBotForm() { + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'QortAmountInput').value = this.initialBotAmount + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'PriceInput').value = this.initialAmount + this.shadowRoot.getElementById('autoBuy' + this.listedCoins.get(this.selectedCoin).coinCode + 'TotalInput').value = this.initialBotAmount + this.autoBuyBtnDisable = true + } + + isEmptyArray(arr) { + if (!arr) { + return true + } + return arr.length === 0 + } + + round(number) { + let result = (Math.round(parseFloat(number) * 1e8) / 1e8).toFixed(8) + return result + } + + inlineWorker(passedFunction, modifiers) { + let parsedFunction = `` + + modifiers.forEach((modifier) => { + let regex = new RegExp(modifier.searchValue, 'g') + parsedFunction = parsedFunction.length === 0 ? `(function ${passedFunction.toString().trim().replace(regex, modifier.replaceValue)})()` : parsedFunction.toString().trim().replace(regex, modifier.replaceValue) + }) + + const workerUrl = URL.createObjectURL(new Blob([parsedFunction], { type: 'text/javascript' })) + const worker = new Worker(workerUrl) + URL.revokeObjectURL(workerUrl) + return worker + } + + clearPaneCache() { + this._openOrdersGrid.clearCache() + this._myOrdersGrid.clearCache() + } + + createConnection() { + if (workers.get(this.selectedCoin).tradesConnectedWorker !== null) { + this.isLoadingOpenTrades = false + return + } + + const handleMessage = (message) => { + switch (message.type) { + case 'TRADE_OFFERS': + if (!message.isRestarted) { + this.listedCoins.get(this.selectedCoin).tradeOffersSocketCounter = message.counter + this.processTradeOffers(JSON.parse(message.data)) + this.listedCoins.get(this.selectedCoin).tradeOffersSocketCounter === 1 ? this.clearPaneCache() : null + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage(this.listedCoins.get(this.selectedCoin).openOrders) + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage({ type: "open_orders", content: this.listedCoins.get(this.selectedCoin).openOrders }) + } + return null + case 'TRADE_BOT': + if (!message.isRestarted) this.processTradeBotStates(JSON.parse(message.data)) + return null + case 'PRESENCE': + this.listedCoins.get(message.data.relatedCoin).openOrders = message.data.offers + this.listedCoins.get(message.data.relatedCoin).openFilteredOrders = message.data.filteredOffers + this.reRenderOpenFilteredOrders() + return null + default: + break + } + } + + let myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + let nodeUrl = myNode.domain + ':' + myNode.port + + const modifiers = [ + { searchValue: 'NODEURL', replaceValue: nodeUrl }, + { searchValue: 'FOREIGN_BLOCKCHAIN', replaceValue: this.selectedCoin }, + ] + + workers.get(this.selectedCoin).tradesConnectedWorker = this.inlineWorker(this.initSocket, modifiers) + + workers.get(this.selectedCoin).tradesConnectedWorker.addEventListener('message', function (event) { handleMessage(event.data) }, { passive: true }) + + workers.get(this.selectedCoin).tradesConnectedWorker.postMessage({ type: "set_coin", content: this.selectedCoin }) + + } +} + +window.customElements.define('trade-bot-ltc', TradeBotLTC) \ No newline at end of file diff --git a/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js b/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js index d8fdc5e2..b850b33c 100644 --- a/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js +++ b/qortal-ui-plugins/plugins/core/trade-portal/trade-portal.src.js @@ -9,12 +9,15 @@ registerTranslateConfig({ import '@material/mwc-button' import '@material/mwc-textfield' +import '@material/mwc-icon' import '@material/mwc-icon-button' import '@material/mwc-dialog' import '@material/mwc-tab-bar' import '@material/mwc-tab' import '@material/mwc-list/mwc-list-item' import '@material/mwc-select' +import '@polymer/iron-icons/iron-icons.js' +import '@polymer/paper-icon-button/paper-icon-button.js' import '@polymer/paper-spinner/paper-spinner-lite.js' import '@vaadin/grid' import '@vaadin/grid/vaadin-grid-sorter' @@ -33,6 +36,9 @@ class TradePortal extends LitElement { isSellLoading: { type: Boolean }, isBuyLoading: { type: Boolean }, buyBtnDisable: { type: Boolean }, + autoBuyWarning: { type: Boolean }, + autoBuyBtnDisable: { type: Boolean }, + autoBuyBotDisable: { type: Boolean }, initialAmount: { type: Number }, cancelBtnDisable: { type: Boolean }, cancelStuckOfferBtnDisable: { type: Boolean }, @@ -40,7 +46,14 @@ class TradePortal extends LitElement { isLoadingHistoricTrades: { type: Boolean }, isLoadingOpenTrades: { type: Boolean }, isLoadingMyOpenOrders: { type: Boolean }, + showGetWalletBance: { type: Boolean }, theme: { type: String, reflect: true }, + btcWallet: { type: String }, + ltcWallet: { type: String }, + dogeWallet: { type: String }, + dgbWallet: { type: String }, + rvnWallet: { type: String }, + arrrWallet: { type: String }, arrrWalletAddress: { type: String }, qortbtc: { type: Number }, qortltc: { type: Number }, @@ -53,7 +66,13 @@ class TradePortal extends LitElement { dogeqort: { type: Number }, dgbqort: { type: Number }, rvnqort: { type: Number }, - arrrqort: { type: Number } + arrrqort: { type: Number }, + tradeInfoAccountName: { type: String }, + tradeImageUrl: { type: String }, + tradeAddressResult: { type: Array }, + displayTradeAddress: { type: String }, + displayTradeLevel: { type: String }, + displayTradeBalance: { type: String } } } @@ -73,6 +92,7 @@ class TradePortal extends LitElement { --mdc-select-ink-color: var(--black); --mdc-theme-surface: var(--white); --mdc-dialog-content-ink-color: var(--black); + --mdc-dialog-shape-radius: 25px; --paper-input-container-focus-color: var(--mdc-theme-primary); --lumo-primary-text-color: rgb(0, 167, 245); --lumo-primary-color-50pct: rgba(0, 167, 245, 0.5); @@ -121,6 +141,16 @@ class TradePortal extends LitElement { --mdc-icon-button-size: 32px; color: var(--black); } + .btn-clear-bot { + --mdc-icon-button-size: 32px; + color: var(--black); + float: right; + } + .btn-info { + color: #03a9f4; + --mdc-icon-size: 16px; + padding-top: 3px; + } #tab-sell[active] { --mdc-theme-primary: rgb(255, 89, 89); } @@ -133,9 +163,102 @@ class TradePortal extends LitElement { padding: 1em; box-shadow: 0 0.3px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 1px -1px rgba(0, 0, 0, 0.12), 0 1px 2px 0 rgba(0, 0, 0, 0.2); } - h2 { - margin: 0; - } + h2 { + margin: 10px 0; + } + + h4 { + margin: 5px 0; + } + + p { + font-size: 14px; + line-height: 21px; + } + + .card-body { + background-color: var(--white); + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + min-height: 100vh; + margin: 0; + } + + .card-container { + background-color: var(--white); + border-radius: 5px; + color: var(--black); + padding-top: 30px; + position: relative; + width: 350px; + max-width: 100%; + text-align: center; + } + + .card-container .level { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + left: 30px; + } + + .card-container .founder { + color: #ffffff; + background-color: #03a9f4; + border-radius: 3px; + font-size: 14px; + font-weight: bold; + padding: 3px 7px; + position: absolute; + top: 30px; + right: 30px; + } + + .card-container .round { + width: 96px; + height: 96px; + border: 1px solid #03a9f4; + border-radius: 50%; + padding: 2px; + } + + .card-container .badge { + width: 200px; + height: 135px; + border: 1px solid transparent; + border-radius: 10%; + padding: 2px; + } + + .userdata { + background-color: #1F1A36; + text-align: left; + padding: 15px; + margin-top: 30px; + } + + .userdata ul { + list-style-type: none; + margin: 0; + padding: 0; + } + + .userdata ul li { + border: 1px solid #2D2747; + border-radius: 2px; + display: inline-block; + font-size: 12px; + margin: 0 7px 7px 0; + padding: 7px; + } + h2, h3, h4, @@ -172,6 +295,13 @@ class TradePortal extends LitElement { flex-flow: column; height: 100%; } + .box-bot { + margin: 0; + padding: 0; + display: flex; + flex-flow: column; + height: 150px; + } #first-trade-section { margin-bottom: 10px; } @@ -195,6 +325,12 @@ class TradePortal extends LitElement { .open-trades { text-align: center; } + .open-market-container { + text-align: center; + } + .trade-bot-container { + text-align: center; + } .no-last-seen { background: rgb(255, 89, 89); padding: 9px 1.3px; @@ -202,9 +338,6 @@ class TradePortal extends LitElement { width: 1rem; margin: 0 auto; } - .open-market-container { - text-align: center; - } .card { padding: 1em; border: 1px var(--tradeborder) solid; @@ -214,6 +347,15 @@ class TradePortal extends LitElement { justify-content: space-evenly; min-height: inherit; } + .card-bot { + padding: 1em; + flex: 1 1 auto; + display: flex; + flex-flow: column; + justify-content: space-evenly; + width: 350px; + min-height: inherit; + } .cancel { --mdc-theme-primary: rgb(255, 89, 89); } @@ -279,10 +421,15 @@ class TradePortal extends LitElement { .sell-button { --mdc-theme-primary: rgb(255, 89, 89); } + .trade-bot-button { + margin-top: 20px; + margin-bottom: 20px; + --mdc-theme-primary: rgba(55, 160, 51, 0.9); + } .full-width { background-color: var(--white); - border: 2px #ddd solid; - height: 100px; + border: 2px var(--black); + height: 200px; text-align: center; } vaading-grid { @@ -314,7 +461,7 @@ class TradePortal extends LitElement { } mwc-select#coinSelectionMenu { font-size: 24px; - width:220px; + width:220px; } mwc-select#coinSelectionMenu mwc-list-item { line-height: 30px; @@ -353,6 +500,22 @@ class TradePortal extends LitElement { height: 26px; padding-left: 45px; } + .warning-text { + animation: blinker 1.5s linear infinite; + display: inline; + float: left; + margin-bottom: 5px; + color: rgb(255, 89, 89); + } + .warning-bot-text { + animation: blinker 1.5s linear infinite; + display: inline; + text-align: center; + color: rgb(255, 89, 89); + } + .red { + --mdc-theme-primary: #F44336; + } @-webkit-keyframes loadingAnimation { 0% { -webkit-transform: rotate(0deg); @@ -372,7 +535,12 @@ class TradePortal extends LitElement { -webkit-transform: rotate(360deg); transform: rotate(360deg); } - } + } + @keyframes blinker { + 50% { + opacity: 0; + } + } @media (min-width: 701px) { * { } @@ -389,7 +557,7 @@ class TradePortal extends LitElement { } #second-trade-section { display: grid; - grid-template-columns: 2fr 1fr; + grid-template-columns: 2fr 1fr; grid-auto-rows: max(450px); column-gap: 0.5em; row-gap: 0.4em; @@ -397,6 +565,16 @@ class TradePortal extends LitElement { align-items: stretch; margin-bottom: 10px; } + #third-trade-section { + display: grid; + grid-template-columns: 1fr 2fr 1fr; + grid-auto-rows: max(200px); + column-gap: 0.5em; + row-gap: 0.4em; + justify-items: stretch; + align-items: stretch; + margin-bottom: 10px; + } } ` } @@ -557,6 +735,9 @@ class TradePortal extends LitElement { this.sellBtnDisable = false this.isSellLoading = false this.buyBtnDisable = true + this.autoBuyWarning = false + this.autoBuyBtnDisable = true + this.autoBuyBotDisable = false this.isBuyLoading = false this.initialAmount = 0 this.cancelBtnDisable = false @@ -564,7 +745,14 @@ class TradePortal extends LitElement { this.isLoadingHistoricTrades = true this.isLoadingOpenTrades = true this.isLoadingMyOpenOrders = false + this.showGetWalletBance = true this.theme = localStorage.getItem('qortalTheme') ? localStorage.getItem('qortalTheme') : 'light' + this.btcWallet = '' + this.ltcWallet = '' + this.dogeWallet = '' + this.dgbWallet = '' + this.rvnWallet = '' + this.arrrWallet = '' this.arrrWalletAddress = '' this.qortbtc = 0 this.qortltc = 0 @@ -578,9 +766,14 @@ class TradePortal extends LitElement { this.dgbqort = 0 this.rvnqort = 0 this.arrrqort = 0 + this.tradeInfoAccountName = '' + this.tradeImageUrl = '' + this.tradeAddressResult = [] + this.displayTradeAddress = '' + this.displayTradeLevel = '' + this.displayTradeBalance = '' } - // TODO: Move each template to a separate components! Maybe historicTradesTemplate() { return html`
@@ -613,7 +806,7 @@ class TradePortal extends LitElement {
- ` + ` } openTradesTemplate() { @@ -629,9 +822,8 @@ class TradePortal extends LitElement { resizable header="${translate("tradepage.tchange8")} (QORT)" id="qortAmountColumn" - path="qortAmount" .renderer=${(root, column, data) => { - render(html` ${this.round(data.item.qortAmount)} `, root) + render(html`${this.round(data.item.qortAmount)}`, root) }} > @@ -640,9 +832,8 @@ class TradePortal extends LitElement { resizable header="${translate("tradepage.tchange9")} (${this.listedCoins.get(this.selectedCoin).coinCode})" id="priceColumn" - path="price" .renderer=${(root, column, data) => { - render(html` ${this.round(data.item.price)} `, root) + render(html`${this.round(data.item.price)}`, root) }} > @@ -650,8 +841,9 @@ class TradePortal extends LitElement { auto-width resizable header="${translate("tradepage.tchange10")} (${this.listedCoins.get(this.selectedCoin).coinCode})" + id="foreignAmountColumn" .renderer=${(root, column, data) => { - render(html` ${data.item.foreignAmount} `, root) + render(html`${data.item.foreignAmount}`, root) }} > @@ -659,8 +851,18 @@ class TradePortal extends LitElement { auto-width resizable header="${translate("tradepage.tchange13")}" + id="qortalCreatorColumn" + .renderer=${(root, column, data) => { + render(html`${data.item.qortalCreator}`, root) + }} + > + + { - render(html` ${data.item.qortalCreator} `, root) + render(html`info`, root) }} > @@ -761,7 +963,7 @@ class TradePortal extends LitElement { style="width: 100%; color: var(--black);" id="sellAmountInput" required - label="" + label="" placeholder="0.0000" @input="${(e) => { this._checkSellAmount(e) }}" type="number" @@ -776,7 +978,7 @@ class TradePortal extends LitElement { style="width: 100%; color: var(--black);" id="sellPriceInput" required - label="" + label="" placeholder="0.0000" @input="${(e) => { this._checkSellAmount(e) }}" type="number" @@ -865,7 +1067,7 @@ class TradePortal extends LitElement { auto-width resizable header="${translate("tradepage.tchange10")} (${this.listedCoins.get(this.selectedCoin).coinCode})" - path="foreignAmount" + path="foreignAmount" > +
+
+
+

${translate("tradepage.tchange33")} ${this.listedCoins.get(this.selectedCoin).coinCode} ${translate("tradepage.tchange40")}

+

1 QORT = ${this.exchangeRateQort()} ${this.listedCoins.get(this.selectedCoin).coinCode}

+
+
+
-
-

${translate("tradepage.tchange33")}

-

1 QORT = ${this.exchangeRateQort()} ${this.listedCoins.get(this.selectedCoin).coinCode}

-
@@ -990,6 +1196,18 @@ class TradePortal extends LitElement {
${translate("general.close")}
+ + +
+ ${translate("mintingpage.mchange27")} ${this.displayTradeLevel} + ${this.tradeFounderBadge()} + ${this.tradeAvatarImage()} +

${this.tradeInfoAccountName}

+

${this.displayTradeAddress}

+

${translate("explorerpage.exp2")}: ${this.displayTradeBalance} QORT

+
+ ${translate("general.close")} +
` } @@ -1006,7 +1224,6 @@ class TradePortal extends LitElement { this.displayTabContent('buy') }, 0) - // Set Trade Panes this._openOrdersGrid = this.shadowRoot.getElementById('openOrdersGrid') this._openOrdersGrid.querySelector('#priceColumn').headerRenderer = function (root) { @@ -1024,8 +1241,6 @@ class TradePortal extends LitElement { this._myHistoricTradesGrid = this.shadowRoot.getElementById('myHistoricTradesGrid') this._stuckOrdersGrid = this.shadowRoot.getElementById('stuckOrdersGrid') - this.getOpenOrdersGrid() - const getQortBtcPrice = () => { parentEpml.request("apiCall", { url: `/crosschain/price/BITCOIN?inverse=true` }).then((res) => { setTimeout(() => { this.qortbtc = (Number(res) / 1e8).toFixed(8) }, 1) @@ -1097,6 +1312,13 @@ class TradePortal extends LitElement { if (e.keyCode === 27) parentEpml.request('closeCopyTextMenu', null) } + this.btcWallet = window.parent.reduxStore.getState().app.selectedAddress.btcWallet.address + this.ltcWallet = window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.address + this.dogeWallet = window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.address + this.dgbWallet = window.parent.reduxStore.getState().app.selectedAddress.dgbWallet.address + this.rvnWallet = window.parent.reduxStore.getState().app.selectedAddress.rvnWallet.address + this.arrrWallet = window.parent.reduxStore.getState().app.selectedAddress.arrrWallet.address + let configLoaded = false parentEpml.ready().then(() => { @@ -1105,6 +1327,14 @@ class TradePortal extends LitElement { selectedAddress = JSON.parse(selectedAddress) if (!selectedAddress || Object.entries(selectedAddress).length === 0) return this.selectedAddress = selectedAddress + + this.btcWallet = window.parent.reduxStore.getState().app.selectedAddress.btcWallet.address + this.ltcWwallet = window.parent.reduxStore.getState().app.selectedAddress.ltcWallet.address + this.dogeWallet = window.parent.reduxStore.getState().app.selectedAddress.dogeWallet.address + this.dgbWallet = window.parent.reduxStore.getState().app.selectedAddress.dgbWallet.address + this.rvnWallet = window.parent.reduxStore.getState().app.selectedAddress.rvnWallet.address + this.arrrWallet = window.parent.reduxStore.getState().app.selectedAddress.arrrWallet.address + this.updateAccountBalance() }) @@ -1135,7 +1365,6 @@ class TradePortal extends LitElement { }) parentEpml.imReady() - // Set Last Seen column's title on OpenOrders grid setTimeout(() => this.shadowRoot.querySelector('[slot="vaadin-grid-cell-content-3"]').setAttribute('title', 'Last Seen'), 3000) } @@ -1160,6 +1389,14 @@ class TradePortal extends LitElement { } } + renderFetchText() { + return html`${translate("walletpage.wchange1")}` + } + + renderWarning() { + return html`NOT ENOUGH ${this.listedCoins.get(this.selectedCoin).coinCode}` + } + exchangeRateQort() { if (this.listedCoins.get(this.selectedCoin).coinCode === "BTC") { return html`${this.qortbtc}` @@ -1222,7 +1459,7 @@ class TradePortal extends LitElement { } } - updateWalletBalance() { + async updateWalletBalance() { let _url = `` let _body = null @@ -1255,7 +1492,9 @@ class TradePortal extends LitElement { break } - parentEpml.request('apiCall', { + this.showGetWalletBance = true + + await parentEpml.request('apiCall', { url: _url, method: 'POST', body: _body, @@ -1267,6 +1506,8 @@ class TradePortal extends LitElement { this.listedCoins.get(this.selectedCoin).balance = (Number(res) / 1e8).toFixed(8) } }) + + this.showGetWalletBance = false } async fetchWalletAddress(coin) { @@ -1288,24 +1529,20 @@ class TradePortal extends LitElement { } } - setForeignCoin(coin,beingInitialized) { + async setForeignCoin(coin,beingInitialized) { let _this = this this.selectedCoin = coin let coinSelectionMenu=this.shadowRoot.getElementById("coinSelectionMenu") if(beingInitialized){ - //apply padding to the container coinSelectionMenu.shadowRoot.querySelector('.mdc-select--outlined .mdc-select__anchor').setAttribute('style', 'padding-left: 60px;') - //create the coin pair container let pairIconContainer = document.createElement("span") let pairicon = (_this.listedCoins.get(_this.selectedCoin).coinCode).toLowerCase() pairIconContainer.setAttribute("class","pairIconContainer") pairIconContainer.setAttribute('style', 'left: 10px;top: 50%;transform: translate(0, -50%);height: 26px;width: 45px;position: absolute;background-repeat: no-repeat;background-size: cover;background-image: url(/img/qort'+pairicon+'.png);') - - //appending the coin pair container to the menu coinSelectionMenu.shadowRoot.querySelector('.mdc-select--outlined .mdc-select__anchor').appendChild(pairIconContainer) - }else{//we need just to update the existing pair icon container + }else{ let pairIconContainer = coinSelectionMenu.shadowRoot.querySelector(".mdc-select--outlined .mdc-select__anchor span.pairIconContainer") let pairicon = (_this.listedCoins.get(_this.selectedCoin).coinCode).toLowerCase() pairIconContainer.style.backgroundImage='url(/img/qort'+pairicon+'.png)' @@ -1320,7 +1557,7 @@ class TradePortal extends LitElement { } this.clearSellForm() this.clearBuyForm() - this.updateWalletBalance() + await this.updateWalletBalance() this.fetchWalletAddress(coin) } @@ -1350,29 +1587,91 @@ class TradePortal extends LitElement { } fillBuyForm(sellerRequest) { - this.shadowRoot.getElementById('buyAmountInput').value = parseFloat(sellerRequest.qortAmount) - this.shadowRoot.getElementById('buyPriceInput').value = this.round(parseFloat(sellerRequest.foreignAmount) / parseFloat(sellerRequest.qortAmount)) - this.shadowRoot.getElementById('buyTotalInput').value = parseFloat(sellerRequest.foreignAmount) - this.shadowRoot.getElementById('qortalAtAddress').value = sellerRequest.qortalAtAddress - this.buyBtnDisable = false + this.shadowRoot.getElementById('buyAmountInput').value = parseFloat(sellerRequest.item.qortAmount) + this.shadowRoot.getElementById('buyPriceInput').value = this.round(parseFloat(sellerRequest.item.foreignAmount) / parseFloat(sellerRequest.item.qortAmount)) + this.shadowRoot.getElementById('buyTotalInput').value = parseFloat(sellerRequest.item.foreignAmount) + this.shadowRoot.getElementById('qortalAtAddress').value = sellerRequest.item.qortalAtAddress + const buyFunds = this.round(parseFloat(sellerRequest.item.foreignAmount)) + const haveFunds = this.round(parseFloat(this.listedCoins.get(this.selectedCoin).balance)) + if (Number(haveFunds) > Number(buyFunds)) { + this.buyBtnDisable = false + this.autoBuyWarning = false + } else { + this.buyBtnDisable = true + this.autoBuyWarning = true + } } - getOpenOrdersGrid() { - const myGrid = this.shadowRoot.querySelector('#openOrdersGrid') - myGrid.addEventListener( - 'click', (e) => { - let myItem = myGrid.getEventContext(e).item - if (myItem !== undefined && myItem.qortalCreator !== this.selectedAddress.address) { - this.fillBuyForm(myItem) - } - }, - { passive: true } - ) + async getAllForAddress(tradeAddress) { + await this.getAddressTradeInfo(tradeAddress) + await this.getAddressTradeAvatar(tradeAddress) + await this.getAddressTradeBalance(tradeAddress) + this.displayTradeAddress = this.tradeAddressResult.address + this.displayTradeLevel = this.tradeAddressResult.level + this.shadowRoot.querySelector('#sellerDialog').show() + } + + async getAddressTradeInfo(tradeInfoAddress) { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const tradeInfoAddressUrl = `${nodeUrl}/addresses/${tradeInfoAddress}` + + const qortalTradeAddressInfo = await fetch(tradeInfoAddressUrl).then(response => { + return response.json() + }) + + this.tradeAddressResult = qortalTradeAddressInfo + } + + async getAddressTradeAvatar(tradeAvatarAddress) { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const tradeNameUrl = `${nodeUrl}/names/address/${tradeAvatarAddress}?limit=0&reverse=true` + + await fetch(tradeNameUrl).then(res => { + return res.json() + }).then(jsonRes => { + if(jsonRes.length) { + jsonRes.map (item => { + this.tradeInfoAccountName = item.name + this.tradeImageName = item.name + }) + } else { + this.tradeInfoAccountName = "No registered name" + this.tradeImageName = tradeAvatarAddress + } + }) + + const myTradeImageUrl = `${nodeUrl}/arbitrary/THUMBNAIL/${this.tradeImageName}/qortal_avatar?async=true&apiKey=${this.getApiKey()}` + this.tradeImageUrl = myTradeImageUrl + } + + async getAddressTradeBalance(tradeBalanceAddress) { + const myNode = window.parent.reduxStore.getState().app.nodeConfig.knownNodes[window.parent.reduxStore.getState().app.nodeConfig.node] + const nodeUrl = myNode.protocol + '://' + myNode.domain + ':' + myNode.port + const tradeBalanceAddressUrl = `${nodeUrl}/addresses/balance/${tradeBalanceAddress}` + + const qortalTradeBalanceInfo = await fetch(tradeBalanceAddressUrl).then(res => { + return res.json() + }) + this.displayTradeBalance = qortalTradeBalanceInfo + } + + tradeAvatarImage() { + return html`` + } + + tradeFounderBadge() { + if (this.tradeAddressResult.flags === 1) { + return html`${translate("explorerpage.exp6")}` + } else { + return html`` + } } processOfferingTrade(offer) { - try{ - if(this.listedCoins.get(offer.foreignBlockchain).name!=''){//check if the foreignBlockchain value is part of supported blockchains + try { + if(this.listedCoins.get(offer.foreignBlockchain).name!='') { const offerItem = { ...offer, qortAmount: parseFloat(offer.qortAmount), @@ -1387,18 +1686,16 @@ class TradePortal extends LitElement { this.listedCoins.get(offer.foreignBlockchain).openOrders.length === 0 ? initOffer() : addOffer() this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._openOrdersGrid.clearCache() : null } - }catch(e){ + } catch(e) { console.log("Error adding offer from "+offer.foreignBlockchain) } } processRedeemedTrade(offer) { - try{ - if(this.listedCoins.get(offer.foreignBlockchain).name!=''){//check if the foreignBlockchain value is part of supported blockchains - - // If trade is mine, add it to my historic trades and also add it to historic trades + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { + if (offer.qortalCreator === this.selectedAddress.address) { - // Check and Update Wallet Balance if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { this.updateWalletBalance() } @@ -1406,11 +1703,9 @@ class TradePortal extends LitElement { ...offer, mode: 'SOLD', } - // Add to my historic trades this._myHistoricTradesGrid.items.unshift(offerItem) this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null } else if (offer.partnerQortalReceivingAddress === this.selectedAddress.address) { - // Check and Update Wallet Balance if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { this.updateWalletBalance() } @@ -1418,11 +1713,9 @@ class TradePortal extends LitElement { ...offer, mode: 'BOUGHT', } - // Add to my historic trades this._myHistoricTradesGrid.items.unshift(offerItem) this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null } - // Add to historic trades const addNewHistoricTrade = () => { this._historicTradesGrid.items.unshift(offer) this._historicTradesGrid.clearCache() @@ -1430,18 +1723,16 @@ class TradePortal extends LitElement { this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? addNewHistoricTrade() : null } - }catch(e){ + } catch(e) { console.log("Error processing redeemed trade offer from "+offer.foreignBlockchain) } } processTradingTrade(offer) { - try{ - if(this.listedCoins.get(offer.foreignBlockchain).name!=''){//check if the foreignBlockchain value is part of supported blockchains + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { - // Remove from open market orders if (offer.qortalCreator === this.selectedAddress.address && this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { - // Check and Update Wallet Balance this.updateWalletBalance() } this._openOrdersGrid.items.forEach((item, index) => { @@ -1452,41 +1743,37 @@ class TradePortal extends LitElement { }) this.listedCoins.get(offer.foreignBlockchain).openOrders = this.listedCoins.get(offer.foreignBlockchain).openOrders.filter((order) => order.qortalAtAddress !== offer.qortalAtAddress) } - }catch(e){ + } catch(e) { console.log("Error processing trading trade offer from "+offer.foreignBlockchain) } } processRefundedTrade(offer) { - try{ - if(this.listedCoins.get(offer.foreignBlockchain).name!=''){//check if the foreignBlockchain value is part of supported blockchains + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { if (offer.qortalCreator === this.selectedAddress.address) { - // Check and Update Wallet Balance if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { this.updateWalletBalance() } - // Add to my historic trades this._myHistoricTradesGrid.items.unshift(offer) this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null } } - }catch(e){ + } catch(e) { console.log("Error processing refunded trade offer from "+offer.foreignBlockchain) } } processCancelledTrade(offer) { - try{ - if(this.listedCoins.get(offer.foreignBlockchain).name!=''){//check if the foreignBlockchain value is part of supported blockchains + try { + if (this.listedCoins.get(offer.foreignBlockchain).name!='') { if (offer.qortalCreator === this.selectedAddress.address) { - // Check and Update Wallet Balance if (this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1) { this.updateWalletBalance() } - // Add to my historic trades this._myHistoricTradesGrid.items.unshift(offer) this.listedCoins.get(offer.foreignBlockchain).tradeOffersSocketCounter > 1 ? this._myHistoricTradesGrid.clearCache() : null } @@ -1504,7 +1791,7 @@ class TradePortal extends LitElement { } }) } - }catch(e){ + } catch(e) { console.log("Error processing cancelled trade offer from "+offer.foreignBlockchain) } } @@ -1580,7 +1867,6 @@ class TradePortal extends LitElement { */ const BitcoinACCTv1 = (states) => { - // Reverse the states states.reverse() states.forEach((state) => { if (state.creatorAddress === this.selectedAddress.address) { @@ -1623,7 +1909,6 @@ class TradePortal extends LitElement { */ const LitecoinACCTv1 = (states) => { - // Reverse the states states.reverse() states.forEach((state) => { if (state.creatorAddress === this.selectedAddress.address) { @@ -1666,7 +1951,6 @@ class TradePortal extends LitElement { */ const DogecoinACCTv1 = (states) => { - // Reverse the states states.reverse() states.forEach((state) => { if (state.creatorAddress === this.selectedAddress.address) { @@ -1709,7 +1993,6 @@ class TradePortal extends LitElement { */ const DigibyteACCTv1 = (states) => { - // Reverse the states states.reverse() states.forEach((state) => { if (state.creatorAddress === this.selectedAddress.address) { @@ -1752,7 +2035,6 @@ class TradePortal extends LitElement { */ const RavencoinACCTv1 = (states) => { - // Reverse the states states.reverse() states.forEach((state) => { if (state.creatorAddress === this.selectedAddress.address) { @@ -1795,7 +2077,6 @@ class TradePortal extends LitElement { */ const PirateChainACCTv1 = (states) => { - // Reverse the states states.reverse() states.forEach((state) => { if (state.creatorAddress === this.selectedAddress.address) { @@ -1844,15 +2125,12 @@ class TradePortal extends LitElement { default: break } - - // Fill Historic Trades and Filter Stuck Trades if (this.listedCoins.get(this.selectedCoin).tradeOffersSocketCounter === 1) { setTimeout(() => this.filterStuckTrades(tradeStates), 250) } } changeTradeBotState(state, tradeState) { - // Set Loading state this.isLoadingMyOpenOrders = true const stateItem = { ...state, @@ -1877,7 +2155,6 @@ class TradePortal extends LitElement { item ? updateStateItem() : addStateItem() } - // ONLY USE FOR BOB_DONE, BOB_REFUNDED, ALICE_DONE, ALICE_REFUNDED handleCompletedState(state) { this._myOrdersGrid.items.forEach((item, index) => { if (item.atAddress === state.atAddress) { @@ -1947,12 +2224,10 @@ class TradePortal extends LitElement { let socketTimeout let socketLink = `ws://NODEURL/websockets/crosschain/tradeoffers?foreignBlockchain=FOREIGN_BLOCKCHAIN&includeHistoric=true` const socket = new WebSocket(socketLink) - // Open Connection socket.onopen = () => { - setTimeout(pingSocket, 250) + setTimeout(pingSocket, 50) tradeOffersSocketCounter += 1 } - // Message Event socket.onmessage = (e) => { e.relatedCoin = _relatedCoin self.postMessage({ @@ -1964,12 +2239,10 @@ class TradePortal extends LitElement { tradeOffersSocketCounter += 1 restarted = false } - // Closed Event socket.onclose = () => { clearTimeout(socketTimeout) restartTradeOffersWebSocket() } - // Error Event socket.onerror = (e) => { clearTimeout(socketTimeout) } @@ -1983,11 +2256,9 @@ class TradePortal extends LitElement { let socketTimeout let socketLink = `ws://NODEURL/websockets/crosschain/tradebot?foreignBlockchain=FOREIGN_BLOCKCHAIN` const socket = new WebSocket(socketLink) - // Open Connection socket.onopen = () => { - setTimeout(pingSocket, 250) + setTimeout(pingSocket, 50) } - // Message Event socket.onmessage = (e) => { e.relatedCoin = _relatedCoin self.postMessage({ @@ -1997,12 +2268,10 @@ class TradePortal extends LitElement { }) restarted = false } - // Closed Event socket.onclose = () => { clearTimeout(socketTimeout) restartTradeBotWebSocket() } - // Error Event socket.onerror = (e) => { clearTimeout(socketTimeout) } @@ -2016,22 +2285,18 @@ class TradePortal extends LitElement { let socketTimeout let socketLink = `ws://NODEURL/websockets/crosschain/tradepresence` const socket = new WebSocket(socketLink) - // Open Connection socket.onopen = () => { - setTimeout(pingSocket, 250) + setTimeout(pingSocket, 50) } - // Message Event socket.onmessage = (e) => { tradePresenceTxns = JSON.parse(e.data) processOffersWithPresence() restarted = false } - // Closed Event socket.onclose = () => { clearTimeout(socketTimeout) restartTradePresenceWebSocket() } - // Error Event socket.onerror = (e) => { clearTimeout(socketTimeout) } @@ -2042,24 +2307,19 @@ class TradePortal extends LitElement { } const restartTradePresenceWebSocket = () => { - setTimeout(() => initTradePresenceWebSocket(true), 1000) + setTimeout(() => initTradePresenceWebSocket(true), 50) } const restartTradeOffersWebSocket = () => { - setTimeout(() => initTradeOffersWebSocket(true), 1000) + setTimeout(() => initTradeOffersWebSocket(true), 50) } const restartTradeBotWebSocket = () => { - setTimeout(() => initTradeBotWebSocket(true), 1000) + setTimeout(() => initTradeBotWebSocket(true), 50) } - // Start TradeOffersWebSocket initTradeOffersWebSocket() - - // Start TradePresenceWebSocket initTradePresenceWebSocket() - - // Start TradeBotWebSocket initTradeBotWebSocket() } @@ -2085,7 +2345,7 @@ class TradePortal extends LitElement { case 'DIGIBYTE': _receivingAddress = this.selectedAddress.dgbWallet.address break - case 'RAVENCOIN': + case 'RAVENCOIN': _receivingAddress = this.selectedAddress.rvnWallet.address break case 'PIRATECHAIN': @@ -2198,8 +2458,6 @@ class TradePortal extends LitElement { parentEpml.request('showSnackBar', `${snack7string}: ${response.message}`) } } - - // Call makeRequest const res = await makeRequest() manageResponse(res) } @@ -2235,8 +2493,6 @@ class TradePortal extends LitElement { parentEpml.request('showSnackBar', `${snack10string}: ${response.message}`) } } - - // Call makeRequest const res = await makeRequest() manageResponse(res) } @@ -2290,8 +2546,6 @@ class TradePortal extends LitElement { parentEpml.request('showSnackBar', `${snack13string}: ${response.message}`) } } - - // Call makeRequest const res = await makeRequest() manageResponse(res) } @@ -2304,7 +2558,6 @@ class TradePortal extends LitElement { } } - // Helper Functions (Re-Used in Most part of the UI ) _checkSellAmount(e) { const targetAmount = e.target.value const target = e.target @@ -2543,6 +2796,7 @@ class TradePortal extends LitElement { workers.get(this.selectedCoin).tradesConnectedWorker.addEventListener('message', function (event) { handleMessage(event.data) }, { passive: true }) workers.get(this.selectedCoin).tradesConnectedWorker.postMessage({ type: "set_coin", content: this.selectedCoin }) + } handleStuckTrades() { @@ -2580,11 +2834,7 @@ class TradePortal extends LitElement { const stuckOffers = filterStuckOffers(myOpenTradeOrders) self.postMessage({ type: 'STUCK_OFFERS', data: stuckOffers }) } - - // Get Offers setTimeout(() => { getOffers() }, 1000) - - // Get Historic Trades setTimeout(() => { getCompletedTrades() }, 1000) } @@ -2594,7 +2844,6 @@ class TradePortal extends LitElement { return } - //show the loading on historic trades this.shadowRoot.getElementById('loadingHistoricTrades').style.display = "block"; let isHandleTradesDone = false let isHandleStuckOffersDone = false diff --git a/scripts/notarize.js b/scripts/notarize.js index 957a555a..db2614a1 100644 --- a/scripts/notarize.js +++ b/scripts/notarize.js @@ -1,5 +1,5 @@ require('dotenv').config(); -const { notarize } = require('electron-notarize'); +const { notarize } = require('@electron/notarize'); exports.default = async function notarizing(context) { const { electronPlatformName, appOutDir } = context;