Consolidate legal/requirements fetch

Ticket: 1128

Requirements:
- Fetch once per refresh. This is to cover the case of switching to VPNs, etc.
- 1 call per refreshed session.
This commit is contained in:
infinite-persistence 2022-03-17 14:12:16 +08:00 committed by Thomas Zarebczan
parent f01004e9b6
commit 51079ea611
13 changed files with 73 additions and 85 deletions

1
flow-typed/user.js vendored
View file

@ -65,6 +65,7 @@ declare type UserState ={
referrerSetIsPending: boolean, referrerSetIsPending: boolean,
referrerSetError: string, referrerSetError: string,
odyseeMembershipsPerClaimIds: ?{ [string]: string }, odyseeMembershipsPerClaimIds: ?{ [string]: string },
locale: ?LocaleInfo,
}; };
declare type LocaleInfo = { declare type LocaleInfo = {

View file

@ -2,7 +2,7 @@ import { hot } from 'react-hot-loader/root';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { selectGetSyncErrorMessage, selectSyncFatalError, selectSyncIsLocked } from 'redux/selectors/sync'; import { selectGetSyncErrorMessage, selectSyncFatalError, selectSyncIsLocked } from 'redux/selectors/sync';
import { doUserSetReferrer } from 'redux/actions/user'; import { doUserSetReferrer } from 'redux/actions/user';
import { selectUser, selectUserVerifiedEmail } from 'redux/selectors/user'; import { selectUser, selectUserLocale, selectUserVerifiedEmail } from 'redux/selectors/user';
import { selectUnclaimedRewards } from 'redux/selectors/rewards'; import { selectUnclaimedRewards } from 'redux/selectors/rewards';
import { doFetchChannelListMine, doFetchCollectionListMine, doResolveUris } from 'redux/actions/claims'; import { doFetchChannelListMine, doFetchCollectionListMine, doResolveUris } from 'redux/actions/claims';
import { selectMyChannelClaimIds } from 'redux/selectors/claims'; import { selectMyChannelClaimIds } from 'redux/selectors/claims';
@ -24,6 +24,7 @@ import App from './view';
const select = (state) => ({ const select = (state) => ({
user: selectUser(state), user: selectUser(state),
locale: selectUserLocale(state),
theme: selectThemePath(state), theme: selectThemePath(state),
language: selectLanguage(state), language: selectLanguage(state),
languages: selectLoadedLanguages(state), languages: selectLoadedLanguages(state),

View file

@ -1,6 +1,5 @@
// @flow // @flow
import * as PAGES from 'constants/pages'; import * as PAGES from 'constants/pages';
import * as SETTINGS from 'constants/settings';
import React, { useEffect, useRef, useState, useLayoutEffect } from 'react'; import React, { useEffect, useRef, useState, useLayoutEffect } from 'react';
import { lazyImport } from 'util/lazyImport'; import { lazyImport } from 'util/lazyImport';
import { tusUnlockAndNotify, tusHandleTabUpdates } from 'util/tus'; import { tusUnlockAndNotify, tusHandleTabUpdates } from 'util/tus';
@ -35,7 +34,6 @@ import {
} from 'web/effects/use-degraded-performance'; } from 'web/effects/use-degraded-performance';
import LANGUAGE_MIGRATIONS from 'constants/language-migrations'; import LANGUAGE_MIGRATIONS from 'constants/language-migrations';
import { useIsMobile } from 'effects/use-screensize'; import { useIsMobile } from 'effects/use-screensize';
import { fetchLocaleApi } from 'locale';
import getLanguagesForCountry from 'constants/country_languages'; import getLanguagesForCountry from 'constants/country_languages';
import SUPPORTED_LANGUAGES from 'constants/supported_languages'; import SUPPORTED_LANGUAGES from 'constants/supported_languages';
@ -64,6 +62,7 @@ type Props = {
languages: Array<string>, languages: Array<string>,
theme: string, theme: string,
user: ?{ id: string, has_verified_email: boolean, is_reward_approved: boolean }, user: ?{ id: string, has_verified_email: boolean, is_reward_approved: boolean },
locale: ?LocaleInfo,
location: { pathname: string, hash: string, search: string }, location: { pathname: string, hash: string, search: string },
history: { push: (string) => void }, history: { push: (string) => void },
fetchChannelListMine: () => void, fetchChannelListMine: () => void,
@ -98,6 +97,7 @@ function App(props: Props) {
const { const {
theme, theme,
user, user,
locale,
fetchChannelListMine, fetchChannelListMine,
fetchCollectionListMine, fetchCollectionListMine,
signIn, signIn,
@ -137,10 +137,7 @@ function App(props: Props) {
const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail); const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail);
const previousRewardApproved = usePrevious(isRewardApproved); const previousRewardApproved = usePrevious(isRewardApproved);
const [gdprRequired, setGdprRequired] = usePersistedState('gdprRequired');
const [localeLangs, setLocaleLangs] = React.useState(); const [localeLangs, setLocaleLangs] = React.useState();
const [localeSwitchDismissed] = usePersistedState('locale-switch-dismissed', false);
const [showAnalyticsNag, setShowAnalyticsNag] = usePersistedState('analytics-nag', true); const [showAnalyticsNag, setShowAnalyticsNag] = usePersistedState('analytics-nag', true);
const [lbryTvApiStatus, setLbryTvApiStatus] = useState(STATUS_OK); const [lbryTvApiStatus, setLbryTvApiStatus] = useState(STATUS_OK);
@ -365,17 +362,6 @@ function App(props: Props) {
} }
}, [previousRewardApproved, isRewardApproved]); }, [previousRewardApproved, isRewardApproved]);
useEffect(() => {
fetchLocaleApi().then((response) => {
const locale: LocaleInfo = response?.data;
if (locale) {
// Put in 'window' for now. Can be moved to localStorage or wherever,
// but the key should remain the same so clients are not affected.
window[SETTINGS.LOCALE] = locale;
}
});
}, []);
// Load IMA3 SDK for aniview // Load IMA3 SDK for aniview
// useEffect(() => { // useEffect(() => {
// if (!isAuthenticated && SHOW_ADS) { // if (!isAuthenticated && SHOW_ADS) {
@ -402,7 +388,7 @@ function App(props: Props) {
} }
} }
if (inIframe()) { if (inIframe() || !locale || !locale.gdpr_required) {
return; return;
} }
@ -422,47 +408,10 @@ function App(props: Props) {
// OneTrust asks to add this // OneTrust asks to add this
secondScript.innerHTML = 'function OptanonWrapper() { }'; secondScript.innerHTML = 'function OptanonWrapper() { }';
// gdpr is known to be required, add script // $FlowFixMe
if (gdprRequired) { document.head.appendChild(script);
// $FlowFixMe // $FlowFixMe
document.head.appendChild(script); document.head.appendChild(secondScript);
// $FlowFixMe
document.head.appendChild(secondScript);
}
const shouldFetchLanguage = !localeLangs && !localeSwitchDismissed;
const shouldFetchGdpr = gdprRequired === null || gdprRequired === undefined;
if (shouldFetchLanguage || shouldFetchGdpr) {
fetchLocaleApi().then((response) => {
if (shouldFetchLanguage) {
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 (shouldFetchGdpr) {
const gdprRequiredBasedOnLocation = response?.data?.gdpr_required;
// note we need gdpr and load script
if (gdprRequiredBasedOnLocation) {
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) {
setGdprRequired(false);
}
}
});
}
return () => { return () => {
try { try {
@ -472,11 +421,23 @@ function App(props: Props) {
document.head.removeChild(secondScript); document.head.removeChild(secondScript);
} catch (err) { } catch (err) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(err); // console.log(err); <-- disabling this ... it's clogging up Sentry logs.
} }
}; };
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps, (one time after locale is fetched)
}, []); }, [locale]);
React.useEffect(() => {
if (locale) {
const countryCode = locale.country;
const langs = getLanguagesForCountry(countryCode) || [];
const supportedLangs = langs.filter((lang) => lang !== 'en' && SUPPORTED_LANGUAGES[lang]);
if (supportedLangs.length > 0) {
setLocaleLangs(supportedLangs);
}
}
}, [locale]);
// ready for sync syncs, however after signin when hasVerifiedEmail, that syncs too. // ready for sync syncs, however after signin when hasVerifiedEmail, that syncs too.
useEffect(() => { useEffect(() => {
@ -563,7 +524,7 @@ function App(props: Props) {
/> />
) : ( ) : (
<React.Fragment> <React.Fragment>
<Router /> <Router />
<ModalRouter /> <ModalRouter />
<React.Suspense fallback={null}>{renderFiledrop && <FileDrop />}</React.Suspense> <React.Suspense fallback={null}>{renderFiledrop && <FileDrop />}</React.Suspense>

View file

@ -302,6 +302,7 @@ export const USER_SET_REFERRER_SUCCESS = 'USER_SET_REFERRER_SUCCESS';
export const USER_SET_REFERRER_FAILURE = 'USER_SET_REFERRER_FAILURE'; export const USER_SET_REFERRER_FAILURE = 'USER_SET_REFERRER_FAILURE';
export const USER_SET_REFERRER_RESET = 'USER_SET_REFERRER_RESET'; export const USER_SET_REFERRER_RESET = 'USER_SET_REFERRER_RESET';
export const USER_EMAIL_VERIFY_RETRY = 'USER_EMAIL_VERIFY_RETRY'; export const USER_EMAIL_VERIFY_RETRY = 'USER_EMAIL_VERIFY_RETRY';
export const USER_FETCH_LOCALE_DONE = 'USER_FETCH_LOCALE_DONE';
export const ADD_ODYSEE_MEMBERSHIP_DATA = 'ADD_ODYSEE_MEMBERSHIP_DATA'; export const ADD_ODYSEE_MEMBERSHIP_DATA = 'ADD_ODYSEE_MEMBERSHIP_DATA';
export const ADD_CLAIMIDS_MEMBERSHIP_DATA = 'ADD_CLAIMIDS_MEMBERSHIP_DATA'; export const ADD_CLAIMIDS_MEMBERSHIP_DATA = 'ADD_CLAIMIDS_MEMBERSHIP_DATA';

View file

@ -41,7 +41,6 @@ export const SUPPORT_OPTION = 'support_option';
export const TILE_LAYOUT = 'tile_layout'; export const TILE_LAYOUT = 'tile_layout';
export const VIDEO_THEATER_MODE = 'video_theater_mode'; export const VIDEO_THEATER_MODE = 'video_theater_mode';
export const VIDEO_PLAYBACK_RATE = 'video_playback_rate'; export const VIDEO_PLAYBACK_RATE = 'video_playback_rate';
export const LOCALE = 'odysee_user_locale';
export const SETTINGS_GRP = { export const SETTINGS_GRP = {
APPEARANCE: 'appearance', APPEARANCE: 'appearance',

View file

@ -18,6 +18,7 @@ import Lbry, { apiCall } from 'lbry';
import { isURIValid } from 'util/lbryURI'; import { isURIValid } from 'util/lbryURI';
import { setSearchApi } from 'redux/actions/search'; import { setSearchApi } from 'redux/actions/search';
import { doSetLanguage, doFetchLanguage, doUpdateIsNightAsync, doFetchHomepages } from 'redux/actions/settings'; import { doSetLanguage, doFetchLanguage, doUpdateIsNightAsync, doFetchHomepages } from 'redux/actions/settings';
import { doFetchUserLocale } from 'redux/actions/user';
import { Lbryio, doBlackListedOutpointsSubscribe, doFilteredOutpointsSubscribe } from 'lbryinc'; import { Lbryio, doBlackListedOutpointsSubscribe, doFilteredOutpointsSubscribe } from 'lbryinc';
import rewards from 'rewards'; import rewards from 'rewards';
import { store, persistor, history } from 'store'; import { store, persistor, history } from 'store';
@ -246,6 +247,7 @@ function AppWrapper() {
app.store.dispatch(doUpdateIsNightAsync()); app.store.dispatch(doUpdateIsNightAsync());
app.store.dispatch(doBlackListedOutpointsSubscribe()); app.store.dispatch(doBlackListedOutpointsSubscribe());
app.store.dispatch(doFilteredOutpointsSubscribe()); app.store.dispatch(doFilteredOutpointsSubscribe());
app.store.dispatch(doFetchUserLocale());
}, 25); }, 25);
analytics.startupEvent(Date.now()); analytics.startupEvent(Date.now());

View file

@ -1,6 +0,0 @@
// @flow
import { LOCALE_API } from 'config';
export async function fetchLocaleApi() {
return fetch(LOCALE_API).then((res) => res.json());
}

View file

@ -4,7 +4,7 @@ import OdyseeMembership from './view';
import { selectActiveChannelClaim, selectIncognito } from 'redux/selectors/app'; import { selectActiveChannelClaim, selectIncognito } from 'redux/selectors/app';
import { selectMyChannelClaims, selectClaimsByUri } from 'redux/selectors/claims'; import { selectMyChannelClaims, selectClaimsByUri } from 'redux/selectors/claims';
import { doFetchUserMemberships, doCheckUserOdyseeMemberships } from 'redux/actions/user'; import { doFetchUserMemberships, doCheckUserOdyseeMemberships } from 'redux/actions/user';
import { selectUser } from 'redux/selectors/user'; import { selectUser, selectUserLocale } from 'redux/selectors/user';
const select = (state) => { const select = (state) => {
const activeChannelClaim = selectActiveChannelClaim(state); const activeChannelClaim = selectActiveChannelClaim(state);
@ -15,6 +15,7 @@ const select = (state) => {
claimsByUri: selectClaimsByUri(state), claimsByUri: selectClaimsByUri(state),
incognito: selectIncognito(state), incognito: selectIncognito(state),
user: selectUser(state), user: selectUser(state),
locale: selectUserLocale(state),
}; };
}; };

View file

@ -17,7 +17,6 @@ import PremiumBadge from 'component/common/premium-badge';
import I18nMessage from 'component/i18nMessage'; import I18nMessage from 'component/i18nMessage';
import useGetUserMemberships from 'effects/use-get-user-memberships'; import useGetUserMemberships from 'effects/use-get-user-memberships';
import usePersistedState from 'effects/use-persisted-state'; import usePersistedState from 'effects/use-persisted-state';
import { fetchLocaleApi } from 'locale';
let stripeEnvironment = getStripeEnvironment(); let stripeEnvironment = getStripeEnvironment();
@ -39,6 +38,7 @@ type Props = {
incognito: boolean, incognito: boolean,
updateUserOdyseeMembershipStatus: () => void, updateUserOdyseeMembershipStatus: () => void,
user: ?User, user: ?User,
locale: ?LocaleInfo,
}; };
const OdyseeMembershipPage = (props: Props) => { const OdyseeMembershipPage = (props: Props) => {
@ -51,6 +51,7 @@ const OdyseeMembershipPage = (props: Props) => {
updateUserOdyseeMembershipStatus, updateUserOdyseeMembershipStatus,
incognito, incognito,
user, user,
locale,
} = props; } = props;
const userChannelName = activeChannelClaim ? activeChannelClaim.name : ''; const userChannelName = activeChannelClaim ? activeChannelClaim.name : '';
@ -183,13 +184,8 @@ const OdyseeMembershipPage = (props: Props) => {
console.log(err); console.log(err);
} }
try { if (locale?.continent === 'EU') {
// use EUR if from European continent setCurrencyToUse('eur');
const localeResponse = await fetchLocaleApi();
const isFromEurope = localeResponse?.data?.continent === 'EU';
if (isFromEurope) setCurrencyToUse('eur');
} catch (err) {
console.log(err);
} }
populateMembershipData(); populateMembershipData();

View file

@ -12,7 +12,7 @@ import { selectEmailToVerify, selectPhoneToVerify, selectUserCountryCode, select
import { doToast } from 'redux/actions/notifications'; import { doToast } from 'redux/actions/notifications';
import rewards from 'rewards'; import rewards from 'rewards';
import { Lbryio } from 'lbryinc'; import { Lbryio } from 'lbryinc';
import { DOMAIN } from 'config'; import { DOMAIN, LOCALE_API } from 'config';
import { getDefaultLanguage } from 'util/default-languages'; import { getDefaultLanguage } from 'util/default-languages';
const AUTH_IN_PROGRESS = 'authInProgress'; const AUTH_IN_PROGRESS = 'authInProgress';
export let sessionStorageAvailable = false; export let sessionStorageAvailable = false;
@ -899,3 +899,29 @@ export function doFetchUserMemberships(claimIdCsv) {
dispatch({ type: ACTIONS.ADD_CLAIMIDS_MEMBERSHIP_DATA, data: { response: updatedResponse } }); dispatch({ type: ACTIONS.ADD_CLAIMIDS_MEMBERSHIP_DATA, data: { response: updatedResponse } });
}; };
} }
export function doFetchUserLocale(isRetry = false) {
return (dispatch) => {
fetch(LOCALE_API)
.then((res) => res.json())
.then((json) => {
const locale = json.data; // [flow] local: LocaleInfo
if (locale) {
dispatch({
type: ACTIONS.USER_FETCH_LOCALE_DONE,
data: locale,
});
}
})
.catch(() => {
if (!isRetry) {
// If failed, retry one more time after N seconds. This removes the
// need to fetch at each component level. If it failed twice, probably
// don't need to fetch anymore.
setTimeout(() => {
dispatch(doFetchUserLocale(true));
}, 10000);
}
});
};
}

