From 712e02db167094119006d477e854fe7f672f5cd0 Mon Sep 17 00:00:00 2001 From: saltrafael <76502841+saltrafael@users.noreply.github.com> Date: Wed, 2 Mar 2022 11:44:01 -0300 Subject: [PATCH] Use locale/get response to suggest homepage and language switch (#839) * Use locale/get response to suggest homepage and language switch * Fix language modal condition * Fixes from review * Fixes from review * Fix gdpr * string * Fix multiple options behavior * Fix gdpr and use only one fetch * Only show if no languages set or loaded * pt-br * Fix ad * Fix homepage select * Fix zh langs --- .env.defaults | 1 + .flowconfig | 1 + config.js | 1 + static/app-strings.json | 7 + ui/component/app/view.jsx | 54 +++-- ui/component/common/nag.jsx | 24 ++- ui/component/homepageSelector/view.jsx | 4 +- ui/component/nagLocaleSwitch/index.js | 12 ++ ui/component/nagLocaleSwitch/view.jsx | 194 +++++++++++++++++ ui/constants/country_languages.js | 279 +++++++++++++++++++++++++ ui/constants/homepage_languages.js | 21 ++ ui/constants/languages.js | 8 + ui/constants/searchable_languages.js | 26 +-- ui/constants/supported_languages.js | 85 ++++---- ui/locale.js | 6 + ui/modal/modalConfirm/index.js | 7 +- ui/modal/modalConfirm/view.jsx | 31 ++- ui/page/discover/view.jsx | 4 +- ui/scss/component/_modal.scss | 5 + ui/scss/component/nag.scss | 13 ++ 20 files changed, 684 insertions(+), 99 deletions(-) create mode 100644 ui/component/nagLocaleSwitch/index.js create mode 100644 ui/component/nagLocaleSwitch/view.jsx create mode 100644 ui/constants/country_languages.js create mode 100644 ui/constants/homepage_languages.js create mode 100644 ui/locale.js diff --git a/.env.defaults b/.env.defaults index 9c2f6e817..9c1b0c5f2 100644 --- a/.env.defaults +++ b/.env.defaults @@ -18,6 +18,7 @@ SOCKETY_SERVER_API=wss://sockety.odysee.com/ws RECSYS_ENDPOINT=https://recsys.odysee.tv/v1/lvv THUMBNAIL_CDN_URL=https://thumbnails.odycdn.com/optimize/ THUMBNAIL_CARDS_CDN_URL=https://cards.odycdn.com/ +LOCALE_API=https://api.odysee.com/locale/get THUMBNAIL_HEIGHT=220 THUMBNAIL_WIDTH=390 THUMBNAIL_QUALITY=85 diff --git a/.flowconfig b/.flowconfig index 30d377476..b6992488b 100644 --- a/.flowconfig +++ b/.flowconfig @@ -31,6 +31,7 @@ module.name_mapper='^rewards\(.*\)$' -> '/ui/rewards\1' module.name_mapper='^i18n\(.*\)$' -> '/ui/i18n\1' module.name_mapper='^effects\(.*\)$' -> '/ui/effects\1' module.name_mapper='^comments\(.*\)$' -> '/ui/comments\1' +module.name_mapper='^locale\(.*\)$' -> '/ui/locale\1' module.name_mapper='^config\(.*\)$' -> '/config\1' module.name_mapper='^web\/component\(.*\)$' -> '/web/component\1' module.name_mapper='^web\/effects\(.*\)$' -> '/web/effects\1' diff --git a/config.js b/config.js index 82c4c96b3..aaae9da3f 100644 --- a/config.js +++ b/config.js @@ -16,6 +16,7 @@ const config = { SEARCH_SERVER_API_ALT: process.env.SEARCH_SERVER_API_ALT, COMMENT_SERVER_API: process.env.COMMENT_SERVER_API, SOCKETY_SERVER_API: process.env.SOCKETY_SERVER_API, + LOCALE_API: process.env.LOCALE_API, WELCOME_VERSION: process.env.WELCOME_VERSION, DOMAIN: process.env.DOMAIN, SHARE_DOMAIN_URL: process.env.SHARE_DOMAIN_URL, diff --git a/static/app-strings.json b/static/app-strings.json index b2b70ba0f..a2fae16f7 100644 --- a/static/app-strings.json +++ b/static/app-strings.json @@ -2209,5 +2209,12 @@ "The minimum duration must not exceed Feb 8th, 2022.": "The minimum duration must not exceed Feb 8th, 2022.", "No limit": "No limit", "Search results are being filtered by language. Click here to change the setting.": "Search results are being filtered by language. Click here to change the setting.", + "There are language translations available for your location! Do you want to switch from English?": "There are language translations available for your location! Do you want to switch from English?", + "A homepage and language translations are available for your location! Do you want to switch?": "A homepage and language translations are available for your location! Do you want to switch?", + "Switch Now": "Switch Now", + "Both": "Both", + "Only Language": "Only Language", + "Only Homepage": "Only Homepage", + "Choose Your Preference": "Choose Your Preference", "--end--": "--end--" } diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx index 8b351ee23..2bbc3d535 100644 --- a/ui/component/app/view.jsx +++ b/ui/component/app/view.jsx @@ -34,9 +34,13 @@ import { } from 'web/effects/use-degraded-performance'; import LANGUAGE_MIGRATIONS from 'constants/language-migrations'; import { useIsMobile } from 'effects/use-screensize'; +import { fetchLocaleApi } from 'locale'; +import getLanguagesForCountry from 'constants/country_languages'; +import SUPPORTED_LANGUAGES from 'constants/supported_languages'; const FileDrop = lazyImport(() => import('component/fileDrop' /* webpackChunkName: "fileDrop" */)); const NagContinueFirstRun = lazyImport(() => import('component/nagContinueFirstRun' /* webpackChunkName: "nagCFR" */)); +const NagLocaleSwitch = lazyImport(() => import('component/nagLocaleSwitch' /* webpackChunkName: "nagLocaleSwitch" */)); const OpenInAppLink = lazyImport(() => import('web/component/openInAppLink' /* webpackChunkName: "openInAppLink" */)); const NagDataCollection = lazyImport(() => import('web/component/nag-data-collection' /* webpackChunkName: "nagDC" */)); const NagDegradedPerformance = lazyImport(() => @@ -132,6 +136,10 @@ function App(props: Props) { const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail); const previousRewardApproved = usePrevious(isRewardApproved); + const [gdprRequired, setGdprRequired] = usePersistedState('gdprRequired'); + const [localeLangs, setLocaleLangs] = React.useState(); + const [localeSwitchDismissed] = usePersistedState('locale-switch-dismissed', false); + const [showAnalyticsNag, setShowAnalyticsNag] = usePersistedState('analytics-nag', true); const [lbryTvApiStatus, setLbryTvApiStatus] = useState(STATUS_OK); @@ -212,6 +220,11 @@ function App(props: Props) { /> ); } + + if (localeLangs) { + const noLanguageSet = language === 'en' && languages.length === 1; + return ; + } } useEffect(() => { @@ -312,6 +325,7 @@ function App(props: Props) { fetchModBlockedList(); fetchModAmIList(); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [hasMyChannels, hasNoChannels, hasActiveChannelClaim, setActiveChannelIfNotSet, setIncognito]); useEffect(() => { @@ -396,40 +410,42 @@ function App(props: Props) { // OneTrust asks to add this secondScript.innerHTML = 'function OptanonWrapper() { }'; - const getLocaleEndpoint = 'https://api.odysee.com/locale/get'; - let gdprRequired; - try { - gdprRequired = localStorage.getItem('gdprRequired'); - } catch (err) { - if (err) return; - } // gdpr is known to be required, add script - if (gdprRequired === 'true') { + if (gdprRequired) { // $FlowFixMe document.head.appendChild(script); // $FlowFixMe document.head.appendChild(secondScript); } - // haven't done a gdpr check, do it now - if (gdprRequired === null) { - (async function () { - const response = await fetch(getLocaleEndpoint); - const json = await response.json(); - const gdprRequiredBasedOnLocation = json.data.gdpr_required; + fetchLocaleApi().then((response) => { + if (!localeLangs && !localeSwitchDismissed) { + const countryCode = response?.data?.country; + const langs = getLanguagesForCountry(countryCode); + + const supportedLangs = []; + langs.forEach((lang) => lang !== 'en' && SUPPORTED_LANGUAGES[lang] && supportedLangs.push(lang)); + + if (supportedLangs.length > 0) setLocaleLangs(supportedLangs); + } + + // haven't done a gdpr check, do it now + if (gdprRequired === null || gdprRequired === undefined) { + const gdprRequiredBasedOnLocation = response?.data?.gdpr_required; + // note we need gdpr and load script if (gdprRequiredBasedOnLocation) { - localStorage.setItem('gdprRequired', 'true'); + setGdprRequired(true); // $FlowFixMe document.head.appendChild(script); // $FlowFixMe document.head.appendChild(secondScript); // note we don't need gdpr, save to session } else if (gdprRequiredBasedOnLocation === false) { - localStorage.setItem('gdprRequired', 'false'); + setGdprRequired(false); } - })(); - } + } + }); return () => { try { @@ -438,9 +454,11 @@ function App(props: Props) { // $FlowFixMe document.head.removeChild(secondScript); } catch (err) { + // eslint-disable-next-line no-console console.log(err); } }; + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // ready for sync syncs, however after signin when hasVerifiedEmail, that syncs too. diff --git a/ui/component/common/nag.jsx b/ui/component/common/nag.jsx index 58645fb17..dbd9f2440 100644 --- a/ui/component/common/nag.jsx +++ b/ui/component/common/nag.jsx @@ -7,6 +7,8 @@ import Button from 'component/button'; type Props = { message: string | Node, + action?: Node, + closeTitle?: string, actionText?: string, href?: string, type?: string, @@ -17,9 +19,20 @@ type Props = { }; export default function Nag(props: Props) { - const { message, actionText, href, onClick, onClose, type, inline, relative } = props; + const { + message, + action: customAction, + closeTitle, + actionText, + href, + onClick, + onClose, + type, + inline, + relative, + } = props; - const buttonProps = onClick ? { onClick } : { href }; + const buttonProps = onClick ? { onClick } : href ? { href } : null; return (
{message}
- {(href || onClick) && ( + + {customAction} + + {buttonProps && (
- +
+
} /> diff --git a/ui/page/discover/view.jsx b/ui/page/discover/view.jsx index 342926b87..40568782c 100644 --- a/ui/page/discover/view.jsx +++ b/ui/page/discover/view.jsx @@ -176,7 +176,9 @@ function DiscoverPage(props: Props) { } React.useEffect(() => { - if (isAuthenticated || !SHOW_ADS || window.location.pathname === `/$/${PAGES.WILD_WEST}`) { + const hasAdOnPage = document.querySelector('.homepageAdContainer'); + + if (hasAdOnPage || isAuthenticated || !SHOW_ADS || window.location.pathname === `/$/${PAGES.WILD_WEST}`) { return; } injectAd(); diff --git a/ui/scss/component/_modal.scss b/ui/scss/component/_modal.scss index 91be5bdca..f01f51489 100644 --- a/ui/scss/component/_modal.scss +++ b/ui/scss/component/_modal.scss @@ -109,6 +109,11 @@ z-index: 10000; margin-top: var(--spacing-s); } + + .radio, + .radio + h1 { + margin-top: var(--spacing-s); + } } .modal--card-internal { diff --git a/ui/scss/component/nag.scss b/ui/scss/component/nag.scss index c695071dd..0066a689b 100644 --- a/ui/scss/component/nag.scss +++ b/ui/scss/component/nag.scss @@ -112,3 +112,16 @@ $nag-error-z-index: 999; stroke-width: 4px; } } + +.nag__select { + display: inline; + color: var(--color-text); + + select { + width: unset; + min-width: 10rem; + padding-right: unset; + margin: 0px var(--spacing-s); + height: var(--height-button-mobile); + } +}