2020-10-20 21:50:53 +02:00
|
|
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
|
|
// See LICENSE.txt for license information.
|
2021-08-02 17:46:00 +02:00
|
|
|
import React, {useEffect} from 'react'
|
2020-10-09 17:10:26 +02:00
|
|
|
import {
|
2021-09-16 16:45:15 +02:00
|
|
|
Router,
|
2020-12-04 16:03:09 +01:00
|
|
|
Redirect,
|
2021-01-21 19:16:40 +01:00
|
|
|
Route,
|
|
|
|
Switch,
|
2020-10-20 21:52:56 +02:00
|
|
|
} from 'react-router-dom'
|
2021-07-14 21:22:40 +02:00
|
|
|
import {IntlProvider} from 'react-intl'
|
|
|
|
import {DndProvider} from 'react-dnd'
|
|
|
|
import {HTML5Backend} from 'react-dnd-html5-backend'
|
|
|
|
import {TouchBackend} from 'react-dnd-touch-backend'
|
2020-10-09 17:10:26 +02:00
|
|
|
|
2021-09-16 16:45:15 +02:00
|
|
|
import {createBrowserHistory} from 'history'
|
|
|
|
|
2021-09-01 23:53:27 +02:00
|
|
|
import TelemetryClient from './telemetry/telemetryClient'
|
|
|
|
|
2021-10-16 18:36:03 +02:00
|
|
|
import {IAppWindow} from './types'
|
2021-07-14 21:22:40 +02:00
|
|
|
import {getMessages} from './i18n'
|
2020-10-28 17:56:27 +01:00
|
|
|
import {FlashMessages} from './components/flashMessages'
|
2021-01-21 19:16:40 +01:00
|
|
|
import BoardPage from './pages/boardPage'
|
|
|
|
import ChangePasswordPage from './pages/changePasswordPage'
|
2021-09-14 18:42:18 +02:00
|
|
|
import DashboardPage from './pages/dashboard/dashboardPage'
|
2021-09-28 15:51:32 +02:00
|
|
|
import WelcomePage from './pages/welcome/welcomePage'
|
2021-03-26 19:01:54 +01:00
|
|
|
import ErrorPage from './pages/errorPage'
|
2020-10-20 21:52:56 +02:00
|
|
|
import LoginPage from './pages/loginPage'
|
2020-12-04 16:03:09 +01:00
|
|
|
import RegisterPage from './pages/registerPage'
|
2021-04-16 13:42:37 +02:00
|
|
|
import {Utils} from './utils'
|
2021-07-28 18:14:18 +02:00
|
|
|
import wsClient from './wsclient'
|
2021-09-01 23:53:27 +02:00
|
|
|
import {fetchMe, getLoggedIn, getMe} from './store/users'
|
2021-07-14 21:22:40 +02:00
|
|
|
import {getLanguage, fetchLanguage} from './store/language'
|
2021-08-02 17:46:00 +02:00
|
|
|
import {setGlobalError, getGlobalError} from './store/globalError'
|
2021-07-14 21:22:40 +02:00
|
|
|
import {useAppSelector, useAppDispatch} from './store/hooks'
|
2021-09-16 21:31:02 +02:00
|
|
|
import {fetchClientConfig} from './store/clientConfig'
|
2020-10-09 17:10:26 +02:00
|
|
|
|
2021-09-01 23:53:27 +02:00
|
|
|
import {IUser} from './user'
|
2021-09-28 15:51:32 +02:00
|
|
|
import {UserSettings} from './userSettings'
|
2021-09-01 23:53:27 +02:00
|
|
|
|
2021-10-16 18:36:03 +02:00
|
|
|
declare let window: IAppWindow
|
|
|
|
|
2021-09-16 16:45:15 +02:00
|
|
|
export const history = createBrowserHistory({basename: Utils.getFrontendBaseURL()})
|
|
|
|
|
2021-09-28 15:51:32 +02:00
|
|
|
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}$/)
|
|
|
|
|
2021-09-16 16:45:15 +02:00
|
|
|
if (Utils.isDesktop() && Utils.isFocalboardPlugin()) {
|
|
|
|
window.addEventListener('message', (event: MessageEvent) => {
|
|
|
|
if (event.origin !== window.location.origin) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-22 06:12:41 +02:00
|
|
|
const pathName = event.data.message?.pathName
|
2021-10-16 18:36:03 +02:00
|
|
|
if (!pathName || !pathName.startsWith(window.frontendBaseURL)) {
|
2021-09-16 16:45:15 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-10-06 10:41:47 +02:00
|
|
|
Utils.log(`Navigating Boards to ${pathName}`)
|
2021-10-16 18:36:03 +02:00
|
|
|
history.replace(pathName.replace(window.frontendBaseURL, ''))
|
2021-09-16 16:45:15 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-10-16 18:36:03 +02:00
|
|
|
const browserHistory: typeof history = {
|
2021-09-16 16:45:15 +02:00
|
|
|
...history,
|
2021-10-16 18:36:03 +02:00
|
|
|
push: (path: string, state?: unknown) => {
|
2021-09-16 16:45:15 +02:00
|
|
|
if (Utils.isDesktop() && Utils.isFocalboardPlugin()) {
|
|
|
|
window.postMessage(
|
|
|
|
{
|
|
|
|
type: 'browser-history-push',
|
|
|
|
message: {
|
2021-10-16 18:36:03 +02:00
|
|
|
path: `${window.frontendBaseURL}${path}`,
|
2021-09-16 16:45:15 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
window.location.origin,
|
|
|
|
)
|
|
|
|
} else {
|
2021-10-16 18:36:03 +02:00
|
|
|
history.push(path, state)
|
2021-09-16 16:45:15 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-04-09 19:34:35 +02:00
|
|
|
const App = React.memo((): JSX.Element => {
|
2021-07-14 21:22:40 +02:00
|
|
|
const language = useAppSelector<string>(getLanguage)
|
2021-08-02 17:46:00 +02:00
|
|
|
const loggedIn = useAppSelector<boolean|null>(getLoggedIn)
|
|
|
|
const globalError = useAppSelector<string>(getGlobalError)
|
2021-09-01 23:53:27 +02:00
|
|
|
const me = useAppSelector<IUser|null>(getMe)
|
2021-07-14 21:22:40 +02:00
|
|
|
const dispatch = useAppDispatch()
|
2020-12-07 20:40:16 +01:00
|
|
|
|
2021-04-09 19:34:35 +02:00
|
|
|
useEffect(() => {
|
2021-07-14 21:22:40 +02:00
|
|
|
dispatch(fetchLanguage())
|
2021-08-02 17:46:00 +02:00
|
|
|
dispatch(fetchMe())
|
2021-09-16 21:31:02 +02:00
|
|
|
dispatch(fetchClientConfig())
|
2021-04-09 19:34:35 +02:00
|
|
|
}, [])
|
2020-12-07 20:40:16 +01:00
|
|
|
|
2021-10-06 17:55:19 +02:00
|
|
|
if (Utils.isFocalboardPlugin()) {
|
|
|
|
useEffect(() => {
|
2021-10-16 18:36:03 +02:00
|
|
|
if (window.frontendBaseURL) {
|
|
|
|
history.replace(window.location.pathname.replace(window.frontendBaseURL, ''))
|
|
|
|
}
|
2021-10-06 17:55:19 +02:00
|
|
|
}, [])
|
|
|
|
}
|
|
|
|
|
2021-11-04 18:24:57 +01:00
|
|
|
// 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()) {
|
2021-10-04 18:01:49 +02:00
|
|
|
useEffect(() => {
|
|
|
|
wsClient.open()
|
|
|
|
return () => {
|
|
|
|
wsClient.close()
|
|
|
|
}
|
|
|
|
}, [])
|
|
|
|
}
|
2021-07-28 18:14:18 +02:00
|
|
|
|
2021-09-01 23:53:27 +02:00
|
|
|
useEffect(() => {
|
|
|
|
if (me) {
|
|
|
|
TelemetryClient.setUser(me)
|
|
|
|
}
|
|
|
|
}, [me])
|
|
|
|
|
2021-08-02 17:46:00 +02:00
|
|
|
let globalErrorRedirect = null
|
|
|
|
if (globalError) {
|
|
|
|
globalErrorRedirect = <Route path='/*'><Redirect to={`/error?id=${globalError}`}/></Route>
|
|
|
|
setTimeout(() => dispatch(setGlobalError('')), 0)
|
|
|
|
}
|
|
|
|
|
2021-10-04 11:14:18 +02:00
|
|
|
const continueToWelcomeScreen = () => {
|
|
|
|
return Utils.isFocalboardPlugin() && loggedIn === true && !UserSettings.welcomePageViewed
|
2021-09-28 15:51:32 +02:00
|
|
|
}
|
|
|
|
|
2021-04-09 19:34:35 +02:00
|
|
|
return (
|
2021-07-14 21:22:40 +02:00
|
|
|
<IntlProvider
|
|
|
|
locale={language.split(/[_]/)[0]}
|
|
|
|
messages={getMessages(language)}
|
2021-04-09 19:34:35 +02:00
|
|
|
>
|
2021-07-14 21:22:40 +02:00
|
|
|
<DndProvider backend={Utils.isMobile() ? TouchBackend : HTML5Backend}>
|
|
|
|
<FlashMessages milliseconds={2000}/>
|
2021-09-16 16:45:15 +02:00
|
|
|
<Router
|
|
|
|
history={browserHistory}
|
|
|
|
>
|
2021-07-14 21:22:40 +02:00
|
|
|
<div id='frame'>
|
|
|
|
<div id='main'>
|
|
|
|
<Switch>
|
2021-08-02 17:46:00 +02:00
|
|
|
{globalErrorRedirect}
|
2021-07-14 21:22:40 +02:00
|
|
|
<Route path='/error'>
|
|
|
|
<ErrorPage/>
|
|
|
|
</Route>
|
|
|
|
<Route path='/login'>
|
|
|
|
<LoginPage/>
|
|
|
|
</Route>
|
|
|
|
<Route path='/register'>
|
|
|
|
<RegisterPage/>
|
|
|
|
</Route>
|
|
|
|
<Route path='/change_password'>
|
|
|
|
<ChangePasswordPage/>
|
|
|
|
</Route>
|
2021-10-01 19:29:44 +02:00
|
|
|
<Route path='/shared/:boardId?/:viewId?/:cardId?'>
|
2021-07-14 21:22:40 +02:00
|
|
|
<BoardPage readonly={true}/>
|
|
|
|
</Route>
|
2021-09-28 15:51:32 +02:00
|
|
|
<Route
|
|
|
|
path='/board/:boardId?/:viewId?/:cardId?'
|
|
|
|
render={({match: {params: {boardId, viewId, cardId}}}) => {
|
|
|
|
if (loggedIn === false) {
|
|
|
|
return <Redirect to='/login'/>
|
|
|
|
}
|
|
|
|
|
|
|
|
if (continueToWelcomeScreen()) {
|
2021-10-22 11:07:46 +02:00
|
|
|
const originalPath = `/board/${Utils.buildOriginalPath('', boardId, viewId, cardId)}`
|
2021-09-28 15:51:32 +02:00
|
|
|
return <Redirect to={`/welcome?r=${originalPath}`}/>
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loggedIn === true) {
|
|
|
|
return <BoardPage/>
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
}}
|
|
|
|
/>
|
2021-10-01 19:29:44 +02:00
|
|
|
<Route path='/workspace/:workspaceId/shared/:boardId?/:viewId?/:cardId?'>
|
2021-07-14 21:22:40 +02:00
|
|
|
<BoardPage readonly={true}/>
|
|
|
|
</Route>
|
|
|
|
<Route
|
2021-08-26 11:27:06 +02:00
|
|
|
path='/workspace/:workspaceId/:boardId?/:viewId?/:cardId?'
|
2021-09-28 15:51:32 +02:00
|
|
|
render={({match: {params: {workspaceId, boardId, viewId, cardId}}}) => {
|
2021-10-22 11:07:46 +02:00
|
|
|
const originalPath = `/workspace/${Utils.buildOriginalPath(workspaceId, boardId, viewId, cardId)}`
|
2021-08-02 17:46:00 +02:00
|
|
|
if (loggedIn === false) {
|
2021-09-28 15:51:32 +02:00
|
|
|
let redirectUrl = '/' + Utils.buildURL(originalPath)
|
2021-07-14 21:22:40 +02:00
|
|
|
if (redirectUrl.indexOf('//') === 0) {
|
|
|
|
redirectUrl = redirectUrl.slice(1)
|
|
|
|
}
|
|
|
|
const loginUrl = `/login?r=${encodeURIComponent(redirectUrl)}`
|
|
|
|
return <Redirect to={loginUrl}/>
|
2021-08-02 17:46:00 +02:00
|
|
|
} else if (loggedIn === true) {
|
2021-09-28 15:51:32 +02:00
|
|
|
if (continueToWelcomeScreen()) {
|
|
|
|
return <Redirect to={`/welcome?r=${originalPath}`}/>
|
|
|
|
}
|
|
|
|
|
2021-08-02 17:46:00 +02:00
|
|
|
return (
|
|
|
|
<BoardPage/>
|
|
|
|
)
|
2021-04-26 13:43:02 +02:00
|
|
|
}
|
2021-08-02 17:46:00 +02:00
|
|
|
return null
|
2021-07-14 21:22:40 +02:00
|
|
|
}}
|
|
|
|
/>
|
2021-07-26 14:17:28 +02:00
|
|
|
<Route
|
|
|
|
exact={true}
|
|
|
|
path='/dashboard'
|
|
|
|
>
|
|
|
|
<DashboardPage/>
|
|
|
|
</Route>
|
2021-09-28 15:51:32 +02:00
|
|
|
<Route
|
|
|
|
exact={true}
|
|
|
|
path='/welcome'
|
|
|
|
>
|
|
|
|
<WelcomePage/>
|
2021-07-14 21:22:40 +02:00
|
|
|
</Route>
|
2021-09-28 15:51:32 +02:00
|
|
|
|
|
|
|
<Route
|
|
|
|
path='/:boardId?/:viewId?/:cardId?'
|
|
|
|
render={({match: {params: {boardId, viewId, cardId}}}) => {
|
|
|
|
// 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 <Redirect to='/login'/>
|
|
|
|
}
|
|
|
|
|
2021-10-04 11:14:18 +02:00
|
|
|
if (continueToWelcomeScreen()) {
|
2021-10-22 11:07:46 +02:00
|
|
|
const originalPath = `/${Utils.buildOriginalPath('', boardId, viewId, cardId)}`
|
2021-09-28 15:51:32 +02:00
|
|
|
const queryString = boardIdIsValidUUIDV4 ? `r=${originalPath}` : ''
|
|
|
|
return <Redirect to={`/welcome?${queryString}`}/>
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loggedIn === true) {
|
|
|
|
return <BoardPage/>
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
}}
|
|
|
|
/>
|
2021-07-14 21:22:40 +02:00
|
|
|
</Switch>
|
|
|
|
</div>
|
2021-04-12 17:47:47 +02:00
|
|
|
</div>
|
2021-07-14 21:22:40 +02:00
|
|
|
</Router>
|
|
|
|
</DndProvider>
|
|
|
|
</IntlProvider>
|
2021-04-09 19:34:35 +02:00
|
|
|
)
|
|
|
|
})
|
|
|
|
|
|
|
|
export default App
|