mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-25 04:24:43 -04:00
parent
557386f78f
commit
e2155e735d
65 changed files with 834 additions and 467 deletions
|
@ -8,8 +8,8 @@ export const getBackendConfig: () => Promise<BackendConfigState> = async () => {
|
|||
return await response.json() as Promise<BackendConfigState>
|
||||
}
|
||||
|
||||
export const getFrontendConfig: () => Promise<FrontendConfigState> = async () => {
|
||||
const response = await fetch('config.json')
|
||||
export const getFrontendConfig: (baseUrl: string) => Promise<FrontendConfigState> = async (baseUrl) => {
|
||||
const response = await fetch(`${baseUrl}config.json`)
|
||||
expectResponseCode(response)
|
||||
return await response.json() as Promise<FrontendConfigState>
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ export interface meResponse {
|
|||
photo: string
|
||||
}
|
||||
|
||||
export const postEmailLogin: ((email: string, password: string) => Promise<void>) = async (email, password) => {
|
||||
export const postEmailLogin = async (email: string, password: string):Promise<void> => {
|
||||
const response = await fetch(getBackendUrl() + '/auth/email', {
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import React, { Fragment, useEffect, useState } from 'react'
|
||||
import { useLocation } from 'react-router'
|
||||
import { setUp } from '../../initializers'
|
||||
import './application-loader.scss'
|
||||
import { LoadingScreen } from './loading-screen'
|
||||
|
||||
interface ApplicationLoaderProps {
|
||||
initTasks: Promise<void>[]
|
||||
}
|
||||
|
||||
export const ApplicationLoader: React.FC<ApplicationLoaderProps> = ({ children, initTasks }) => {
|
||||
export const ApplicationLoader: React.FC = ({ children }) => {
|
||||
const [failed, setFailed] = useState<boolean>(false)
|
||||
const [doneTasks, setDoneTasks] = useState<number>(0)
|
||||
const [initTasks, setInitTasks] = useState<Promise<void>[]>([])
|
||||
const { pathname } = useLocation()
|
||||
|
||||
const runTask:((task: Promise<void>) => (Promise<void>)) = async (task) => {
|
||||
const runTask = async (task: Promise<void>): Promise<void> => {
|
||||
await task
|
||||
setDoneTasks(prevDoneTasks => {
|
||||
return prevDoneTasks + 1
|
||||
|
@ -18,7 +18,12 @@ export const ApplicationLoader: React.FC<ApplicationLoaderProps> = ({ children,
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
setDoneTasks(0)
|
||||
const baseUrl:string = window.location.pathname.replace(pathname, '') + '/'
|
||||
console.debug('Base URL is', baseUrl)
|
||||
setInitTasks(setUp(baseUrl))
|
||||
}, [pathname])
|
||||
|
||||
useEffect(() => {
|
||||
for (const task of initTasks) {
|
||||
runTask(task).catch(reason => {
|
||||
setFailed(true)
|
||||
|
|
11
src/components/editor/editor-window/editor-window.tsx
Normal file
11
src/components/editor/editor-window/editor-window.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from 'react'
|
||||
|
||||
const EditorWindow: React.FC = () => {
|
||||
return (
|
||||
<div style={{ backgroundColor: 'green' }}>
|
||||
Hello, EditorWindow!
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { EditorWindow }
|
30
src/components/editor/editor.tsx
Normal file
30
src/components/editor/editor.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { useParams } from 'react-router'
|
||||
import { ApplicationState } from '../../redux'
|
||||
import { EditorMode } from '../../redux/editor/types'
|
||||
import { EditorWindow } from './editor-window/editor-window'
|
||||
import { MarkdownPreview } from './markdown-preview/markdown-preview'
|
||||
import { TaskBar } from './task-bar/task-bar'
|
||||
|
||||
interface RouteParameters {
|
||||
id: string
|
||||
}
|
||||
|
||||
const Editor: React.FC = () => {
|
||||
const editorMode: EditorMode = useSelector((state: ApplicationState) => state.editorConfig.editorMode)
|
||||
const { id } = useParams<RouteParameters>()
|
||||
|
||||
return (
|
||||
<div className={'d-flex flex-column vh-100'}>
|
||||
<TaskBar/>
|
||||
<h1>{id}</h1>
|
||||
<div className={'flex-fill flex-row d-flex'}>
|
||||
{ editorMode === EditorMode.EDITOR || editorMode === EditorMode.BOTH ? <EditorWindow/> : null }
|
||||
{ editorMode === EditorMode.PREVIEW || editorMode === EditorMode.BOTH ? <MarkdownPreview/> : null }
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Editor }
|
11
src/components/editor/markdown-preview/markdown-preview.tsx
Normal file
11
src/components/editor/markdown-preview/markdown-preview.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from 'react'
|
||||
|
||||
const MarkdownPreview: React.FC = () => {
|
||||
return (
|
||||
<div style={{ backgroundColor: 'red' }}>
|
||||
Hello, MarkdownPreview!
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { MarkdownPreview }
|
14
src/components/editor/task-bar/active-indicator.scss
Normal file
14
src/components/editor/task-bar/active-indicator.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
.activeIndicator {
|
||||
$indicator-size: 12px;
|
||||
border-radius: $indicator-size;
|
||||
height: $indicator-size;
|
||||
width: $indicator-size;
|
||||
|
||||
&.active {
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
background-color: #d20000;
|
||||
}
|
||||
}
|
17
src/components/editor/task-bar/active-indicator.tsx
Normal file
17
src/components/editor/task-bar/active-indicator.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import React from 'react'
|
||||
import './active-indicator.scss'
|
||||
|
||||
export enum ActiveIndicatorStatus {
|
||||
ACTIVE ='active',
|
||||
INACTIVE ='inactive'
|
||||
}
|
||||
|
||||
export interface ActiveIndicatorProps {
|
||||
status: ActiveIndicatorStatus;
|
||||
}
|
||||
|
||||
export const ActiveIndicator: React.FC<ActiveIndicatorProps> = ({ status }) => {
|
||||
return (
|
||||
<span className={`activeIndicator ${status}`}/>
|
||||
)
|
||||
}
|
3
src/components/editor/task-bar/connection-indicator.scss
Normal file
3
src/components/editor/task-bar/connection-indicator.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
.upper-case {
|
||||
text-transform: uppercase;
|
||||
}
|
27
src/components/editor/task-bar/connection-indicator.tsx
Normal file
27
src/components/editor/task-bar/connection-indicator.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import React from 'react'
|
||||
import { Dropdown } from 'react-bootstrap'
|
||||
import { ActiveIndicatorStatus } from './active-indicator'
|
||||
import './connection-indicator.scss'
|
||||
import { UserLine } from './user-line'
|
||||
|
||||
const ConnectionIndicator: React.FC = () => {
|
||||
const userOnline = 2
|
||||
return (
|
||||
<Dropdown className="small" alignRight>
|
||||
<Dropdown.Toggle id="connection-indicator" size="sm" variant="primary" className="upper-case">
|
||||
<FontAwesomeIcon icon="users"/> {userOnline} Online
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Item disabled={true} className="d-flex align-items-center p-0">
|
||||
<UserLine name="Philip Molares" photo="https://1.gravatar.com/avatar/767fc9c115a1b989744c755db47feb60?s=200&r=pg&d=mp" color="red" status={ActiveIndicatorStatus.INACTIVE}/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item disabled={true} className="d-flex align-items-center p-0">
|
||||
<UserLine name="Philip Molares" photo="https://1.gravatar.com/avatar/767fc9c115a1b989744c755db47feb60?s=200&r=pg&d=mp" color="blue" status={ActiveIndicatorStatus.ACTIVE}/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
||||
export { ConnectionIndicator }
|
15
src/components/editor/task-bar/dark-mode-button.tsx
Normal file
15
src/components/editor/task-bar/dark-mode-button.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import React from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
|
||||
|
||||
const DarkModeButton: React.FC = () => {
|
||||
return (
|
||||
<ToggleButtonGroup type="checkbox" defaultValue={[]} name="dark-mode" className="ml-2">
|
||||
<ToggleButton value={1} variant="light" className="text-secondary">
|
||||
<FontAwesomeIcon icon="moon"/>
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
)
|
||||
}
|
||||
|
||||
export { DarkModeButton }
|
72
src/components/editor/task-bar/editor-menu.tsx
Normal file
72
src/components/editor/task-bar/editor-menu.tsx
Normal file
|
@ -0,0 +1,72 @@
|
|||
import React from 'react'
|
||||
import { Dropdown } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
|
||||
const EditorMenu: React.FC = () => {
|
||||
useTranslation()
|
||||
return (
|
||||
<Dropdown className="small" alignRight={true}>
|
||||
<Dropdown.Toggle variant="light" size="sm" id="editor-menu" className="text-secondary">
|
||||
<Trans i18nKey="menu"/>
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
<Dropdown.Header>
|
||||
<Trans i18nKey="extra"/>
|
||||
</Dropdown.Header>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon="history"/> <Trans i18nKey="revision"/>
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon="tv"/> <Trans i18nKey="slideMode"/>
|
||||
</Dropdown.Item>
|
||||
|
||||
<Dropdown.Divider/>
|
||||
|
||||
<Dropdown.Header>
|
||||
<Trans i18nKey="export"/>
|
||||
</Dropdown.Header>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon={['fab', 'dropbox']}/> Dropbox
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon={['fab', 'github']}/> Gist
|
||||
</Dropdown.Item>
|
||||
|
||||
<Dropdown.Divider/>
|
||||
|
||||
<Dropdown.Header>
|
||||
<Trans i18nKey="import"/>
|
||||
</Dropdown.Header>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon={['fab', 'dropbox']}/> Dropbox
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon={['fab', 'github']}/> Gist
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon="paste"/> <Trans i18nKey="clipboard"/>
|
||||
</Dropdown.Item>
|
||||
|
||||
<Dropdown.Divider/>
|
||||
|
||||
<Dropdown.Header>
|
||||
<Trans i18nKey="download"/>
|
||||
</Dropdown.Header>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon="file-alt"/> Markdown
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon="file-code"/> HTML
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item className="small">
|
||||
<FontAwesomeIcon icon="file-code"/> Raw HTML
|
||||
</Dropdown.Item>
|
||||
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
)
|
||||
}
|
||||
|
||||
export { EditorMenu }
|
30
src/components/editor/task-bar/editor-view-mode.tsx
Normal file
30
src/components/editor/task-bar/editor-view-mode.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ApplicationState } from '../../../redux'
|
||||
import { EditorMode } from '../../../redux/editor/types'
|
||||
import { setEditorModeConfig } from '../../../redux/editor/methods'
|
||||
|
||||
const EditorViewMode: React.FC = () => {
|
||||
const editorConfig = useSelector((state: ApplicationState) => state.editorConfig)
|
||||
return (
|
||||
<ToggleButtonGroup
|
||||
type="radio"
|
||||
name="options"
|
||||
defaultValue={editorConfig.editorMode}
|
||||
onChange={(value: EditorMode) => { setEditorModeConfig(value) }}>
|
||||
<ToggleButton value={EditorMode.PREVIEW} variant="outline-secondary">
|
||||
<FontAwesomeIcon icon="eye"/>
|
||||
</ToggleButton>
|
||||
<ToggleButton value={EditorMode.BOTH} variant="outline-secondary">
|
||||
<FontAwesomeIcon icon="columns"/>
|
||||
</ToggleButton>
|
||||
<ToggleButton value={EditorMode.EDITOR} variant="outline-secondary">
|
||||
<FontAwesomeIcon icon="pencil-alt"/>
|
||||
</ToggleButton>
|
||||
</ToggleButtonGroup>
|
||||
)
|
||||
}
|
||||
|
||||
export { EditorViewMode }
|
46
src/components/editor/task-bar/task-bar.tsx
Normal file
46
src/components/editor/task-bar/task-bar.tsx
Normal file
|
@ -0,0 +1,46 @@
|
|||
import React from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Button, Nav, Navbar } from 'react-bootstrap'
|
||||
import { DarkModeButton } from './dark-mode-button'
|
||||
import { EditorViewMode } from './editor-view-mode'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { EditorMenu } from './editor-menu'
|
||||
import { ConnectionIndicator } from './connection-indicator'
|
||||
|
||||
const TaskBar: React.FC = () => {
|
||||
useTranslation()
|
||||
return (
|
||||
<Navbar bg={'light'}>
|
||||
<Nav className="mr-auto d-flex align-items-center">
|
||||
<Navbar.Brand>
|
||||
<Link to="/intro" className="text-secondary">
|
||||
<FontAwesomeIcon icon="file-alt"/> CodiMD
|
||||
</Link>
|
||||
</Navbar.Brand>
|
||||
<EditorViewMode/>
|
||||
<DarkModeButton/>
|
||||
<Button className="ml-2 text-secondary" size="sm"
|
||||
variant="outline-light">
|
||||
<FontAwesomeIcon icon="question-circle"/>
|
||||
</Button>
|
||||
</Nav>
|
||||
<Nav className="d-flex align-items-center text-secondary">
|
||||
<Button className="ml-2 text-secondary" size="sm" variant="outline-light">
|
||||
<FontAwesomeIcon icon="plus"/> <Trans i18nKey="new"/>
|
||||
</Button>
|
||||
<Button className="ml-2 text-secondary" size="sm" variant="outline-light">
|
||||
<FontAwesomeIcon icon="share-square"/> <Trans i18nKey="publish"/>
|
||||
</Button>
|
||||
<div className="text-secondary">
|
||||
<EditorMenu/>
|
||||
</div>
|
||||
<div className="mr-2">
|
||||
<ConnectionIndicator/>
|
||||
</div>
|
||||
</Nav>
|
||||
</Navbar>
|
||||
)
|
||||
}
|
||||
|
||||
export { TaskBar }
|
5
src/components/editor/task-bar/user-line.scss
Normal file
5
src/components/editor/task-bar/user-line.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
.user-line-color-indicator {
|
||||
border-left: 3px solid;
|
||||
min-height: 30px;
|
||||
height: 100%;
|
||||
}
|
21
src/components/editor/task-bar/user-line.tsx
Normal file
21
src/components/editor/task-bar/user-line.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React, { Fragment } from 'react'
|
||||
import { UserAvatar } from '../../landing/layout/user-avatar/user-avatar'
|
||||
import { ActiveIndicator, ActiveIndicatorStatus } from './active-indicator'
|
||||
import './user-line.scss'
|
||||
|
||||
export interface UserLineProps {
|
||||
name: string;
|
||||
photo: string;
|
||||
color: string;
|
||||
status: ActiveIndicatorStatus;
|
||||
}
|
||||
|
||||
export const UserLine: React.FC<UserLineProps> = ({ name, photo, color, status }) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className='d-inline-flex align-items-bottom user-line-color-indicator' style={{ borderLeftColor: color }}/>
|
||||
<UserAvatar photo={photo} name={name} additionalClasses={'mx-2'}/>
|
||||
<ActiveIndicator status={status} />
|
||||
</Fragment>
|
||||
)
|
||||
}
|
15
src/components/landing/landing-layout.tsx
Normal file
15
src/components/landing/landing-layout.tsx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import React from 'react'
|
||||
import { Container } from 'react-bootstrap'
|
||||
import { HeaderBar } from './layout/navigation/header-bar/header-bar'
|
||||
import { Footer } from './layout/footer/footer'
|
||||
import './layout/style/index.scss'
|
||||
|
||||
export const LandingLayout: React.FC = ({ children }) => {
|
||||
return (
|
||||
<Container>
|
||||
<HeaderBar/>
|
||||
{children}
|
||||
<Footer/>
|
||||
</Container>
|
||||
)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Redirect, Route, Switch } from 'react-router-dom'
|
||||
import { History } from '../pages/history/history'
|
||||
import { Intro } from '../pages/intro/intro'
|
||||
import { Container } from 'react-bootstrap'
|
||||
import { HeaderBar } from './navigation/header-bar/header-bar'
|
||||
import { Footer } from './footer/footer'
|
||||
import './style/index.scss'
|
||||
import { Login } from '../pages/login/login'
|
||||
|
||||
export const Landing: React.FC = () => {
|
||||
return (<Container>
|
||||
<HeaderBar/>
|
||||
<Switch>
|
||||
<Route path="/history">
|
||||
<History/>
|
||||
</Route>
|
||||
<Route path="/intro">
|
||||
<Intro/>
|
||||
</Route>
|
||||
<Route path="/login">
|
||||
<Login/>
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<Redirect to="/intro"/>
|
||||
</Route>
|
||||
</Switch>
|
||||
<Footer/>
|
||||
</Container>)
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
.user-avatar {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
|
@ -5,22 +5,16 @@ import { useSelector } from 'react-redux'
|
|||
import { ApplicationState } from '../../../../../redux'
|
||||
import { LinkContainer } from 'react-router-bootstrap'
|
||||
import { clearUser } from '../../../../../redux/user/methods'
|
||||
import './user-dropdown.scss'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { UserAvatar } from '../../user-avatar/user-avatar'
|
||||
|
||||
export const UserDropdown: React.FC = () => {
|
||||
const user = useSelector((state: ApplicationState) => state.user)
|
||||
|
||||
return (
|
||||
<Dropdown alignRight>
|
||||
<Dropdown.Toggle size="sm" variant="dark" id="dropdown-basic">
|
||||
<div className='d-inline-flex align-items-baseline'>
|
||||
<img
|
||||
src={user.photo}
|
||||
className="user-avatar"
|
||||
alt={`Avatar of ${user.name}`}
|
||||
/><span>{user.name}</span>
|
||||
</div>
|
||||
<Dropdown.Toggle size="sm" variant="dark" id="dropdown-user" className={'d-flex align-items-center'}>
|
||||
<UserAvatar name={user.name} photo={user.photo}/>
|
||||
</Dropdown.Toggle>
|
||||
|
||||
<Dropdown.Menu>
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
.user-avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
font-size: 1rem;
|
||||
}
|
23
src/components/landing/layout/user-avatar/user-avatar.tsx
Normal file
23
src/components/landing/layout/user-avatar/user-avatar.tsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React from 'react'
|
||||
import './user-avatar.scss'
|
||||
|
||||
export interface UserAvatarProps {
|
||||
name: string;
|
||||
photo: string;
|
||||
additionalClasses?: string;
|
||||
}
|
||||
|
||||
const UserAvatar: React.FC<UserAvatarProps> = ({ name, photo, additionalClasses = '' }) => {
|
||||
return (
|
||||
<span className={'d-inline-flex align-items-center ' + additionalClasses}>
|
||||
<img
|
||||
src={photo}
|
||||
className="user-avatar"
|
||||
alt={`Avatar of ${name}`}
|
||||
/>
|
||||
<span className="ml-1 user-name">{name}</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export { UserAvatar }
|
|
@ -1,22 +1,48 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { BrowserRouter as Router } from 'react-router-dom'
|
||||
import * as serviceWorker from './service-worker'
|
||||
import { Landing } from './components/landing/layout'
|
||||
import { ApplicationLoader } from './components/application-loader/application-loader'
|
||||
import { Provider } from 'react-redux'
|
||||
import { BrowserRouter as Router, Redirect, Route, Switch } from 'react-router-dom'
|
||||
import { ApplicationLoader } from './components/application-loader/application-loader'
|
||||
import { Editor } from './components/editor/editor'
|
||||
import { LandingLayout } from './components/landing/landing-layout'
|
||||
import { History } from './components/landing/pages/history/history'
|
||||
import { Intro } from './components/landing/pages/intro/intro'
|
||||
import { Login } from './components/landing/pages/login/login'
|
||||
import { setUpFontAwesome } from './initializers/fontAwesome'
|
||||
import * as serviceWorker from './service-worker'
|
||||
import { store } from './utils/store'
|
||||
import { setUp } from './initializers'
|
||||
|
||||
const initTasks = setUp()
|
||||
setUpFontAwesome()
|
||||
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<ApplicationLoader initTasks={initTasks}>
|
||||
<Router>
|
||||
<Landing/>
|
||||
</Router>
|
||||
</ApplicationLoader>
|
||||
<Router>
|
||||
<ApplicationLoader>
|
||||
<Switch>
|
||||
<Route path="/history">
|
||||
<LandingLayout>
|
||||
<History/>
|
||||
</LandingLayout>
|
||||
</Route>
|
||||
<Route path="/intro">
|
||||
<LandingLayout>
|
||||
<Intro/>
|
||||
</LandingLayout>
|
||||
</Route>
|
||||
<Route path="/login">
|
||||
<LandingLayout>
|
||||
<Login/>
|
||||
</LandingLayout>
|
||||
</Route>
|
||||
<Route path="/n/:id">
|
||||
<Editor/>
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<Redirect to="/intro"/>
|
||||
</Route>
|
||||
</Switch>
|
||||
</ApplicationLoader>
|
||||
</Router>
|
||||
</Provider>
|
||||
, document.getElementById('root')
|
||||
)
|
||||
|
|
|
@ -3,8 +3,8 @@ import { setFrontendConfig } from '../redux/frontend-config/methods'
|
|||
import { setBackendConfig } from '../redux/backend-config/methods'
|
||||
import { getAndSetUser } from '../utils/apiUtils'
|
||||
|
||||
export const loadAllConfig: () => Promise<void> = async () => {
|
||||
const frontendConfig = await getFrontendConfig()
|
||||
export const loadAllConfig: (baseUrl: string) => Promise<void> = async (baseUrl) => {
|
||||
const frontendConfig = await getFrontendConfig(baseUrl)
|
||||
if (!frontendConfig) {
|
||||
return Promise.reject(new Error('Frontend config empty!'))
|
||||
}
|
||||
|
|
|
@ -1,16 +1,36 @@
|
|||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faTwitter,
|
||||
faMastodon,
|
||||
faGoogle,
|
||||
faFacebook,
|
||||
faDropbox,
|
||||
faDiscourse,
|
||||
faGithub,
|
||||
faGitlab
|
||||
} from '@fortawesome/free-brands-svg-icons'
|
||||
import {
|
||||
faAddressCard,
|
||||
faBolt,
|
||||
faChartBar,
|
||||
faCircle,
|
||||
faClock,
|
||||
faCloudDownloadAlt,
|
||||
faColumns,
|
||||
faComment,
|
||||
faCopy,
|
||||
faDownload,
|
||||
faEye,
|
||||
faFileAlt,
|
||||
faFileCode,
|
||||
faGlobe,
|
||||
faHistory,
|
||||
faMoon,
|
||||
faPaste,
|
||||
faPencilAlt,
|
||||
faPlus,
|
||||
faQuestionCircle,
|
||||
faShareSquare,
|
||||
faSignOutAlt,
|
||||
faSort,
|
||||
faSortDown,
|
||||
|
@ -23,20 +43,12 @@ import {
|
|||
faUpload,
|
||||
faUsers
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faDiscourse,
|
||||
faDropbox,
|
||||
faFacebook,
|
||||
faGithub,
|
||||
faGitlab,
|
||||
faGoogle,
|
||||
faMastodon,
|
||||
faTwitter
|
||||
} from '@fortawesome/free-brands-svg-icons'
|
||||
|
||||
export const setUpFontAwesome: () => void = () => {
|
||||
export const setUpFontAwesome: (() => void) = () => {
|
||||
library.add(faBolt, faPlus, faChartBar, faTv, faFileAlt, faCloudDownloadAlt,
|
||||
faTrash, faSignOutAlt, faComment, faDiscourse, faMastodon, faGlobe,
|
||||
faThumbtack, faClock, faTimes, faGithub, faGitlab, faGoogle, faFacebook,
|
||||
faDropbox, faTwitter, faUsers, faAddressCard, faSort, faDownload, faUpload, faTrash, faSync, faSortUp, faSortDown, faCopy)
|
||||
faDropbox, faTwitter, faUsers, faAddressCard, faEye, faPencilAlt, faColumns,
|
||||
faMoon, faQuestionCircle, faShareSquare, faHistory, faFileCode, faPaste,
|
||||
faCircle, faSort, faDownload, faUpload, faTrash, faSync, faSortUp, faSortDown, faCopy)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { setUpFontAwesome } from './fontAwesome'
|
||||
import { setUpI18n } from './i18n'
|
||||
import { loadAllConfig } from './configLoader'
|
||||
import { setUpI18n } from './i18n'
|
||||
|
||||
const customDelay: () => Promise<void> = async () => {
|
||||
if (window.localStorage.getItem('customDelay')) {
|
||||
|
@ -10,7 +9,6 @@ const customDelay: () => Promise<void> = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
export const setUp: () => Promise<void>[] = () => {
|
||||
setUpFontAwesome()
|
||||
return [setUpI18n(), loadAllConfig(), customDelay()]
|
||||
export const setUp: (baseUrl: string) => Promise<void>[] = (baseUrl) => {
|
||||
return [setUpI18n(), loadAllConfig(baseUrl), customDelay()]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { BackendConfigState, SET_BACKEND_CONFIG_ACTION_TYPE, SetBackendConfigAction } from './types'
|
||||
import { store } from '../../utils/store'
|
||||
|
||||
export const setBackendConfig: (state: BackendConfigState) => void = (state: BackendConfigState) => {
|
||||
export const setBackendConfig = (state: BackendConfigState): void => {
|
||||
const action: SetBackendConfigAction = {
|
||||
type: SET_BACKEND_CONFIG_ACTION_TYPE,
|
||||
payload: {
|
||||
|
|
14
src/redux/editor/methods.ts
Normal file
14
src/redux/editor/methods.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {
|
||||
EditorMode,
|
||||
SET_EDITOR_CONFIG_MODE_ACTION_TYPE,
|
||||
SetEditorConfigAction
|
||||
} from './types'
|
||||
import { store } from '../../utils/store'
|
||||
|
||||
export const setEditorModeConfig = (editorMode: EditorMode): void => {
|
||||
const action: SetEditorConfigAction = {
|
||||
type: SET_EDITOR_CONFIG_MODE_ACTION_TYPE,
|
||||
payload: editorMode
|
||||
}
|
||||
store.dispatch(action)
|
||||
}
|
23
src/redux/editor/reducers.ts
Normal file
23
src/redux/editor/reducers.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { Reducer } from 'redux'
|
||||
import {
|
||||
EditorConfigActions,
|
||||
EditorConfigState,
|
||||
EditorMode,
|
||||
SET_EDITOR_CONFIG_MODE_ACTION_TYPE
|
||||
} from './types'
|
||||
|
||||
export const initialState: EditorConfigState = {
|
||||
editorMode: EditorMode.PREVIEW
|
||||
}
|
||||
|
||||
export const EditorConfigReducer: Reducer<EditorConfigState, EditorConfigActions> = (state: EditorConfigState = initialState, action: EditorConfigActions) => {
|
||||
switch (action.type) {
|
||||
case SET_EDITOR_CONFIG_MODE_ACTION_TYPE:
|
||||
return {
|
||||
...state,
|
||||
editorMode: action.payload
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
20
src/redux/editor/types.ts
Normal file
20
src/redux/editor/types.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { Action } from 'redux'
|
||||
|
||||
export const SET_EDITOR_CONFIG_MODE_ACTION_TYPE = 'editor/mode/set'
|
||||
|
||||
export interface EditorConfigState {
|
||||
editorMode: EditorMode;
|
||||
}
|
||||
|
||||
export enum EditorMode {
|
||||
PREVIEW,
|
||||
BOTH,
|
||||
EDITOR,
|
||||
}
|
||||
|
||||
export interface SetEditorConfigAction extends Action {
|
||||
type: string;
|
||||
payload: EditorMode;
|
||||
}
|
||||
|
||||
export type EditorConfigActions = SetEditorConfigAction;
|
|
@ -1,7 +1,7 @@
|
|||
import { FrontendConfigState, SET_FRONTEND_CONFIG_ACTION_TYPE, SetFrontendConfigAction } from './types'
|
||||
import { store } from '../../utils/store'
|
||||
|
||||
export const setFrontendConfig: (state: FrontendConfigState) => void = (state: FrontendConfigState) => {
|
||||
export const setFrontendConfig = (state: FrontendConfigState): void => {
|
||||
const action: SetFrontendConfigAction = {
|
||||
type: SET_FRONTEND_CONFIG_ACTION_TYPE,
|
||||
payload: {
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
import { combineReducers, Reducer } from 'redux'
|
||||
import { UserState } from './user/types'
|
||||
import { UserReducer } from './user/reducers'
|
||||
import { ModalShowReducer } from './modal/reducers'
|
||||
import { ModalShowState } from './modal/types'
|
||||
import { BackendConfigState } from './backend-config/types'
|
||||
import { FrontendConfigState } from './frontend-config/types'
|
||||
import { BackendConfigReducer } from './backend-config/reducers'
|
||||
import { FrontendConfigReducer } from './frontend-config/reducers'
|
||||
import { EditorConfigState } from './editor/types'
|
||||
import { EditorConfigReducer } from './editor/reducers'
|
||||
|
||||
export interface ApplicationState {
|
||||
user: UserState;
|
||||
modalShow: ModalShowState;
|
||||
backendConfig: BackendConfigState;
|
||||
frontendConfig: FrontendConfigState;
|
||||
editorConfig: EditorConfigState;
|
||||
}
|
||||
|
||||
export const allReducers: Reducer<ApplicationState> = combineReducers<ApplicationState>({
|
||||
user: UserReducer,
|
||||
modalShow: ModalShowReducer,
|
||||
backendConfig: BackendConfigReducer,
|
||||
frontendConfig: FrontendConfigReducer
|
||||
frontendConfig: FrontendConfigReducer,
|
||||
editorConfig: EditorConfigReducer
|
||||
})
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import { ActionCreator } from 'redux'
|
||||
import { SET_HISTORY_DELETE_MODAL_SHOW_ACTION_TYPE, SetHistoryDeleteModalShowAction } from './types'
|
||||
|
||||
export const setSignInModalShow: ActionCreator<SetHistoryDeleteModalShowAction> = (historyDelete: boolean) => ({
|
||||
type: SET_HISTORY_DELETE_MODAL_SHOW_ACTION_TYPE,
|
||||
payload: historyDelete
|
||||
})
|
|
@ -1,18 +0,0 @@
|
|||
import { Reducer } from 'redux'
|
||||
import { ModalShowActions, ModalShowState, SET_HISTORY_DELETE_MODAL_SHOW_ACTION_TYPE } from './types'
|
||||
|
||||
export const initialState: ModalShowState = {
|
||||
historyDelete: false
|
||||
}
|
||||
|
||||
export const ModalShowReducer: Reducer<ModalShowState, ModalShowActions> = (state: ModalShowState = initialState, action: ModalShowActions) => {
|
||||
switch (action.type) {
|
||||
case SET_HISTORY_DELETE_MODAL_SHOW_ACTION_TYPE:
|
||||
return {
|
||||
...state,
|
||||
historyDelete: action.payload
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
import { Action } from 'redux'
|
||||
|
||||
export const SET_HISTORY_DELETE_MODAL_SHOW_ACTION_TYPE = 'modal/history-delete/set'
|
||||
|
||||
export interface ModalShowState {
|
||||
historyDelete: boolean
|
||||
}
|
||||
|
||||
export interface SetHistoryDeleteModalShowAction extends Action {
|
||||
type: string;
|
||||
payload: boolean;
|
||||
}
|
||||
|
||||
export type ModalShowActions = SetHistoryDeleteModalShowAction;
|
|
@ -17,6 +17,6 @@ export const getBackendUrl: (() => string) = () => {
|
|||
return store.getState().frontendConfig.backendUrl
|
||||
}
|
||||
|
||||
export const expectResponseCode: ((response: Response, code?: number) => boolean) = (response, code = 200) => {
|
||||
export const expectResponseCode = (response: Response, code = 200): boolean => {
|
||||
return response.status !== code
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue