// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React, {useEffect, useMemo} from 'react' import { Router, Redirect, Route, Switch, } from 'react-router-dom' import {IntlProvider} from 'react-intl' import {DndProvider} from 'react-dnd' import {HTML5Backend} from 'react-dnd-html5-backend' import {TouchBackend} from 'react-dnd-touch-backend' import {createBrowserHistory} from 'history' import TelemetryClient from './telemetry/telemetryClient' import {IAppWindow} from './types' import {getMessages} from './i18n' import {FlashMessages} from './components/flashMessages' import NewVersionBanner from './components/newVersionBanner' import BoardPage from './pages/boardPage' import ChangePasswordPage from './pages/changePasswordPage' import DashboardPage from './pages/dashboard/dashboardPage' import WelcomePage from './pages/welcome/welcomePage' import ErrorPage from './pages/errorPage' import LoginPage from './pages/loginPage' import RegisterPage from './pages/registerPage' import {Utils} from './utils' import wsClient from './wsclient' import {fetchMe, getLoggedIn, getMe} from './store/users' import {getLanguage, fetchLanguage} from './store/language' import {setGlobalError, getGlobalError} from './store/globalError' import {useAppSelector, useAppDispatch} from './store/hooks' import {fetchClientConfig} from './store/clientConfig' import {IUser} from './user' import {UserSettings} from './userSettings' declare let window: IAppWindow const UUID_REGEX = new RegExp(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/) const App = (): JSX.Element => { const language = useAppSelector(getLanguage) const loggedIn = useAppSelector(getLoggedIn) const globalError = useAppSelector(getGlobalError) const me = useAppSelector(getMe) const dispatch = useAppDispatch() const browserHistory: ReturnType = useMemo(() => { const history = createBrowserHistory({basename: Utils.getFrontendBaseURL()}) if (Utils.isDesktop() && Utils.isFocalboardPlugin()) { window.addEventListener('message', (event: MessageEvent) => { if (event.origin !== window.location.origin) { return } const pathName = event.data.message?.pathName if (!pathName || !pathName.startsWith(window.frontendBaseURL)) { return } Utils.log(`Navigating Boards to ${pathName}`) history.replace(pathName.replace(window.frontendBaseURL, '')) }) } return { ...history, push: (path: string, state?: unknown) => { if (Utils.isDesktop() && Utils.isFocalboardPlugin()) { window.postMessage( { type: 'browser-history-push', message: { path: `${window.frontendBaseURL}${path}`, }, }, window.location.origin, ) } else { history.push(path, state) } }, } }, []) useEffect(() => { dispatch(fetchLanguage()) dispatch(fetchMe()) dispatch(fetchClientConfig()) }, []) if (Utils.isFocalboardPlugin()) { useEffect(() => { if (window.frontendBaseURL) { browserHistory.replace(window.location.pathname.replace(window.frontendBaseURL, '')) } }, []) } // this is a temporary solution while we're using legacy routes // for shared boards as a way to disable websockets, and should be // removed when anonymous plugin routes are implemented. This // check is used to detect if we're running inside the plugin but // in a legacy route if (!Utils.isFocalboardLegacy()) { useEffect(() => { wsClient.open() return () => { wsClient.close() } }, []) } useEffect(() => { if (me) { TelemetryClient.setUser(me) } }, [me]) let globalErrorRedirect = null if (globalError) { globalErrorRedirect = setTimeout(() => dispatch(setGlobalError('')), 0) } const continueToWelcomeScreen = () => { return Utils.isFocalboardPlugin() && loggedIn === true && !UserSettings.welcomePageViewed } return (
{globalErrorRedirect} { Utils.isFocalboardPlugin() && { if (loggedIn === false) { return } if (continueToWelcomeScreen()) { return } if (Utils.isFocalboardPlugin() && UserSettings.lastWorkspaceId) { return } if (loggedIn === true) { return } return null }} /> } { if (loggedIn === false) { return } if (continueToWelcomeScreen()) { const originalPath = `/board/${Utils.buildOriginalPath('', boardId, viewId, cardId)}` return } if (loggedIn === true) { return } return null }} /> { const originalPath = `/workspace/${Utils.buildOriginalPath(workspaceId, boardId, viewId, cardId)}` if (loggedIn === false) { let redirectUrl = '/' + Utils.buildURL(originalPath) if (redirectUrl.indexOf('//') === 0) { redirectUrl = redirectUrl.slice(1) } const loginUrl = `/login?r=${encodeURIComponent(redirectUrl)}` return } else if (loggedIn === true) { if (continueToWelcomeScreen()) { return } return ( ) } return null }} /> {!Utils.isFocalboardPlugin() && { // Since these 3 path values are optional and they can be anything, we can pass /x/y/z and it will // match this route however these values may not be valid so we should at the very least check // board id for descisions made below const boardIdIsValidUUIDV4 = UUID_REGEX.test(boardId || '') if (loggedIn === false) { return } if (continueToWelcomeScreen()) { const originalPath = `/${Utils.buildOriginalPath('', boardId, viewId, cardId)}` const queryString = boardIdIsValidUUIDV4 ? `r=${originalPath}` : '' return } if (loggedIn === true) { return } return null }} />}
) } export default React.memo(App)