From 721cd83218b8b25d77a416b154bfb7683c40cee5 Mon Sep 17 00:00:00 2001 From: ameerul-deriv <103412909+ameerul-deriv@users.noreply.github.com> Date: Wed, 22 May 2024 14:02:05 +0800 Subject: [PATCH 1/2] fix: use the data object returned from useExchangeRates instead of subscribeRates (#74) --- .../AdvertsTableRow/AdvertsTableRow.tsx | 17 +++++++++++----- src/components/BuySellForm/BuySellForm.tsx | 17 +++++++++++----- src/components/FloatingRate/FloatingRate.tsx | 20 ++++++++++++------- .../my-ads/components/AdSummary/AdSummary.tsx | 17 +++++++++++----- .../MyAds/MyAdsTableRow/MyAdsTableRow.tsx | 19 +++++++++++++----- 5 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/components/AdvertsTableRow/AdvertsTableRow.tsx b/src/components/AdvertsTableRow/AdvertsTableRow.tsx index 2f1f9449..f7b8abee 100644 --- a/src/components/AdvertsTableRow/AdvertsTableRow.tsx +++ b/src/components/AdvertsTableRow/AdvertsTableRow.tsx @@ -1,7 +1,7 @@ import { Fragment, memo, useEffect, useRef } from 'react'; import clsx from 'clsx'; import { useHistory, useLocation } from 'react-router-dom'; -import { TAdvertsTableRowRenderer, TCurrency, TExchangeRate } from 'types'; +import { TAdvertsTableRowRenderer, TCurrency } from 'types'; import { Badge, BuySellForm, PaymentMethodLabel, StarRating, UserAvatar } from '@/components'; import { NicknameModal } from '@/components/Modals'; import { ADVERTISER_URL, BUY_SELL } from '@/constants'; @@ -18,7 +18,7 @@ const BASE_CURRENCY = 'USD'; const AdvertsTableRow = memo((props: TAdvertsTableRowRenderer) => { const { hideModal, isModalOpenFor, showModal } = useModalManager(); - const { subscribeRates } = useExchangeRates(); + const { data: exchangeRateData, subscribeRates } = useExchangeRates(); const { isDesktop, isMobile } = useDevice(); const history = useHistory(); const location = useLocation(); @@ -29,7 +29,7 @@ const AdvertsTableRow = memo((props: TAdvertsTableRowRenderer) => { const { data: poiPoaData } = usePoiPoaStatus(); const { isPoaVerified, isPoiVerified } = poiPoaData || {}; - const exchangeRateRef = useRef(null); + const exchangeRateRef = useRef(undefined); const { account_currency, @@ -48,7 +48,7 @@ const AdvertsTableRow = memo((props: TAdvertsTableRowRenderer) => { useEffect(() => { if (local_currency) { - exchangeRateRef.current = subscribeRates({ + subscribeRates({ base_currency: BASE_CURRENCY, target_currencies: [local_currency], }); @@ -56,12 +56,19 @@ const AdvertsTableRow = memo((props: TAdvertsTableRowRenderer) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [local_currency]); + useEffect(() => { + if (exchangeRateData?.exchange_rates?.rates) { + exchangeRateRef.current = exchangeRateData?.exchange_rates?.rates?.[local_currency]; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [exchangeRateData]); + const Container = isMobile ? 'div' : Fragment; const { completed_orders_count, id, is_online, name, rating_average, rating_count } = advertiser_details || {}; const { displayEffectiveRate } = generateEffectiveRate({ - exchangeRate: exchangeRateRef.current?.rates?.[local_currency], + exchangeRate: exchangeRateRef.current, localCurrency: local_currency as TCurrency, marketRate: Number(effective_rate), price: Number(price_display), diff --git a/src/components/BuySellForm/BuySellForm.tsx b/src/components/BuySellForm/BuySellForm.tsx index f4439c91..1ff50001 100644 --- a/src/components/BuySellForm/BuySellForm.tsx +++ b/src/components/BuySellForm/BuySellForm.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'; import { Control, FieldValues, useForm } from 'react-hook-form'; import { useHistory } from 'react-router-dom'; -import { TCurrency, TExchangeRate, THooks, TPaymentMethod } from 'types'; +import { TCurrency, THooks, TPaymentMethod } from 'types'; import { BUY_SELL, ORDERS_URL, RATE_TYPE } from '@/constants'; import { api } from '@/hooks'; import { useIsAdvertiser } from '@/hooks/custom-hooks'; @@ -47,7 +47,7 @@ const getAdvertiserMaxLimit = ( const BASE_CURRENCY = 'USD'; const BuySellForm = ({ advertId, isModalOpen, onRequestClose }: TBuySellFormProps) => { - const { subscribeRates } = useExchangeRates(); + const { data: exchangeRatesData, subscribeRates } = useExchangeRates(); const { data: advertInfo } = api.advert.useGet({ id: advertId }); const { data: orderCreatedInfo, isSuccess, mutate } = api.order.useCreate(); const { data: paymentMethods } = api.paymentMethods.useGet(); @@ -67,7 +67,7 @@ const BuySellForm = ({ advertId, isModalOpen, onRequestClose }: TBuySellFormProp const [calculatedRate, setCalculatedRate] = useState('0'); const [initialAmount, setInitialAmount] = useState('0'); - const exchangeRateRef = useRef(null); + const exchangeRateRef = useRef(undefined); const { account_currency, @@ -96,7 +96,7 @@ const BuySellForm = ({ advertId, isModalOpen, onRequestClose }: TBuySellFormProp useEffect(() => { if (local_currency) { - exchangeRateRef.current = subscribeRates({ + subscribeRates({ base_currency: BASE_CURRENCY, target_currencies: [local_currency], }); @@ -104,8 +104,15 @@ const BuySellForm = ({ advertId, isModalOpen, onRequestClose }: TBuySellFormProp // eslint-disable-next-line react-hooks/exhaustive-deps }, [local_currency]); + useEffect(() => { + if (exchangeRatesData?.exchange_rates?.rates) { + exchangeRateRef.current = exchangeRatesData?.exchange_rates.rates?.[local_currency]; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [exchangeRatesData]); + const { displayEffectiveRate, effectiveRate } = generateEffectiveRate({ - exchangeRate: exchangeRateRef.current?.rates?.[local_currency], + exchangeRate: exchangeRateRef.current, localCurrency: local_currency as TCurrency, marketRate: Number(effective_rate), price: Number(price_display), diff --git a/src/components/FloatingRate/FloatingRate.tsx b/src/components/FloatingRate/FloatingRate.tsx index 0ccb962f..8c26c45f 100644 --- a/src/components/FloatingRate/FloatingRate.tsx +++ b/src/components/FloatingRate/FloatingRate.tsx @@ -1,5 +1,5 @@ import { ChangeEvent, FocusEvent, useEffect, useRef } from 'react'; -import { TCurrency, TExchangeRate } from 'types'; +import { TCurrency } from 'types'; import { api } from '@/hooks'; import { mobileOSDetect, percentOf, removeTrailingZeros, roundOffDecimal, setDecimalPlaces } from '@/utils'; import { useExchangeRates } from '@deriv-com/api-hooks'; @@ -28,25 +28,31 @@ const FloatingRate = ({ onChange, value, }: TFloatingRate) => { - const { subscribeRates } = useExchangeRates(); + const { data: exchangeRateData, subscribeRates } = useExchangeRates(); const { isMobile } = useDevice(); const { data: p2pSettings } = api.settings.useSettings(); const overrideExchangeRate = p2pSettings?.override_exchange_rate; - const exchangeRateRef = useRef(null); + const exchangeRateRef = useRef(undefined); useEffect(() => { if (localCurrency) { - exchangeRateRef.current = subscribeRates({ + subscribeRates({ base_currency: 'USD', target_currencies: [localCurrency], }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [localCurrency]); - const marketRate = overrideExchangeRate - ? Number(overrideExchangeRate) - : exchangeRateRef.current?.rates?.[localCurrency] ?? 1; + useEffect(() => { + if (exchangeRateData?.exchange_rates?.rates) { + exchangeRateRef.current = exchangeRateData?.exchange_rates?.rates?.[localCurrency]; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [exchangeRateData]); + + const marketRate = overrideExchangeRate ? Number(overrideExchangeRate) : exchangeRateRef.current ?? 1; const os = mobileOSDetect(); const marketFeed = value ? percentOf(marketRate, Number(value)) : marketRate; const decimalPlace = setDecimalPlaces(marketFeed, 6); diff --git a/src/pages/my-ads/components/AdSummary/AdSummary.tsx b/src/pages/my-ads/components/AdSummary/AdSummary.tsx index 5a6c9ba5..5773cb10 100644 --- a/src/pages/my-ads/components/AdSummary/AdSummary.tsx +++ b/src/pages/my-ads/components/AdSummary/AdSummary.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import { TCurrency, TExchangeRate } from 'types'; +import { TCurrency } from 'types'; import { AD_ACTION, RATE_TYPE } from '@/constants'; import { api } from '@/hooks'; import { useQueryString } from '@/hooks/custom-hooks'; @@ -33,7 +33,7 @@ const AdSummary = ({ const { queryString } = useQueryString(); const adOption = queryString.formAction; const { data: p2pSettings } = api.settings.useSettings(); - const { subscribeRates } = useExchangeRates(); + const { data: exchangeRatesData, subscribeRates } = useExchangeRates(); const overrideExchangeRate = p2pSettings?.override_exchange_rate; const marketRateType = adOption === AD_ACTION.CREATE ? rateType : adRateType; @@ -43,11 +43,11 @@ const AdSummary = ({ let displayPriceRate: number | string = ''; let displayTotal = ''; - const exchangeRateRef = useRef(null); + const exchangeRateRef = useRef(undefined); useEffect(() => { if (localCurrency) { - exchangeRateRef.current = subscribeRates({ + subscribeRates({ base_currency: currency, target_currencies: [localCurrency], }); @@ -55,7 +55,14 @@ const AdSummary = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [currency, localCurrency]); - const exchangeRate = exchangeRateRef?.current?.rates?.[localCurrency]; + useEffect(() => { + if (exchangeRatesData?.exchange_rates?.rates) { + exchangeRateRef.current = exchangeRatesData.exchange_rates?.rates?.[localCurrency]; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [exchangeRatesData]); + + const exchangeRate = exchangeRateRef?.current; const marketRate = overrideExchangeRate ? Number(overrideExchangeRate) : exchangeRate; const marketFeed = marketRateType === RATE_TYPE.FLOAT ? marketRate : null; const summaryTextSize = isMobile ? 'md' : 'sm'; diff --git a/src/pages/my-ads/screens/MyAds/MyAdsTableRow/MyAdsTableRow.tsx b/src/pages/my-ads/screens/MyAds/MyAdsTableRow/MyAdsTableRow.tsx index fc2ef96a..15d0a3c7 100644 --- a/src/pages/my-ads/screens/MyAds/MyAdsTableRow/MyAdsTableRow.tsx +++ b/src/pages/my-ads/screens/MyAds/MyAdsTableRow/MyAdsTableRow.tsx @@ -1,6 +1,6 @@ import { memo, useEffect, useRef, useState } from 'react'; import clsx from 'clsx'; -import { TCurrency, TExchangeRate, TLocalize } from 'types'; +import { TCurrency, TLocalize } from 'types'; import { PaymentMethodLabel, PopoverDropdown } from '@/components'; import { AD_ACTION, ADVERT_TYPE, RATE_TYPE } from '@/constants'; import { useFloatingRate } from '@/hooks/custom-hooks'; @@ -39,7 +39,7 @@ type TMyAdsTableProps = Omit { const { localize } = useTranslations(); const { isMobile } = useDevice(); - const { subscribeRates } = useExchangeRates(); + const { data: exchangeRatesData, subscribeRates } = useExchangeRates(); const { account_currency: accountCurrency = '', @@ -66,17 +66,25 @@ const MyAdsTableRow = ({ currentRateType, showModal, ...rest }: TMyAdsTableProps const isFloatingRate = rateType === RATE_TYPE.FLOAT; - const exchangeRateRef = useRef(null); + const exchangeRateRef = useRef(undefined); useEffect(() => { if (localCurrency) { - exchangeRateRef.current = subscribeRates({ + subscribeRates({ base_currency: BASE_CURRENCY, target_currencies: [localCurrency], }); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [localCurrency]); + useEffect(() => { + if (exchangeRatesData?.exchange_rates?.rates) { + exchangeRateRef.current = exchangeRatesData.exchange_rates?.rates?.[localCurrency ?? '']; + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [exchangeRatesData]); + const [showAlertIcon, setShowAlertIcon] = useState(false); const isAdvertListed = isListed && !isBarred; const adPauseColor = isAdvertListed ? 'general' : 'less-prominent'; @@ -85,11 +93,12 @@ const MyAdsTableRow = ({ currentRateType, showModal, ...rest }: TMyAdsTableProps const isRowDisabled = !isActive || isBarred || !isListed; const isAdActive = !!isActive && !isBarred; - const exchangeRate = exchangeRateRef.current?.rates?.[localCurrency ?? '']; + const exchangeRate = exchangeRateRef.current; const enableActionPoint = currentRateType !== rateType; useEffect(() => { setShowAlertIcon(enableActionPoint || shouldShowTooltipIcon(visibilityStatus) || !isListed); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [enableActionPoint, isListed, shouldShowTooltipIcon]); const { displayEffectiveRate } = generateEffectiveRate({ From 8412f821bb3ee0be169ddd1b44934c338355f149 Mon Sep 17 00:00:00 2001 From: Niloofar Sadeghi <93518187+niloofar-deriv@users.noreply.github.com> Date: Fri, 24 May 2024 14:09:31 +0800 Subject: [PATCH 2/2] Niloofar/ Footer component (#76) * feat: footer base components * fix: review comments * feat: endpoint component * fix: styles --- package-lock.json | 35 +++++++--- package.json | 2 +- src/components/AppFooter/AccountLimits.tsx | 20 ++++++ src/components/AppFooter/AppFooter.scss | 48 ++++++++++++-- src/components/AppFooter/AppFooter.tsx | 65 +++++++++++++------ src/components/AppFooter/ChangeTheme.tsx | 15 +++++ src/components/AppFooter/Deriv.tsx | 21 ++++++ src/components/AppFooter/Endpoint.tsx | 21 ++++++ src/components/AppFooter/FullScreen.tsx | 22 +++++++ src/components/AppFooter/HelpCentre.tsx | 21 ++++++ src/components/AppFooter/LanguageSettings.tsx | 30 +++++++++ src/components/AppFooter/Livechat.tsx | 15 +++++ src/components/AppFooter/NetworkStatus.tsx | 17 +++++ .../AppFooter/ResponsibleTrading.tsx | 21 ++++++ src/components/AppFooter/ServerTime.tsx | 17 +++++ src/components/AppFooter/WhatsApp.tsx | 21 ++++++ .../AppFooter/__tests__/AppFooter.spec.tsx | 23 ------- .../Modals/LanguagesModal/LanguagesModal.scss | 23 ------- .../Modals/LanguagesModal/LanguagesModal.tsx | 45 ------------- src/components/Modals/LanguagesModal/index.ts | 1 - src/components/Modals/index.ts | 1 - src/constants/languages.ts | 29 --------- src/constants/languages.tsx | 29 +++++++++ src/hooks/custom-hooks/index.ts | 1 + src/hooks/custom-hooks/useFullScreen.ts | 43 ++++++++++++ 25 files changed, 427 insertions(+), 159 deletions(-) create mode 100644 src/components/AppFooter/AccountLimits.tsx create mode 100644 src/components/AppFooter/ChangeTheme.tsx create mode 100644 src/components/AppFooter/Deriv.tsx create mode 100644 src/components/AppFooter/Endpoint.tsx create mode 100644 src/components/AppFooter/FullScreen.tsx create mode 100644 src/components/AppFooter/HelpCentre.tsx create mode 100644 src/components/AppFooter/LanguageSettings.tsx create mode 100644 src/components/AppFooter/Livechat.tsx create mode 100644 src/components/AppFooter/NetworkStatus.tsx create mode 100644 src/components/AppFooter/ResponsibleTrading.tsx create mode 100644 src/components/AppFooter/ServerTime.tsx create mode 100644 src/components/AppFooter/WhatsApp.tsx delete mode 100644 src/components/AppFooter/__tests__/AppFooter.spec.tsx delete mode 100644 src/components/Modals/LanguagesModal/LanguagesModal.scss delete mode 100644 src/components/Modals/LanguagesModal/LanguagesModal.tsx delete mode 100644 src/components/Modals/LanguagesModal/index.ts delete mode 100644 src/constants/languages.ts create mode 100644 src/constants/languages.tsx create mode 100644 src/hooks/custom-hooks/useFullScreen.ts diff --git a/package-lock.json b/package-lock.json index fab5ffa9..e8fd2255 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@babel/preset-env": "^7.24.5", "@deriv-com/api-hooks": "^0.1.19", "@deriv-com/translations": "^1.2.3", - "@deriv-com/ui": "^1.21.1", + "@deriv-com/ui": "^1.26.0", "@deriv-com/utils": "latest", "@deriv/deriv-api": "^1.0.15", "@deriv/quill-design": "^1.2.24", @@ -2716,12 +2716,13 @@ } }, "node_modules/@deriv-com/ui": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.21.1.tgz", - "integrity": "sha512-8aokCnRAQJqnpr8Kzz4qpdRoqy2EJrusfVtUSS8nb1uUvIQiMRwPMzqPt1A/OojFmTDHpES7YJPPLlu2e90LpQ==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.26.0.tgz", + "integrity": "sha512-nk6NVpuqE/ibvuJ82+Eu5pqfaMVWTVOMj0lyMouD7oTcI1etkat2TdQ0qObDp9KGrLRhhNKrTlC6OT2gQpBgog==", "dependencies": { "@deriv/quill-icons": "^1.22.5", - "@types/react-modal": "^3.16.3" + "@types/react-modal": "^3.16.3", + "react-tiny-popover": "^8.0.4" }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "^4.13.0" @@ -16998,6 +16999,15 @@ "react-dom": ">=18.0.0" } }, + "node_modules/react-tiny-popover": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/react-tiny-popover/-/react-tiny-popover-8.0.4.tgz", + "integrity": "sha512-pn0Y/G0gyMdYTBEWSKCCnaZsXAa54PkfnRE4fnMM5633SSClYrXxwXKc6vPYgJ9shLatGginxMjnhXq6guZmng==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -22684,13 +22694,14 @@ } }, "@deriv-com/ui": { - "version": "1.21.1", - "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.21.1.tgz", - "integrity": "sha512-8aokCnRAQJqnpr8Kzz4qpdRoqy2EJrusfVtUSS8nb1uUvIQiMRwPMzqPt1A/OojFmTDHpES7YJPPLlu2e90LpQ==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@deriv-com/ui/-/ui-1.26.0.tgz", + "integrity": "sha512-nk6NVpuqE/ibvuJ82+Eu5pqfaMVWTVOMj0lyMouD7oTcI1etkat2TdQ0qObDp9KGrLRhhNKrTlC6OT2gQpBgog==", "requires": { "@deriv/quill-icons": "^1.22.5", "@rollup/rollup-linux-x64-gnu": "^4.13.0", - "@types/react-modal": "^3.16.3" + "@types/react-modal": "^3.16.3", + "react-tiny-popover": "^8.0.4" } }, "@deriv-com/utils": { @@ -32933,6 +32944,12 @@ "integrity": "sha512-NTFkW8W3uwvI82Fv7JW5i7gmDjEZKxJmj+Z9vn+BjYIXT6ILdnU9qnSUP2cWrWN/WAUlue81f9SgM4CQcenltQ==", "requires": {} }, + "react-tiny-popover": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/react-tiny-popover/-/react-tiny-popover-8.0.4.tgz", + "integrity": "sha512-pn0Y/G0gyMdYTBEWSKCCnaZsXAa54PkfnRE4fnMM5633SSClYrXxwXKc6vPYgJ9shLatGginxMjnhXq6guZmng==", + "requires": {} + }, "react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", diff --git a/package.json b/package.json index 1daa2d89..797d2c34 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@babel/preset-env": "^7.24.5", "@deriv-com/api-hooks": "^0.1.19", "@deriv-com/translations": "^1.2.3", - "@deriv-com/ui": "^1.21.1", + "@deriv-com/ui": "^1.26.0", "@deriv-com/utils": "latest", "@deriv/deriv-api": "^1.0.15", "@deriv/quill-design": "^1.2.24", diff --git a/src/components/AppFooter/AccountLimits.tsx b/src/components/AppFooter/AccountLimits.tsx new file mode 100644 index 00000000..a039bfcb --- /dev/null +++ b/src/components/AppFooter/AccountLimits.tsx @@ -0,0 +1,20 @@ +import { LegacyAccountLimitsIcon } from '@deriv/quill-icons'; +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +const AccountLimits = () => { + const { localize } = useTranslations(); + + return ( + + + + ); +}; + +export default AccountLimits; diff --git a/src/components/AppFooter/AppFooter.scss b/src/components/AppFooter/AppFooter.scss index 222db1ef..a4ab0a7e 100644 --- a/src/components/AppFooter/AppFooter.scss +++ b/src/components/AppFooter/AppFooter.scss @@ -1,13 +1,47 @@ .app-footer { - padding: 2.4rem; + height: 3.2rem; + display: flex; + flex-direction: row-reverse; + align-items: center; + position: fixed; + bottom: 0; + right: 0; + left: 0; + border-top: 1px solid #f2f3f4; + box-shadow: 0px 1px 0px 0px #f2f3f4 inset; + background: #fff; - &__language-btn { - &:hover { - background-color: transparent !important; + &__icon { + padding: 0.8rem; + } + + &__vertical-line { + width: 0.1rem; + height: 1.6rem; + background-color: #f2f3f4; + margin: 0 0.8rem; + } + + &__language { + display: flex; + align-items: center; + padding: 0.8rem; - span { - color: #000 !important; - } + & span { + margin-left: 0.4rem; } } + + &__network-status { + width: 0.8rem; + height: 0.8rem; + border-radius: 100%; + background-color: #4bb4b3; + margin-left: 2rem; + } + + &__endpoint { + color: #377cfc; + text-decoration: underline; + } } diff --git a/src/components/AppFooter/AppFooter.tsx b/src/components/AppFooter/AppFooter.tsx index 340e4947..147d7288 100644 --- a/src/components/AppFooter/AppFooter.tsx +++ b/src/components/AppFooter/AppFooter.tsx @@ -1,31 +1,56 @@ -import { LanguagesModal } from '@/components/Modals'; import { LANGUAGES } from '@/constants'; -import { useModalManager } from '@/hooks/custom-hooks'; -import { IconTypes } from '@deriv/quill-icons'; +import { useModalManager } from '@/hooks'; import { useTranslations } from '@deriv-com/translations'; -import { Button, Footer } from '@deriv-com/ui'; +import { DesktopLanguagesModal } from '@deriv-com/ui'; +import AccountLimits from './AccountLimits'; +import ChangeTheme from './ChangeTheme'; +import Deriv from './Deriv'; +import Endpoint from './Endpoint'; +import FullScreen from './FullScreen'; +import HelpCentre from './HelpCentre'; +import LanguageSettings from './LanguageSettings'; +import Livechat from './Livechat'; +import { NetworkStatus } from './NetworkStatus'; +import ResponsibleTrading from './ResponsibleTrading'; +import { ServerTime } from './ServerTime'; +import WhatsApp from './WhatsApp'; import './AppFooter.scss'; -// TODO: handle local storage values not updating after changing local storage values const AppFooter = () => { - const { currentLang } = useTranslations(); + const { currentLang, localize, switchLanguage } = useTranslations(); const { hideModal, isModalOpenFor, showModal } = useModalManager(); - const CountryIcon = LANGUAGES.find(lang => lang.code === currentLang)?.icon as IconTypes; + + const openLanguageSettingModal = () => showModal('DesktopLanguagesModal'); return ( -
- - {isModalOpenFor('LanguagesModal') && } -
+
+ + + +
+ + + + + + +
+ +
+ + + + {isModalOpenFor('DesktopLanguagesModal') && ( + switchLanguage(code)} + selectedLanguage={currentLang} + /> + )} +
); }; diff --git a/src/components/AppFooter/ChangeTheme.tsx b/src/components/AppFooter/ChangeTheme.tsx new file mode 100644 index 00000000..b5dcf083 --- /dev/null +++ b/src/components/AppFooter/ChangeTheme.tsx @@ -0,0 +1,15 @@ +import { LegacySettings1pxIcon } from '@deriv/quill-icons'; +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +const ChangeTheme = () => { + const { localize } = useTranslations(); + + return ( + + + + ); +}; + +export default ChangeTheme; diff --git a/src/components/AppFooter/Deriv.tsx b/src/components/AppFooter/Deriv.tsx new file mode 100644 index 00000000..1e7d9f76 --- /dev/null +++ b/src/components/AppFooter/Deriv.tsx @@ -0,0 +1,21 @@ +import { LegacyDerivIcon } from '@deriv/quill-icons'; +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +const Deriv = () => { + const { localize } = useTranslations(); + + return ( + + + + ); +}; + +export default Deriv; diff --git a/src/components/AppFooter/Endpoint.tsx b/src/components/AppFooter/Endpoint.tsx new file mode 100644 index 00000000..4ea8565e --- /dev/null +++ b/src/components/AppFooter/Endpoint.tsx @@ -0,0 +1,21 @@ +import { Link } from 'react-router-dom'; +import { Text } from '@deriv-com/ui'; + +const Endpoint = () => { + const serverURL = localStorage.getItem('config.server_url'); + + if (serverURL) { + return ( + + The server{' '} + + endpoint + {' '} + {`is: ${serverURL}`} + + ); + } + return null; +}; + +export default Endpoint; diff --git a/src/components/AppFooter/FullScreen.tsx b/src/components/AppFooter/FullScreen.tsx new file mode 100644 index 00000000..69140426 --- /dev/null +++ b/src/components/AppFooter/FullScreen.tsx @@ -0,0 +1,22 @@ +import { useFullScreen } from '@/hooks'; +import { LegacyFullscreen1pxIcon } from '@deriv/quill-icons'; +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +const FullScreen = () => { + const { toggleFullScreenMode } = useFullScreen(); + const { localize } = useTranslations(); + + return ( + + + + ); +}; + +export default FullScreen; diff --git a/src/components/AppFooter/HelpCentre.tsx b/src/components/AppFooter/HelpCentre.tsx new file mode 100644 index 00000000..7beae7d4 --- /dev/null +++ b/src/components/AppFooter/HelpCentre.tsx @@ -0,0 +1,21 @@ +import { LegacyHelpCentreIcon } from '@deriv/quill-icons'; +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +const HelpCentre = () => { + const { localize } = useTranslations(); + + return ( + + + + ); +}; + +export default HelpCentre; diff --git a/src/components/AppFooter/LanguageSettings.tsx b/src/components/AppFooter/LanguageSettings.tsx new file mode 100644 index 00000000..5add8a3a --- /dev/null +++ b/src/components/AppFooter/LanguageSettings.tsx @@ -0,0 +1,30 @@ +import { LANGUAGES } from '@/constants'; +import { useTranslations } from '@deriv-com/translations'; +import { Text, TooltipMenuIcon } from '@deriv-com/ui'; +import './AppFooter.scss'; + +type TLanguageSettings = { + openLanguageSettingModal: () => void; +}; + +const LanguageSettings = ({ openLanguageSettingModal }: TLanguageSettings) => { + const { currentLang, localize } = useTranslations(); + + const countryIcon = LANGUAGES.find(({ code }) => code == currentLang)?.icon; + + return ( + + {countryIcon} + + {currentLang} + + + ); +}; + +export default LanguageSettings; diff --git a/src/components/AppFooter/Livechat.tsx b/src/components/AppFooter/Livechat.tsx new file mode 100644 index 00000000..dae77d22 --- /dev/null +++ b/src/components/AppFooter/Livechat.tsx @@ -0,0 +1,15 @@ +import { LegacyLiveChatOutlineIcon } from '@deriv/quill-icons'; +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +const Livechat = () => { + const { localize } = useTranslations(); + + return ( + + + + ); +}; + +export default Livechat; diff --git a/src/components/AppFooter/NetworkStatus.tsx b/src/components/AppFooter/NetworkStatus.tsx new file mode 100644 index 00000000..df87c97b --- /dev/null +++ b/src/components/AppFooter/NetworkStatus.tsx @@ -0,0 +1,17 @@ +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +export const NetworkStatus = () => { + const { localize } = useTranslations(); + + return ( + +
+ + ); +}; diff --git a/src/components/AppFooter/ResponsibleTrading.tsx b/src/components/AppFooter/ResponsibleTrading.tsx new file mode 100644 index 00000000..007cf981 --- /dev/null +++ b/src/components/AppFooter/ResponsibleTrading.tsx @@ -0,0 +1,21 @@ +import { LegacyResponsibleTradingIcon } from '@deriv/quill-icons'; +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +const ResponsibleTrading = () => { + const { localize } = useTranslations(); + + return ( + + + + ); +}; + +export default ResponsibleTrading; diff --git a/src/components/AppFooter/ServerTime.tsx b/src/components/AppFooter/ServerTime.tsx new file mode 100644 index 00000000..1eb93591 --- /dev/null +++ b/src/components/AppFooter/ServerTime.tsx @@ -0,0 +1,17 @@ +import { useTranslations } from '@deriv-com/translations'; +import { Text, TooltipMenuIcon } from '@deriv-com/ui'; + +export const ServerTime = () => { + const { localize } = useTranslations(); + + return ( + + 01 Jan 2021 00:00:00 GMT + + ); +}; diff --git a/src/components/AppFooter/WhatsApp.tsx b/src/components/AppFooter/WhatsApp.tsx new file mode 100644 index 00000000..a4dbb0df --- /dev/null +++ b/src/components/AppFooter/WhatsApp.tsx @@ -0,0 +1,21 @@ +import { LegacyWhatsappIcon } from '@deriv/quill-icons'; +import { useTranslations } from '@deriv-com/translations'; +import { TooltipMenuIcon } from '@deriv-com/ui'; + +const WhatsApp = () => { + const { localize } = useTranslations(); + + return ( + + + + ); +}; + +export default WhatsApp; diff --git a/src/components/AppFooter/__tests__/AppFooter.spec.tsx b/src/components/AppFooter/__tests__/AppFooter.spec.tsx deleted file mode 100644 index dfdb0b98..00000000 --- a/src/components/AppFooter/__tests__/AppFooter.spec.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import AppFooter from '../AppFooter'; - -jest.mock('use-query-params', () => ({ - ...jest.requireActual('use-query-params'), - useQueryParams: jest.fn().mockReturnValue([{}, jest.fn()]), -})); - -jest.mock('@deriv-com/translations', () => ({ - useTranslations: jest.fn(() => ({ currentLang: 'EN' })), -})); - -jest.mock('@deriv-com/ui', () => ({ - ...jest.requireActual('@deriv-com/ui'), - useDevice: jest.fn(() => ({ isMobile: false })), -})); - -describe('', () => { - it('should render the footer', () => { - render(); - expect(screen.getByRole('button', { name: 'EN' })).toBeInTheDocument(); - }); -}); diff --git a/src/components/Modals/LanguagesModal/LanguagesModal.scss b/src/components/Modals/LanguagesModal/LanguagesModal.scss deleted file mode 100644 index 00e89425..00000000 --- a/src/components/Modals/LanguagesModal/LanguagesModal.scss +++ /dev/null @@ -1,23 +0,0 @@ -.languages-modal { - &__body { - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-gap: 1rem; - padding: 2rem; - - &-button { - height: auto; - display: flex; - align-items: center; - flex-direction: column; - - &:hover { - background-color: transparent !important; - - span { - color: #000 !important; - } - } - } - } -} diff --git a/src/components/Modals/LanguagesModal/LanguagesModal.tsx b/src/components/Modals/LanguagesModal/LanguagesModal.tsx deleted file mode 100644 index b312e768..00000000 --- a/src/components/Modals/LanguagesModal/LanguagesModal.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { LANGUAGES } from '@/constants'; -import { useTranslations } from '@deriv-com/translations'; -import { Button, Modal, Text } from '@deriv-com/ui'; -import './LanguagesModal.scss'; - -type TLanguagesModalProps = { - isModalOpen: boolean; - onClose: () => void; -}; - -const LanguagesModal = ({ isModalOpen, onClose }: TLanguagesModalProps) => { - const { currentLang, switchLanguage } = useTranslations(); - - return ( - - - {'Select Language'} - - - {LANGUAGES.map(language => { - const LanguageIcon = language.icon; - return ( - - ); - })} - - - ); -}; - -export default LanguagesModal; diff --git a/src/components/Modals/LanguagesModal/index.ts b/src/components/Modals/LanguagesModal/index.ts deleted file mode 100644 index 0cfe8480..00000000 --- a/src/components/Modals/LanguagesModal/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as LanguagesModal } from './LanguagesModal'; diff --git a/src/components/Modals/index.ts b/src/components/Modals/index.ts index f98f6fdf..1d239b27 100644 --- a/src/components/Modals/index.ts +++ b/src/components/Modals/index.ts @@ -15,7 +15,6 @@ export * from './EmailVerificationModal'; export * from './ErrorModal'; export * from './FilterModal'; export * from './InvalidVerificationLinkModal'; -export * from './LanguagesModal'; export * from './LoadingModal'; export * from './MyAdsDeleteModal'; export * from './NicknameModal'; diff --git a/src/constants/languages.ts b/src/constants/languages.ts deleted file mode 100644 index 9e5ac83b..00000000 --- a/src/constants/languages.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { - FlagFranceIcon, - FlagGermanyIcon, - FlagItalyIcon, - FlagPolandIcon, - FlagPortugalIcon, - FlagRussiaIcon, - FlagSpainIcon, - FlagThailandIcon, - FlagTurkeyIcon, - FlagUnitedArabEmiratesIcon, - FlagUnitedKingdomIcon, - FlagVietnamIcon, -} from '@deriv/quill-icons'; - -export const LANGUAGES = [ - { code: 'AR', display_name: 'العربية', icon: FlagUnitedArabEmiratesIcon }, - { code: 'DE', display_name: 'Deutsch', icon: FlagGermanyIcon }, - { code: 'EN', display_name: 'English', icon: FlagUnitedKingdomIcon }, - { code: 'ES', display_name: 'Español', icon: FlagSpainIcon }, - { code: 'FR', display_name: 'Français', icon: FlagFranceIcon }, - { code: 'IT', display_name: 'Italiano', icon: FlagItalyIcon }, - { code: 'PL', display_name: 'Polish', icon: FlagPolandIcon }, - { code: 'PT', display_name: 'Português', icon: FlagPortugalIcon }, - { code: 'RU', display_name: 'Русский', icon: FlagRussiaIcon }, - { code: 'TH', display_name: 'ไทย', icon: FlagThailandIcon }, - { code: 'TR', display_name: 'Türkçe', icon: FlagTurkeyIcon }, - { code: 'VI', display_name: 'Tiếng Việt', icon: FlagVietnamIcon }, -]; diff --git a/src/constants/languages.tsx b/src/constants/languages.tsx new file mode 100644 index 00000000..ba01c7ef --- /dev/null +++ b/src/constants/languages.tsx @@ -0,0 +1,29 @@ +import { + FlagFranceIcon, + FlagGermanyIcon, + FlagItalyIcon, + FlagPolandIcon, + FlagPortugalIcon, + FlagRussiaIcon, + FlagSpainIcon, + FlagThailandIcon, + FlagTurkeyIcon, + FlagUnitedArabEmiratesIcon, + FlagUnitedKingdomIcon, + FlagVietnamIcon, +} from '@deriv/quill-icons'; + +export const LANGUAGES = [ + { code: 'AR', displayName: 'العربية', icon: }, + { code: 'DE', displayName: 'Deutsch', icon: }, + { code: 'EN', displayName: 'English', icon: }, + { code: 'ES', displayName: 'Español', icon: }, + { code: 'FR', displayName: 'Français', icon: }, + { code: 'IT', displayName: 'Italiano', icon: }, + { code: 'PL', displayName: 'Polish', icon: }, + { code: 'PT', displayName: 'Português', icon: }, + { code: 'RU', displayName: 'Русский', icon: }, + { code: 'TH', displayName: 'ไทย', icon: }, + { code: 'TR', displayName: 'Türkçe', icon: }, + { code: 'VI', displayName: 'Tiếng Việt', icon: }, +]; diff --git a/src/hooks/custom-hooks/index.ts b/src/hooks/custom-hooks/index.ts index 3a18f3ba..dd9862cc 100644 --- a/src/hooks/custom-hooks/index.ts +++ b/src/hooks/custom-hooks/index.ts @@ -4,6 +4,7 @@ export { default as useDevice } from './useDevice'; export { default as useExtendedOrderDetails } from './useExtendedOrderDetails'; export { default as useFetchMore } from './useFetchMore'; export { default as useFloatingRate } from './useFloatingRate'; +export { default as useFullScreen } from './useFullScreen'; export { default as useIsAdvertiser } from './useIsAdvertiser'; export { default as useIsAdvertiserBarred } from './useIsAdvertiserBarred'; export { default as useModalManager } from './useModalManager'; diff --git a/src/hooks/custom-hooks/useFullScreen.ts b/src/hooks/custom-hooks/useFullScreen.ts new file mode 100644 index 00000000..bf50da23 --- /dev/null +++ b/src/hooks/custom-hooks/useFullScreen.ts @@ -0,0 +1,43 @@ +import { MouseEvent, useCallback, useEffect, useState } from 'react'; + +const fullScreenControls = { + exit: ['exitFullscreen', 'webkitExitFullscreen', 'mozCancelFullScreen', 'msExitFullscreen'], + request: ['requestFullscreen', 'webkitRequestFullscreen', 'mozRequestFullScreen', 'msRequestFullscreen'], + screenChange: ['fullscreenchange', 'webkitfullscreenchange', 'mozfullscreenchange', 'MSFullscreenChange'], + screenElement: ['fullscreenElement', 'webkitFullscreenElement', 'mozFullScreenElement', 'msFullscreenElement'], +} as const; + +const useFullScreen = () => { + const [isInFullScreenMode, setFullScreenMode] = useState(false); + const { exit, request, screenChange, screenElement } = fullScreenControls; + + const onFullScreen = useCallback( + () => setFullScreenMode(screenElement.some(element => document[element as keyof Document])), + [screenElement] + ); + + useEffect(() => { + screenChange.forEach(event => { + document.addEventListener(event, onFullScreen, false); + }); + }, [onFullScreen, screenChange]); + + const toggleFullScreenMode = (event: MouseEvent) => { + event.stopPropagation(); + + const exitFullScreen = exit.find(element => document[element as keyof Document]); + const requestFullScreen = request.find(element => document.documentElement[element as keyof HTMLElement]); + + if (isInFullScreenMode && exitFullScreen) { + (document[exitFullScreen as keyof Document] as Document['exitFullscreen'])(); + } else if (requestFullScreen) { + (document.documentElement[requestFullScreen as keyof HTMLElement] as HTMLElement['requestFullscreen'])(); + } else { + setFullScreenMode(false); // fullscreen API is not enabled + } + }; + + return { toggleFullScreenMode }; +}; + +export default useFullScreen;