// @flow import React, { useEffect, Suspense, lazy } from 'react'; import { Route, Redirect, Switch, withRouter } from 'react-router-dom'; // @if TARGET='app' import BackupPage from 'page/backup'; // @endif // @if TARGET='web' import Code2257Page from 'web/page/code2257'; // @endif // constants import * as PAGES from 'constants/pages'; import { LINKED_COMMENT_QUERY_PARAM } from 'constants/comment'; import { parseURI, isURIValid } from 'lbry-redux'; import { SITE_TITLE, WELCOME_VERSION } from 'config'; // Code Splitting const BuyPage = lazy(() => import('page/buy')); const ChannelsPage = lazy(() => import('page/channels')); const ChannelsFollowingPage = lazy(() => import('page/channelsFollowing')); const ChannelsFollowingDiscoverPage = lazy(() => import('page/channelsFollowingDiscover')); const CreatorDashboard = lazy(() => import('page/creatorDashboard')); const CheckoutPage = lazy(() => import('page/checkoutPage')); const ChannelNew = lazy(() => import('page/channelNew')); const DiscoverPage = lazy(() => import('page/discover')); const EmbedWrapperPage = lazy(() => import('page/embedWrapper')); const FileListPublished = lazy(() => import('page/fileListPublished')); const FourOhFourPage = lazy(() => import('page/fourOhFour')); const HelpPage = lazy(() => import('page/help')); const HomePage = lazy(() => import('page/home')); const InvitedPage = lazy(() => import('page/invited')); const InvitePage = lazy(() => import('page/invite')); const LiveStreamPage = lazy(() => import('page/livestream')); const LibraryPage = lazy(() => import('page/library')); const ListBlockedPage = lazy(() => import('page/listBlocked')); const NotificationsPage = lazy(() => import('page/notifications')); const PasswordResetPage = lazy(() => import('page/passwordReset')); const PasswordSetPage = lazy(() => import('page/passwordSet')); const PublishPage = lazy(() => import('page/publish')); const RewardsPage = lazy(() => import('page/rewards')); const RewardsVerifyPage = lazy(() => import('page/rewardsVerify')); const ReportPage = lazy(() => import('page/report')); const RepostNew = lazy(() => import('page/repost')); const SignInPage = lazy(() => import('page/signIn')); const SignUpPage = lazy(() => import('page/signUp')); const SignInVerifyPage = lazy(() => import('page/signInVerify')); const SearchPage = lazy(() => import('page/search')); const SettingsPage = lazy(() => import('page/settings')); const SettingsNotificationsPage = lazy(() => import('page/settingsNotifications')); const SettingsAdvancedPage = lazy(() => import('page/settingsAdvanced')); const SignInWalletPasswordPage = lazy(() => import('page/signInWalletPassword')); const ShowPage = lazy(() => import('page/show')); const TagsFollowingPage = lazy(() => import('page/tagsFollowing')); const TagsFollowingManagePage = lazy(() => import('page/tagsFollowingManage')); const TopPage = lazy(() => import('page/top')); const WalletPage = lazy(() => import('page/wallet')); const Welcome = lazy(() => import('page/welcome')); const YoutubeSyncPage = lazy(() => import('page/youtubeSync')); // Tell the browser we are handling scroll restoration if ('scrollRestoration' in history) { history.scrollRestoration = 'manual'; } type Props = { currentScroll: number, isAuthenticated: boolean, location: { pathname: string, search: string, hash: string }, history: { action: string, entries: { title: string }[], goBack: () => void, goForward: () => void, index: number, length: number, location: { pathname: string }, push: (string) => void, state: {}, replaceState: ({}, string, string) => void, listen: (any) => () => void, }, uri: string, title: string, welcomeVersion: number, hasNavigated: boolean, setHasNavigated: () => void, setReferrer: (string) => void, hasUnclaimedRefereeReward: boolean, homepageData: any, }; type PrivateRouteProps = Props & { component: any, isAuthenticated: boolean, }; function PrivateRoute(props: PrivateRouteProps) { const { component: Component, isAuthenticated, ...rest } = props; return ( isAuthenticated || !IS_WEB ? ( ) : ( ) } /> ); } function AppRouter(props: Props) { const { currentScroll, location: { pathname, search, hash }, isAuthenticated, history, uri, title, welcomeVersion, hasNavigated, setHasNavigated, hasUnclaimedRefereeReward, setReferrer, homepageData, } = props; const { entries } = history; const entryIndex = history.index; const urlParams = new URLSearchParams(search); const resetScroll = urlParams.get('reset_scroll'); const hasLinkedCommentInUrl = urlParams.get(LINKED_COMMENT_QUERY_PARAM); const dynamicRoutes = Object.values(homepageData).filter( (potentialRoute: any) => potentialRoute && potentialRoute.route ); // For people arriving at settings page from deeplinks, know whether they can "go back" useEffect(() => { const unlisten = history.listen((location, action) => { if (action === 'PUSH') { if (!hasNavigated && setHasNavigated) setHasNavigated(); } }); return unlisten; }, [hasNavigated, setHasNavigated]); useEffect(() => { if (!hasNavigated && hasUnclaimedRefereeReward && !isAuthenticated) { const valid = isURIValid(uri); if (valid) { const { path } = parseURI(uri); if (path !== 'undefined') setReferrer(path); } } }, [hasNavigated, uri, hasUnclaimedRefereeReward, setReferrer, isAuthenticated]); useEffect(() => { if (uri) { const { channelName, streamName } = parseURI(uri); if (typeof title !== 'undefined' && title !== '') { document.title = title; } else if (streamName) { document.title = streamName; } else if (channelName) { document.title = channelName; } else { document.title = IS_WEB ? SITE_TITLE : 'LBRY'; } } else { document.title = IS_WEB ? SITE_TITLE : 'LBRY'; } // @if TARGET='app' entries[entryIndex].title = document.title; // @endif return () => { document.title = IS_WEB ? SITE_TITLE : 'LBRY'; }; }, [entries, entryIndex, title, uri]); useEffect(() => { if (!hasLinkedCommentInUrl) { if (hash && history.action === 'PUSH') { const id = hash.replace('#', ''); const element = document.getElementById(id); if (element) { window.scrollTo(0, element.offsetTop); } } else { window.scrollTo(0, currentScroll); } } }, [currentScroll, pathname, search, hash, resetScroll, hasLinkedCommentInUrl]); // react-router doesn't decode pathanmes before doing the route matching check // We have to redirect here because if we redirect on the server, it might get encoded again // in the browser causing a redirect loop const decodedUrl = decodeURIComponent(pathname) + search; if (decodedUrl !== pathname + search) { return ; } return ( Loading...}> {/* @if TARGET='app' */} {welcomeVersion < WELCOME_VERSION && } {/* @endif */} {/* $FlowFixMe */} {dynamicRoutes.map((dynamicRouteProps: RowDataItem) => ( } /> ))} {/* @if TARGET='app' */} {/* @endif */} {/* @if TARGET='web' */} {/* @endif */} {/* Below need to go at the end to make sure we don't match any of our pages first */} ); } export default withRouter(AppRouter);