View file

@ -31,6 +31,7 @@ const defaultState: UserState = {
referrerSetIsPending: false, referrerSetIsPending: false,
referrerSetError: '', referrerSetError: '',
odyseeMembershipsPerClaimIds: undefined, odyseeMembershipsPerClaimIds: undefined,
locale: undefined,
}; };
reducers[ACTIONS.AUTHENTICATION_STARTED] = (state) => reducers[ACTIONS.AUTHENTICATION_STARTED] = (state) =>
@ -365,6 +366,11 @@ reducers[ACTIONS.USER_PASSWORD_SET_FAILURE] = (state, action) =>
passwordSetError: action.data.error, passwordSetError: action.data.error,
}); });
reducers[ACTIONS.USER_FETCH_LOCALE_DONE] = (state, action) =>
Object.assign({}, state, {
locale: action.data,
});
reducers[ACTIONS.ADD_ODYSEE_MEMBERSHIP_DATA] = (state, action) => { reducers[ACTIONS.ADD_ODYSEE_MEMBERSHIP_DATA] = (state, action) => {
return Object.assign({}, state, { return Object.assign({}, state, {
odyseeMembershipName: action.data.odyseeMembershipName, odyseeMembershipName: action.data.odyseeMembershipName,

View file

@ -2,7 +2,7 @@
import { CHANNEL_CREATION_LIMIT } from 'config'; import { CHANNEL_CREATION_LIMIT } from 'config';
import { normalizeURI, parseURI, isURIValid } from 'util/lbryURI'; import { normalizeURI, parseURI, isURIValid } from 'util/lbryURI';
import { selectGeoBlockLists } from 'redux/selectors/blocked'; import { selectGeoBlockLists } from 'redux/selectors/blocked';
import { selectYoutubeChannels } from 'redux/selectors/user'; import { selectUserLocale, selectYoutubeChannels } from 'redux/selectors/user';
import { selectSupportsByOutpoint } from 'redux/selectors/wallet'; import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { createCachedSelector } from 're-reselect'; import { createCachedSelector } from 're-reselect';
@ -14,7 +14,6 @@ import {
getThumbnailFromClaim, getThumbnailFromClaim,
} from 'util/claim'; } from 'util/claim';
import * as CLAIM from 'constants/claim'; import * as CLAIM from 'constants/claim';
import * as SETTINGS from 'constants/settings';
import { INTERNAL_TAGS } from 'constants/tags'; import { INTERNAL_TAGS } from 'constants/tags';
type State = { claims: any, user: UserState }; type State = { claims: any, user: UserState };
@ -844,9 +843,8 @@ export const selectOdyseeMembershipForChannelId = function (state: State, channe
export const selectGeoRestrictionForUri = createCachedSelector( export const selectGeoRestrictionForUri = createCachedSelector(
selectClaimForUri, selectClaimForUri,
selectGeoBlockLists, selectGeoBlockLists,
(claim, geoBlockLists) => { selectUserLocale,
const locale: LocaleInfo = window[SETTINGS.LOCALE]; // <-- NOTE: not handled by redux updates (claim, geoBlockLists, locale: LocaleInfo) => {
if (locale && geoBlockLists && claim) { if (locale && geoBlockLists && claim) {
const claimId: ?string = claim.claim_id; const claimId: ?string = claim.claim_id;
const channelId: ?string = getChannelIdFromClaim(claim); const channelId: ?string = getChannelIdFromClaim(claim);

View file

@ -127,3 +127,5 @@ export const selectYouTubeImportVideosComplete = createSelector(selectState, (st
}); });
export const makeSelectUserPropForProp = (prop) => createSelector(selectUser, (user) => (user ? user[prop] : null)); export const makeSelectUserPropForProp = (prop) => createSelector(selectUser, (user) => (user ? user[prop] : null));
export const selectUserLocale = (state) => selectState(state).locale;