From 6dd90aa1182a7a5e0f0189d1467ba474b68c28c2 Mon Sep 17 00:00:00 2001 From: Reckless_Satoshi Date: Thu, 27 Oct 2022 06:34:58 -0700 Subject: [PATCH] Add settings forms --- frontend/src/App.tsx | 4 + frontend/src/basic/Main.tsx | 10 +- frontend/src/basic/MakerPage/index.tsx | 7 +- frontend/src/basic/NavBar/index.tsx | 1235 +++++++++-------- frontend/src/basic/SettingsPage/index.tsx | 23 + frontend/src/components/BookTable/index.tsx | 2 +- .../SettingsForm/SelectLanguage.tsx | 71 + .../src/components/SettingsForm/index.tsx | 28 + frontend/src/models/Coordinator.model.ts | 20 + frontend/src/models/Info.model.ts | 2 + frontend/src/models/Language.model.ts | 19 + frontend/src/models/Settings.model.ts | 28 +- frontend/src/models/index.ts | 2 + frontend/urls.py | 4 + 14 files changed, 819 insertions(+), 636 deletions(-) create mode 100644 frontend/src/components/SettingsForm/SelectLanguage.tsx create mode 100644 frontend/src/components/SettingsForm/index.tsx create mode 100644 frontend/src/models/Coordinator.model.ts create mode 100644 frontend/src/models/Language.model.ts diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a6002e41..5036a642 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -44,6 +44,10 @@ const App = (): JSX.Element => { updateTheme(); }, [settings]); + useEffect(() => { + i18n.changeLanguage(settings.language); + }, []); + return ( diff --git a/frontend/src/basic/Main.tsx b/frontend/src/basic/Main.tsx index 74725df4..87a90c1e 100644 --- a/frontend/src/basic/Main.tsx +++ b/frontend/src/basic/Main.tsx @@ -29,6 +29,7 @@ import { import DarkModeIcon from '@mui/icons-material/DarkMode'; import LightModeIcon from '@mui/icons-material/LightMode'; import SchoolIcon from '@mui/icons-material/School'; +import SettingsPage from './SettingsPage'; const getWindowSize = function (fontSize: number) { // returns window size in EM units @@ -39,7 +40,6 @@ const getWindowSize = function (fontSize: number) { }; interface MainProps { - updateTheme: () => void; settings: Settings; setSettings: (state: Settings) => void; } @@ -202,6 +202,10 @@ const Main = ({ settings, setSettings }: MainProps): JSX.Element => { path='/order/:orderId' render={(props: any) => } /> + } + />
{ }} > history.push(location)} + width={windowSize.width} robot={robot} setRobot={setRobot} info={info} diff --git a/frontend/src/basic/MakerPage/index.tsx b/frontend/src/basic/MakerPage/index.tsx index 070f3a15..faecff2a 100644 --- a/frontend/src/basic/MakerPage/index.tsx +++ b/frontend/src/basic/MakerPage/index.tsx @@ -74,7 +74,12 @@ const MakerPage = ({ { - this.setState({ openStatsForNerds: true }); - }; - - handleClickCloseStatsForNerds = () => { - this.setState({ openStatsForNerds: false }); - }; - - handleClickOpenCommunity = () => { - this.setState({ openCommunity: true }); - }; - - handleClickCloseCommunity = () => { - this.setState({ openCommunity: false }); - }; - - handleClickOpenProfile = () => { - this.setState({ openProfile: true, profileShown: true }); - }; - - handleClickCloseProfile = () => { - this.setState({ openProfile: false }); - }; - - handleClickOpenExchangeSummary = () => { - this.setState({ openExchangeSummary: true }); - }; - - handleClickCloseExchangeSummary = () => { - this.setState({ openExchangeSummary: false }); - }; - - handleSubmitInvoiceClicked = (e, rewardInvoice) => { - this.setState({ badInvoice: false, showRewardsSpinner: true }); - - apiClient - .post('/api/reward/', { - invoice: rewardInvoice, - }) - .then((data) => { - this.setState({ badInvoice: data.bad_invoice, showRewardsSpinner: false }); - this.props.setInfo({ - ...this.props.info, - badInvoice: data.bad_invoice, - openClaimRewards: !data.successful_withdrawal, - withdrawn: !!data.successful_withdrawal, - showRewardsSpinner: false, - }); - this.props.setRobot({ - ...this.props.robot, - earnedRewards: data.successful_withdrawal ? 0 : this.props.robot.earnedRewards, - }); - }); - e.preventDefault(); - }; - - handleSetStealthInvoice = (wantsStealth) => { - apiClient - .put('/api/stealth/', { wantsStealth }) - .then((data) => - this.props.setRobot({ ...this.props.robot, stealthInvoices: data?.wantsStealth }), - ); - }; - - showProfileButton = () => { - return ( - this.props.robot.avatarLoaded && - (this.props.robot.token - ? systemClient.getCookie('robot_token') === this.props.robot.token - : true) && - systemClient.getCookie('sessionid') - ); - }; - - bottomBarDesktop = () => { - const { t } = this.props; - const hasRewards = this.props.robot.earnedRewards > 0; - const hasOrder = !!( - (this.props.robot.activeOrderId > 0) & - !this.state.profileShown & - this.props.robot.avatarLoaded - ); - const fontSize = this.props.theme.typography.fontSize; - const fontSizeFactor = fontSize / 14; // default fontSize is 14 - const typographyProps = { - primaryTypographyProps: { fontSize }, - secondaryTypographyProps: { fontSize: (fontSize * 12) / 14 }, - }; - return ( - - - -
- - - - 0) & !this.state.profileShown - ? 'primary' - : undefined - } - nickname={this.props.robot.nickname} - onLoad={() => - this.props.setRobot({ ...this.props.robot, avatarLoaded: true }) - } - /> - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {this.LangSelect()} - - - - - - - - - - - - - - - - -
-
- ); - }; - - handleChangeLang = (e) => { - const { i18n } = this.props; - i18n.changeLanguage(e.target.value); - }; - - LangSelect = () => { - const { i18n } = this.props; - const lang = i18n.resolvedLanguage == null ? 'en' : i18n.resolvedLanguage.substring(0, 2); - const flagProps = { - width: 20, - height: 20, - }; - - return ( - - ); - }; - - bottomBarPhone = () => { - const { t } = this.props; - const hasRewards = this.props.robot.earnedRewards > 0; - const hasOrder = !!( - (this.props.info.active_order_id > 0) & - !this.state.profileShown & - this.props.robot.avatarLoaded - ); - return ( - - - -
- - - 0) & !this.state.profileShown - ? 'primary' - : undefined - } - nickname={this.props.robot.nickname} - onLoad={() => this.props.setRobot({ ...this.props.robot, avatarLoaded: true })} - /> - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {this.LangSelect()} - - - - - - - - - - - this.props.fetchInfo()} - onClick={this.handleClickOpenStatsForNerds} - > - - - - - -
-
- ); - }; - - render() { - return ( -
- - - - this.props.setInfo({ ...this.props.info, openUpdateClient: false }) - } - /> - - - - this.props.setRobot({ ...robot, ...newParam })} - stealthInvoices={this.props.robot.stealthInvoices} - handleSetStealthInvoice={this.handleSetStealthInvoice} - /> - - - - {this.bottomBarDesktop()} - - {this.bottomBarPhone()} -
- ); - } +interface NavBarProps { + width: number; } +type Page = 'robot' | 'order' | 'make' | 'book' | 'settings'; -export default withTranslation()(NavBar); +const NavBar = ({ width }: NavBarProps): JSX.Element => { + const theme = useTheme(); + const history = useHistory(); + const { pathname } = history.location; + + const [page, setPage] = useState(history.location.pathname.split('/')); + + const changePage = function (newPage: Page) { + setPage(newPage); + history.push(`/${newPage}`); + }; + + return ( + + {page} + + + + + + + ); +}; + +export default NavBar; + +// constructor(props) { +// super(props); +// this.state = { +// profileShown: false, +// openStatsForNerds: false, +// openCommunity: false, +// openExchangeSummary: false, +// openClaimRewards: false, +// openProfile: false, +// showRewards: false, +// rewardInvoice: null, +// badInvoice: false, +// showRewardsSpinner: false, +// withdrawn: false, +// }; +// } + +// handleClickOpenStatsForNerds = () => { +// this.setState({ openStatsForNerds: true }); +// }; + +// handleClickCloseStatsForNerds = () => { +// this.setState({ openStatsForNerds: false }); +// }; + +// handleClickOpenCommunity = () => { +// this.setState({ openCommunity: true }); +// }; + +// handleClickCloseCommunity = () => { +// this.setState({ openCommunity: false }); +// }; + +// handleClickOpenProfile = () => { +// this.setState({ openProfile: true, profileShown: true }); +// }; + +// handleClickCloseProfile = () => { +// this.setState({ openProfile: false }); +// }; + +// handleClickOpenExchangeSummary = () => { +// this.setState({ openExchangeSummary: true }); +// }; + +// handleClickCloseExchangeSummary = () => { +// this.setState({ openExchangeSummary: false }); +// }; + +// handleSubmitInvoiceClicked = (e, rewardInvoice) => { +// this.setState({ badInvoice: false, showRewardsSpinner: true }); + +// apiClient +// .post('/api/reward/', { +// invoice: rewardInvoice, +// }) +// .then((data) => { +// this.setState({ badInvoice: data.bad_invoice, showRewardsSpinner: false }); +// this.props.setInfo({ +// ...this.props.info, +// badInvoice: data.bad_invoice, +// openClaimRewards: !data.successful_withdrawal, +// withdrawn: !!data.successful_withdrawal, +// showRewardsSpinner: false, +// }); +// this.props.setRobot({ +// ...this.props.robot, +// earnedRewards: data.successful_withdrawal ? 0 : this.props.robot.earnedRewards, +// }); +// }); +// e.preventDefault(); +// }; + +// handleSetStealthInvoice = (wantsStealth) => { +// apiClient +// .put('/api/stealth/', { wantsStealth }) +// .then((data) => +// this.props.setRobot({ ...this.props.robot, stealthInvoices: data?.wantsStealth }), +// ); +// }; + +// showProfileButton = () => { +// return ( +// this.props.robot.avatarLoaded && +// (this.props.robot.token +// ? systemClient.getCookie('robot_token') === this.props.robot.token +// : true) && +// systemClient.getCookie('sessionid') +// ); +// }; + +// bottomBarDesktop = () => { +// const { t } = this.props; +// const hasRewards = this.props.robot.earnedRewards > 0; +// const hasOrder = !!( +// (this.props.robot.activeOrderId > 0) & +// !this.state.profileShown & +// this.props.robot.avatarLoaded +// ); +// const fontSize = this.props.theme.typography.fontSize; +// const fontSizeFactor = fontSize / 14; // default fontSize is 14 +// const typographyProps = { +// primaryTypographyProps: { fontSize }, +// secondaryTypographyProps: { fontSize: (fontSize * 12) / 14 }, +// }; +// return ( +// +// +// +//
+// +// +// +// 0) & !this.state.profileShown +// ? 'primary' +// : undefined +// } +// nickname={this.props.robot.nickname} +// onLoad={() => +// this.props.setRobot({ ...this.props.robot, avatarLoaded: true }) +// } +// /> +// +// +// +// +//
+//
+ +// +// +// +// +// +// +// +// +// +// + +// +// +// +// +// +// +// +// +// +// + +// +// +// +// +// +// +// +// +// +// + +// +// +// +// +// +// +// +// +// +// + +// +// +// +// +// +// +// +// +// +// + +// +// +// {this.LangSelect()} +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +// +//
+//
+// ); +// }; + +// handleChangeLang = (e) => { +// const { i18n } = this.props; +// i18n.changeLanguage(e.target.value); +// }; + +// LangSelect = () => { +// const { i18n } = this.props; +// const lang = i18n.resolvedLanguage == null ? 'en' : i18n.resolvedLanguage.substring(0, 2); +// const flagProps = { +// width: 20, +// height: 20, +// }; + +// return ( +// +// ); +// }; + +// bottomBarPhone = () => { +// const { t } = this.props; +// const hasRewards = this.props.robot.earnedRewards > 0; +// const hasOrder = !!( +// (this.props.info.active_order_id > 0) & +// !this.state.profileShown & +// this.props.robot.avatarLoaded +// ); +// return ( +// +// +// +//
+// +// +// 0) & !this.state.profileShown +// ? 'primary' +// : undefined +// } +// nickname={this.props.robot.nickname} +// onLoad={() => this.props.setRobot({ ...this.props.robot, avatarLoaded: true })} +// /> +// +// +//
+//
+ +// +// +// +// +// +// +// +// +// + +// +// +// +// +// +// +// +// +// + +// +// +// +// +// +// +// +// +// + +// +// +// +// +// +// +// +// +// + +// +// +// {this.LangSelect()} +// +// +// +// +// +// +// +// +// +// +// this.props.fetchInfo()} +// onClick={this.handleClickOpenStatsForNerds} +// > +// +// +// +// +// +//
+//
+// ); +// }; + +// render() { +// return ( +//
+// + +// +// this.props.setInfo({ ...this.props.info, openUpdateClient: false }) +// } +// /> + +// + +// this.props.setRobot({ ...robot, ...newParam })} +// stealthInvoices={this.props.robot.stealthInvoices} +// handleSetStealthInvoice={this.handleSetStealthInvoice} +// /> + +// + +// {this.bottomBarDesktop()} + +// {this.bottomBarPhone()} +//
+// ); +// } +// } + +// export default NavBar; diff --git a/frontend/src/basic/SettingsPage/index.tsx b/frontend/src/basic/SettingsPage/index.tsx index e69de29b..89eddc44 100644 --- a/frontend/src/basic/SettingsPage/index.tsx +++ b/frontend/src/basic/SettingsPage/index.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Paper, useTheme } from '@mui/material'; +import SettingsForm from '../../components/SettingsForm'; +import { Settings } from '../../models'; + +interface SettingsPageProps { + settings: Settings; + setSettings: (state: Settings) => void; +} + +const SettingsPage = ({ settings, setSettings }: SettingsPageProps): JSX.Element => { + const theme = useTheme(); + const { t } = useTranslation(); + + return ( + + + + ); +}; + +export default SettingsPage; diff --git a/frontend/src/components/BookTable/index.tsx b/frontend/src/components/BookTable/index.tsx index 4d755ea4..c0c74cc2 100644 --- a/frontend/src/components/BookTable/index.tsx +++ b/frontend/src/components/BookTable/index.tsx @@ -37,7 +37,7 @@ interface Props { maxHeight: number; fullWidth?: number; fullHeight?: number; - elevation: number; + elevation?: number; defaultFullscreen?: boolean; fillContainer?: boolean; showControls?: boolean; diff --git a/frontend/src/components/SettingsForm/SelectLanguage.tsx b/frontend/src/components/SettingsForm/SelectLanguage.tsx new file mode 100644 index 00000000..72b1a41a --- /dev/null +++ b/frontend/src/components/SettingsForm/SelectLanguage.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Select, MenuItem, useTheme, Grid, Typography } from '@mui/material'; +import Language from '../../models/Language.model'; + +import Flags from 'country-flag-icons/react/3x2'; +import { CataloniaFlag, BasqueCountryFlag } from '../Icons'; + +const menuLanuguages = [ + { name: 'English', i18nCode: 'en', flag: Flags['US'] }, + { name: 'Español', i18nCode: 'es', flag: Flags['ES'] }, + { name: 'Deutsch', i18nCode: 'de', flag: Flags['DE'] }, + { name: 'Polski', i18nCode: 'pl', flag: Flags['PL'] }, + { name: 'Français', i18nCode: 'fr', flag: Flags['FR'] }, + { name: 'Русский', i18nCode: 'ru', flag: Flags['RU'] }, + { name: 'Italiano', i18nCode: 'it', flag: Flags['IT'] }, + { name: 'Português', i18nCode: 'pt', flag: Flags['BR'] }, + { name: '简体', i18nCode: 'zh-si', flag: Flags['CN'] }, + { name: '繁體', i18nCode: 'zh-tr', flag: Flags['CN'] }, + { name: 'Svenska', i18nCode: 'sv', flag: Flags['SE'] }, + { name: 'Čeština', i18nCode: 'cs', flag: Flags['CZ'] }, + { name: 'ภาษาไทย', i18nCode: 'th', flag: Flags['TH'] }, + { name: 'Català', i18nCode: 'ca', flag: CataloniaFlag }, + { name: 'Euskara', i18nCode: 'eu', flag: BasqueCountryFlag }, +]; + +interface SelectLanguageProps { + language: Language; + setLanguage: (lang: Language) => void; +} +const SelectLanguage = ({ language, setLanguage }: SelectLanguageProps): JSX.Element => { + const theme = useTheme(); + const { t, i18n } = useTranslation(); + + const flagProps = { + width: 1.5 * theme.typography.fontSize, + height: 1.5 * theme.typography.fontSize, + }; + + const handleChangeLang = function (e: any) { + setLanguage(e.target.value); + i18n.changeLanguage(e.target.value); + }; + + console.log(language); + return ( + + ); +}; + +export default SelectLanguage; diff --git a/frontend/src/components/SettingsForm/index.tsx b/frontend/src/components/SettingsForm/index.tsx new file mode 100644 index 00000000..bed5fb33 --- /dev/null +++ b/frontend/src/components/SettingsForm/index.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Grid, useTheme } from '@mui/material'; +import { Settings } from '../../models'; +import SelectLanguage from './SelectLanguage'; + +interface SettingsFormProps { + settings: Settings; + setSettings: (state: Settings) => void; +} + +const SettingsForm = ({ settings, setSettings }: SettingsFormProps): JSX.Element => { + const theme = useTheme(); + const { t } = useTranslation(); + + return ( + + + setSettings({ ...settings, language })} + /> + + + ); +}; + +export default SettingsForm; diff --git a/frontend/src/models/Coordinator.model.ts b/frontend/src/models/Coordinator.model.ts new file mode 100644 index 00000000..550861dc --- /dev/null +++ b/frontend/src/models/Coordinator.model.ts @@ -0,0 +1,20 @@ +export interface Coordinator { + alias: string; + description: string | undefined; + coverLetter: string | undefined; + logo: string; + color: string; + contact: { + email: string | undefined; + telegram: string | undefined; + matrix: string | undefined; + twitter: string | undefined; + website: string | undefined; + }; + mainnetOnion: string | undefined; + testnetOnion: string | undefined; + mainnetNodesPubkeys: string[]; + testnetNodesPubkeys: string[]; +} + +export default Coordinator; diff --git a/frontend/src/models/Info.model.ts b/frontend/src/models/Info.model.ts index 0b5d145e..f6c289af 100644 --- a/frontend/src/models/Info.model.ts +++ b/frontend/src/models/Info.model.ts @@ -17,6 +17,7 @@ export interface Info { taker_fee: number; bond_size: number; current_swap_fee_rate: number; + network: 'mainnet' | 'testnet' | undefined; coordinatorVersion: string; clientVersion: string; openUpdateClient: boolean; @@ -41,6 +42,7 @@ export const defaultInfo: Info = { taker_fee: 0, bond_size: 0, current_swap_fee_rate: 0, + network: undefined, coordinatorVersion: 'v?.?.?', clientVersion: 'v?.?.?', openUpdateClient: false, diff --git a/frontend/src/models/Language.model.ts b/frontend/src/models/Language.model.ts new file mode 100644 index 00000000..92530000 --- /dev/null +++ b/frontend/src/models/Language.model.ts @@ -0,0 +1,19 @@ +export type Language = + | 'en' + | 'es' + | 'ru' + | 'de' + | 'pl' + | 'fr' + | 'ca' + | 'it' + | 'pt' + | 'eu' + | 'cs' + | 'th' + | 'pl' + | 'sv' + | 'zh-SI' + | 'zh-TR'; + +export default Language; diff --git a/frontend/src/models/Settings.model.ts b/frontend/src/models/Settings.model.ts index b9480f25..e82a6d38 100644 --- a/frontend/src/models/Settings.model.ts +++ b/frontend/src/models/Settings.model.ts @@ -1,24 +1,14 @@ +import i18n from '../i18n/Web'; +import type Coordinator from './Coordinator.model'; +import type Language from './Language.model'; + export interface Settings { mode: 'light' | 'dark'; fontSize: number; - language: - | 'en' - | 'es' - | 'ru' - | 'de' - | 'pl' - | 'fr' - | 'ca' - | 'it' - | 'pt' - | 'eu' - | 'cs' - | 'th' - | 'pl' - | 'sv' - | 'zh-SI' - | 'zh-TR'; + language: Language; freezeViewports: boolean; + network: 'mainnet' | 'testnet' | undefined; + coordinator: Coordinator | undefined; } export const baseSettings: Settings = { @@ -27,8 +17,10 @@ export const baseSettings: Settings = { ? 'dark' : 'light', fontSize: 14, - language: 'en', + language: i18n.resolvedLanguage == null ? 'en' : i18n.resolvedLanguage.substring(0, 2), freezeViewports: false, + network: undefined, + coordinator: undefined, }; export default Settings; diff --git a/frontend/src/models/index.ts b/frontend/src/models/index.ts index e8aefb1b..3548abe3 100644 --- a/frontend/src/models/index.ts +++ b/frontend/src/models/index.ts @@ -7,6 +7,8 @@ export type { Robot } from './Robot.model'; export type { Info } from './Info.model'; export type { Settings } from './Settings.model'; export type { Favorites } from './Favorites.model'; +export type { Coordinator } from './Coordinator.model'; +export type { Language } from './Coordinator.model'; export { defaultMaker } from './Maker.model'; export { defaultRobot } from './Robot.model'; diff --git a/frontend/urls.py b/frontend/urls.py index 63a99f10..28e848c3 100644 --- a/frontend/urls.py +++ b/frontend/urls.py @@ -3,9 +3,13 @@ from django.urls import path from .views import basic, pro urlpatterns = [ + path("", basic), path("make/", basic), + path("robot/", basic), + path("ref/", basic), path("book/", basic), path("order/", basic), + path("settings/", basic), path("", basic), path("ref/", basic), path("pro/", pro),