From 1849c5b1feaa38dbafe88038b273af24935cb3f8 Mon Sep 17 00:00:00 2001 From: 1aerostorm Date: Wed, 31 Jul 2024 07:38:46 +0300 Subject: [PATCH] OAuth - Web clients, app download modal --- config/oauth.json | 9 ++++ package.json | 1 + public/images/android48x48.png | Bin 0 -> 516 bytes public/images/linux.png | Bin 0 -> 1462 bytes public/images/windows.png | Bin 0 -> 1744 bytes src/App.scss | 1 + src/elements/QrCode.jsx | 18 +++++++ src/elements/WebClients.jsx | 81 ++++++++++++++++++++++++++++++++ src/elements/WebClients.scss | 23 +++++++++ src/locales/en.json | 11 +++++ src/locales/ru-RU.json | 11 +++++ src/modules/app/AppDownload.jsx | 35 ++++++++++++++ src/pages/index.jsx | 5 +- src/server/oauth.js | 1 + yarn.lock | 5 ++ 15 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 public/images/android48x48.png create mode 100644 public/images/linux.png create mode 100644 public/images/windows.png create mode 100644 src/elements/QrCode.jsx create mode 100644 src/elements/WebClients.jsx create mode 100644 src/elements/WebClients.scss create mode 100644 src/modules/app/AppDownload.jsx diff --git a/config/oauth.json b/config/oauth.json index 26c474b..25d9393 100644 --- a/config/oauth.json +++ b/config/oauth.json @@ -10,6 +10,15 @@ "posting": "5K1aJ8JayUA7c2Ptg9Y2DetKxSvXGXa5GCcvYeHtn1Xh3v4egPS", "active": "5JFZC7AtEe1wF2ce6vPAUxDeevzYkPgmtR14z9ZVgvCCtrFAaLw" }, + "web_clients": { + "Blogs": { "img": "https://i.imgur.com/36zv8We.png", + "ru": "Блоги", "url": "https://golos.id" }, + "Wallet": { "img": "https://i.imgur.com/CYfDqIy.png", + "ru": "Кошелек", "url": "https://wallet.golos.id" }, + "Messenger": { "img": "https://devimages.golos.today/DQmbzuzhgGnDRbKG8KyqL5HWSu5T2w1ZDP32ygM556TqLSn/Messenger.png", + "ru": "Мессенджер", "url": "https://chat.golos.app" }, + "_desktop": { "host": "https://files.golos.app" } + }, "allowed_clients": { "localfile": { "origins": ["null"], diff --git a/package.json b/package.json index a1bc470..883353d 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "passport-telegram-official": "^2.0.1", "passport-vk": "^1.0.0", "passport-yandex": "^0.0.5", + "qr-image": "^3.2.0", "querystring": "^0.2.1", "react": "^17.0.2", "react-copy-to-clipboard": "^5.1.0", diff --git a/public/images/android48x48.png b/public/images/android48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..7bd064cdcd7982b8e43acfb7f0b84075d24c74d7 GIT binary patch literal 516 zcmV+f0{i`mP)?K@i6m1VKRreFQD6twfMGb0S8>E-xVX02ZR& ztlUdzVP|6#LClVbqJoXSf`y=9Ds7dZphVW3i61#PANLj99}FD3bGP%q{qNlfVX;^& znp{5DB^t!b=enE8j~rsSB*xp7lc|1Fu6Hy&pmX&-A}vUT7m~3o88%G0#JE$*#WRr> zT!o{O;guNPh+&f$&q-lXGM04c>&h7H`o)*lRAxk^1+S19*0%7AGe-fg=n#fP8juY0 zQo&Qh#rr{wkFwy*C0abOGf50D)xz{ysGf&9@pE3m^E%^1BE@@Zu2(YLd@XQ!f4pyX z&lwC7P;Z_Xub1*;J+TWQhQ(jDP`D)(YzNvVa2^&C5P+x6`u>kNj$MFM_z;!=Vz`f8 zfF&*@!|cbORAJFon5~V6=Krz|(|RrAw*;SGi@T@=P{4XE6a1#4+5xOPZUsoY186PO z67~?Y{R;ha3sI;4iCu+R{U9B5;LJoVzztY0bfOXv8yzozzInCXfcCxKEH$sTUpz(W z^+qvSB5})@umY0C?m*I*T7b}7gs?HS0I{_Q7K_Cq;^ZB1eHx2i6#|j~0000!?lTSYRBX?Zownec zga4R#qXJldl5=<|rG#>?OeLBNnco_4P;e*)@L?@}^Jha1#M?x3LX0pX0%_aQF)nhN z3~DfqMbV$o+1cZZR@4f0aR(CnO>%c`cIHrC`_o z421#yE8=Zp^!&nYyY|B0-ydn4x5}WaQhr2GP!M!F9kQ%>$~5|vc$;XBpFMw39JVLV z#DzWm@gP%!!ma(VW94YXEc^tq%U3AMDW5>Ru?l@R|25dH-B53#0FlC@?1s<3c5AR@ zNk655BgESjsOQ5=wrNn?2rb2}eQF$xSK-ViHOdb#$`q>C>h-S>ZxYQKhAoK+e^j{f zdE_Ulke8@J!3H(#RtmP`JgTpX2pcort~ixrh&PGmgl{81G(Wt3H5+wD17XkW1>3O@ z+`Y6Fl{L3fS#t;RE0dI!sH*>)#utb;NIPfeF3nqpWbOwJHh7$@U1LmmDU4n0lmC5T7KK5;? zD%CwPF<3Bp{NaV+43*Kut2&p(xcvMUuBWH4gpyl4ofh}yn31e8kZt032YJNLW&@KbtC#T5Jn|JJ%SEI44 zCVI#KpQ;TuoLZK!3b(2o4ymV&U5)MaPkZ)3Z!|Tn!f8+N`_ZgW!3`UkEtSOfz)O*7 z$(2*+WKps5aI<&#Ynayu8Tv>!Kms~f21Eu zbEad=^w}6S)r^Sg=KJ$z4Y-uGx=(t?fP-{FZnvQj)mNI}rMPpj2aLn@SP=USuHUM1 z$-upfDe|>;D}_;0XJX`gv#@Z%Sex{{Vcf1E?u8ercvz`wCAc!IYkSMfKlyz#O3TWf z8n}NU@yWHh8yOr*(cy7Ucg2`$_dYK}qzc-CZ;E*zX=M|`$GwB{it7#yJSbW2a4cA< zV&s#T+-JZ_B{(y1V}DO@p+n?yc&y-*;}rTM#<2lWHgOgrHQG%E)X`3FC{jp|?~665 zX%69~L#t@dTXt8*gQ&jl9s{B}$CVpOo6tOMAQr?f#^IyK<+fRZK7a8t(zd5#{FE7T zn%&6gUEu$HaN6iH)N>o-|tFmv#BZvsq+X{Y8g(D z?(Ug=5I>0OREU(#6$4cn-S0`SOlXd;mV#mV%M2@Ake(Z8j=v}kt!~g&Q literal 0 HcmV?d00001 diff --git a/public/images/windows.png b/public/images/windows.png new file mode 100644 index 0000000000000000000000000000000000000000..193679ffb02193086a0446fd26f88b3af3aad788 GIT binary patch literal 1744 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sEa{HEjtmSN`?>!lvI6;x#X;^) z4C~IxyaaL-l0AZa85pY67#JE_7#My5g&JNkFq8sKd6mGxU^Rn*LA+qju0R{0z8?WT zA+A6LJe%I|Y<|tX=`Gi$x11Z^a;$&Lz3C12#Ul4sLP?oBU* zR=?%i^^kY#bDmA_xHrAy+4xp|9#B0^(Hq?6w z+bw55^XP+*-|ji=`jXlE-O<^_|KRS8%Vtf}5Sq~5P+6RxZN<#W#LB|VxPQy~m5b&~ zR~2lY;>4(sH~08MCcdOMc8p?7>4!cr@|eZ!FE^2L*+K^4?mG0eMWiI&k$O~+(Q#;HBlqgIA1pCaR|+#cmh5a> zBDm_{^oF)4BH;!ezTW3gY&n&5u8jXAOVgY?d;T;XS`?uXpx2hIA=u6^^^#GZ92;Al zT^-||NbyCEOZP0=JdwP(+!9rIzym}-7iFTkx($nBHb zlT4-0hgY;6T&UQW!uz3V%~~s+CJx>Yi-LBFQfwpQIo zCE||q%UMqzSQHv<_V(hr#>E;w^{C;|JM~%5OzyvoS|P>rA+$@!IF0S>EZgJ9&6yIz zxesPduUyxh{AkaepIsAHl`s~H?p`sY%jM&tYgw~hH@a+NIZ&n>w`lj28_Sqm85m+^ ze%IKtAE*IFqiTt3L`h0wNvc(HQ7VvPFfuT-&^0jAH82h_G_o==w=yu+HZZa>FmRr! z@ef5qZhlH;S|x4`Mn@(%0X4XRY$(o8D=AMbN@eiOO-xVqO-#>B&Q>tfGuN}wwNwCF z3AE2l*U(HM5QsBMN(!v>^~=l4^)f-Kff%S-FTW`L*?yC4KoccECWd5`<|bKLx#TC8 z=BDPASXl)Cl@>D?F8{wzK3{erx7`%PhfXy*8a097#yDn z7|ERA%&Jrd3q4~aZzm_7fO1Qqv>lRkXpkpET4qivFbMSYOAB)H6H|)yP0jT4jUto0 tLtXR@O${vc%QN#*^2>|k4Gm08O)d0*Zh2GiYc|j%22WQ%mvv4FO#mY<+6Vvu literal 0 HcmV?d00001 diff --git a/src/App.scss b/src/App.scss index 410e3af..33e4c2a 100644 --- a/src/App.scss +++ b/src/App.scss @@ -10,6 +10,7 @@ @import "./elements/GeneratedPasswordInput"; @import "./elements/Expandable"; @import "./elements/LoadingIndicator"; +@import "./elements/WebClients"; @import "./elements/nft/NFTSmallIcon"; @import "./elements/nft/NFTTokens"; @import "./elements/PagedDropdownMenu"; diff --git a/src/elements/QrCode.jsx b/src/elements/QrCode.jsx new file mode 100644 index 0000000..699e869 --- /dev/null +++ b/src/elements/QrCode.jsx @@ -0,0 +1,18 @@ +// modified https://github.com/jprichardson/react-qr/blob/master/index.js +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import qrImage from 'qr-image' + +export default class ReactQR extends Component { + static propTypes = { + text: PropTypes.string.isRequired + } + + render() { + const pngBuffer = qrImage.imageSync(this.props.text, { type: 'png', size: this.props.size || 2, margin: 1 }) + const dataURI = 'data:image/png;base64,' + pngBuffer.toString('base64') + return ( + + ) + } +} diff --git a/src/elements/WebClients.jsx b/src/elements/WebClients.jsx new file mode 100644 index 0000000..1bb5449 --- /dev/null +++ b/src/elements/WebClients.jsx @@ -0,0 +1,81 @@ +import React from 'react' +import tt from 'counterpart' +import CloseButton from 'react-foundation-components/lib/global/close-button' +import Reveal from 'react-foundation-components/lib/global/reveal' + +import AppDownload from '@/modules/app/AppDownload' + +class WebClients extends React.Component { + static propTypes = { + } + + state = { + } + + showAppDownload = (e) => { + e.preventDefault() + this.setState({ + show_app_download_modal: true + }) + } + + hideAppDownload = () => { + this.setState({ + show_app_download_modal: false + }) + } + + render() { + const { web_clients } = this.props + + const isEng = tt.getLocale() !== 'ru' + + let appList = null + const webs = Object.entries(web_clients || {}) + if (webs.length) { + appList = [] + for (const [en, data] of webs) { + if (en === '_desktop') continue + const { img, ru, url } = data + appList.push( +
+
{isEng ? en : ru}
+
) + } + + let desktop + const { _desktop } = web_clients + let desktopHost + if (_desktop) { + desktopHost = _desktop.host + desktop =
+ {tt('web_clients_jx.or_desktop')} + {tt('web_clients_jx.or_desktop_link')} + {tt('web_clients_jx.or_desktop2')} +
+ } + + const modalStyle = { + borderRadius: '8px', + boxShadow: '0 0 19px 3px rgba(0,0,0, 0.2)', + overflow: 'hidden', + } + + const { show_app_download_modal } = this.state + appList =
+

{tt('web_clients_jx.title')}

+ {appList} + {desktop} +
+ + + + +
+ } + return appList + } +} + +export default WebClients diff --git a/src/elements/WebClients.scss b/src/elements/WebClients.scss new file mode 100644 index 0000000..b2e3a60 --- /dev/null +++ b/src/elements/WebClients.scss @@ -0,0 +1,23 @@ +.WebClients { + .web-client { + display: inline-block; + vertical-align: top; + width: 150px; + height: 80px; + position: relative; + + img { + max-width: 100%; + max-height: 100%; + } + .web-label { + position: absolute; + left: 0.25rem; + bottom: 0.25rem; + } + } + + .desktop { + margin-top: 0.5rem; + } +} diff --git a/src/locales/en.json b/src/locales/en.json index 7e3e11a..a1a1a52 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1,4 +1,8 @@ { + "app_download": { + "title": "Download application", + "download_for": "Download for" + }, "chainvalidation_js": { "account_name_should": "Account name should ", "not_be_empty": "not be empty.", @@ -197,6 +201,7 @@ "unfreeze": "Unfreeze account", "apps_title": "Apps", "apps_empty": "You are not authorized in any app.", + "web_clients": "Web Clients", "link": "Link: ", "edit": "Edit", "remove_authority": "Forbid OAuth access to my account", @@ -212,6 +217,12 @@ "memo_is_public": "This memo is public.", "submit": "Transfer" }, + "web_clients_jx": { + "title": "Web-clients", + "or_desktop": "Or ", + "or_desktop_link": "download desktop app", + "or_desktop2": ", which combines all these applications." + }, "oauth_transfer_nft": { "no_tokens": "You have not yet any NFT-tokens.", "token_not_exist": "No such token in your wallet. You can transfer any of these tokens:", diff --git a/src/locales/ru-RU.json b/src/locales/ru-RU.json index 8de2646..884380f 100644 --- a/src/locales/ru-RU.json +++ b/src/locales/ru-RU.json @@ -1,4 +1,8 @@ { + "app_download": { + "title": "Скачать приложение", + "download_for": "Скачать для" + }, "chainvalidation_js": { "account_name_should": "Имя аккаунта должно ", "not_be_empty": "не может быть пустым.", @@ -197,11 +201,18 @@ "unfreeze": "Активировать аккаунт", "apps_title": "Приложения", "apps_empty": "Вы пока не авторизованы ни в одном приложении.", + "web_clients": "Веб-клиенты", "link": "Ссылка: ", "edit": "Настроить", "remove_authority": "Отменить доступ OAuth к моему аккаунту", "remove_authority_confirm": "Вы действительно хотите отменить доступ GolosSigner к вашему аккаунту?" }, + "web_clients_jx": { + "title": "Веб-клиенты", + "or_desktop": "Или ", + "or_desktop_link": "скачайте десктоп-клиент", + "or_desktop2": ", где объединены все эти приложения." + }, "oauth_transfer": { "from": "От", "to": "Отправить аккаунту", diff --git a/src/modules/app/AppDownload.jsx b/src/modules/app/AppDownload.jsx new file mode 100644 index 0000000..579d931 --- /dev/null +++ b/src/modules/app/AppDownload.jsx @@ -0,0 +1,35 @@ +import React from 'react' +import tt from 'counterpart' + +import QRCode from '@/elements/QrCode' + +class AppDownload extends React.Component { + componentDidMount() { + } + + render() { + const updaterHost = this.props.host + if (!updaterHost) return null + const winUrl = new URL('/api/exe/desktop/windows/latest', updaterHost).toString() + const linuxUrl = new URL('/api/exe/desktop/linux/latest', updaterHost).toString() + const androidUrl = new URL('/api/exe/messenger/android/latest', updaterHost).toString() + return + } +} + +export default AppDownload diff --git a/src/pages/index.jsx b/src/pages/index.jsx index 7615d55..9d666bc 100644 --- a/src/pages/index.jsx +++ b/src/pages/index.jsx @@ -5,6 +5,7 @@ import tt from 'counterpart'; import { api } from 'golos-lib-js' import RemoveAuthority from '@/elements/RemoveAuthority'; +import WebClients from '@/elements/WebClients' import Header from '@/modules/Header'; import { callApi, } from '@/utils/OAuthClient'; import { withRouterHelpers, } from '@/utils/routing'; @@ -51,7 +52,7 @@ class Index extends React.Component { render() { const { session, oauthCfg, } = this.props; const { account, clients, } = session; - const { service_account, sign_endpoint, } = oauthCfg; + const { service_account, sign_endpoint, web_clients, } = oauthCfg; let actions = []; for (let action of [ 'transfer', 'donate', 'delegate_vs', 'transfer_nft']) { @@ -102,6 +103,7 @@ class Index extends React.Component { } else { clientList =
{tt('oauth_main_jsx.apps_empty')}
} + return (
@@ -122,6 +124,7 @@ class Index extends React.Component {

{tt('oauth_main_jsx.apps_title')}

{clientList}
+