mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-29 22:35:50 -04:00
Restructure repository (#426)
organized repository Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> Co-authored-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de> Co-authored-by: Philip Molares <git@molar.es>
This commit is contained in:
parent
66258ca615
commit
0fadc09f2b
254 changed files with 384 additions and 403 deletions
14
src/components/landing-layout/footer/footer.tsx
Normal file
14
src/components/landing-layout/footer/footer.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
import React from 'react'
|
||||
import { LanguagePicker } from './language-picker'
|
||||
import { PoweredByLinks } from './powered-by-links'
|
||||
import { SocialLink } from './social-links'
|
||||
|
||||
export const Footer: React.FC = () => {
|
||||
return (
|
||||
<footer className="text-white-50 small">
|
||||
<LanguagePicker/>
|
||||
<PoweredByLinks/>
|
||||
<SocialLink/>
|
||||
</footer>
|
||||
)
|
||||
}
|
71
src/components/landing-layout/footer/language-picker.tsx
Normal file
71
src/components/landing-layout/footer/language-picker.tsx
Normal file
|
@ -0,0 +1,71 @@
|
|||
import moment from 'moment'
|
||||
import React, { useCallback } from 'react'
|
||||
import { Form } from 'react-bootstrap'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const languages = {
|
||||
en: 'English',
|
||||
'zh-CN': '简体中文',
|
||||
'zh-TW': '繁體中文',
|
||||
fr: 'Français',
|
||||
de: 'Deutsch',
|
||||
ja: '日本語',
|
||||
es: 'Español',
|
||||
ca: 'Català',
|
||||
el: 'Ελληνικά',
|
||||
pt: 'Português',
|
||||
it: 'Italiano',
|
||||
tr: 'Türkçe',
|
||||
ru: 'Русский',
|
||||
nl: 'Nederlands',
|
||||
hr: 'Hrvatski',
|
||||
pl: 'Polski',
|
||||
uk: 'Українська',
|
||||
hi: 'हिन्दी',
|
||||
sv: 'Svenska',
|
||||
eo: 'Esperanto',
|
||||
da: 'Dansk',
|
||||
ko: '한국어',
|
||||
id: 'Bahasa Indonesia',
|
||||
sr: 'Cрпски',
|
||||
vi: 'Tiếng Việt',
|
||||
ar: 'العربية',
|
||||
cs: 'Česky',
|
||||
sk: 'Slovensky'
|
||||
}
|
||||
|
||||
const findLanguageCode = (wantedLanguage: string): string => {
|
||||
let foundLanguage = Object.keys(languages).find((supportedLanguage) => wantedLanguage === supportedLanguage)
|
||||
if (!foundLanguage) {
|
||||
foundLanguage = Object.keys(languages).find((supportedLanguage) => wantedLanguage.substr(0, 2) === supportedLanguage)
|
||||
}
|
||||
return foundLanguage || ''
|
||||
}
|
||||
|
||||
const LanguagePicker: React.FC = () => {
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const onChangeLang = useCallback(() => async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const language = event.currentTarget.value
|
||||
moment.locale(language)
|
||||
await i18n.changeLanguage(language)
|
||||
}, [i18n])
|
||||
|
||||
return (
|
||||
<Form.Control
|
||||
as="select"
|
||||
size="sm"
|
||||
className="mb-2 mx-auto w-auto"
|
||||
value={findLanguageCode(i18n.language)}
|
||||
onChange={onChangeLang()}
|
||||
>
|
||||
{
|
||||
Object.entries(languages).map(([language, languageName]) => {
|
||||
return <option key={language} value={language}>{languageName}</option>
|
||||
})
|
||||
}
|
||||
</Form.Control>
|
||||
)
|
||||
}
|
||||
|
||||
export { LanguagePicker }
|
34
src/components/landing-layout/footer/powered-by-links.tsx
Normal file
34
src/components/landing-layout/footer/powered-by-links.tsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
import React, { Fragment } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ApplicationState } from '../../../redux'
|
||||
import { ExternalLink } from '../../common/links/external-link'
|
||||
import { TranslatedExternalLink } from '../../common/links/translated-external-link'
|
||||
import { TranslatedInternalLink } from '../../common/links/translated-internal-link'
|
||||
import { VersionInfo } from './version-info'
|
||||
|
||||
export const PoweredByLinks: React.FC = () => {
|
||||
useTranslation()
|
||||
|
||||
const config = useSelector((state: ApplicationState) => state.config)
|
||||
|
||||
return (
|
||||
<p>
|
||||
<Trans i18nKey="landing.footer.poweredBy">
|
||||
<ExternalLink href="https://codimd.org" text="CodiMD"/>
|
||||
</Trans>
|
||||
|
|
||||
<TranslatedInternalLink href='/n/release-notes' i18nKey='landing.footer.releases'/>
|
||||
{
|
||||
Object.entries({ ...config.specialLinks }).map(([i18nKey, href]) =>
|
||||
<Fragment key={i18nKey}>
|
||||
|
|
||||
<TranslatedExternalLink href={href} i18nKey={'landing.footer.' + i18nKey}/>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
|
|
||||
<VersionInfo/>
|
||||
</p>
|
||||
)
|
||||
}
|
20
src/components/landing-layout/footer/social-links.tsx
Normal file
20
src/components/landing-layout/footer/social-links.tsx
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { ExternalLink } from '../../common/links/external-link'
|
||||
|
||||
const SocialLink: React.FC = () => {
|
||||
useTranslation()
|
||||
return (
|
||||
<p>
|
||||
<Trans i18nKey="landing.footer.followUs" components={[
|
||||
<ExternalLink href="https://github.com/codimd/server" icon='github' text="GitHub"/>,
|
||||
<ExternalLink href="https://community.codimd.org" icon='users' text="Discourse"/>,
|
||||
<ExternalLink href="https://riot.im/app/#/room/#codimd:matrix.org" icon="comment" text="Riot"/>,
|
||||
<ExternalLink href="https://social.codimd.org/mastodon" icon='mastodon' text="Mastodon"/>,
|
||||
<ExternalLink href="https://translate.codimd.org" icon="globe" text="POEditor"/>
|
||||
]}/>
|
||||
</p>
|
||||
)
|
||||
}
|
||||
|
||||
export { SocialLink }
|
54
src/components/landing-layout/footer/version-info.tsx
Normal file
54
src/components/landing-layout/footer/version-info.tsx
Normal file
|
@ -0,0 +1,54 @@
|
|||
import React, { Fragment, useState } from 'react'
|
||||
import { Button, Col, Modal, Row } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ApplicationState } from '../../../redux'
|
||||
import frontendVersion from '../../../version.json'
|
||||
import { TranslatedExternalLink } from '../../common/links/translated-external-link'
|
||||
import { ShowIf } from '../../common/show-if/show-if'
|
||||
import { CopyableField } from '../../common/copyable-field/copyable-field'
|
||||
|
||||
export const VersionInfo: React.FC = () => {
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
const handleClose = () => setShow(false)
|
||||
const handleShow = () => setShow(true)
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
const serverVersion = useSelector((state: ApplicationState) => state.config.version)
|
||||
|
||||
const column = (title: string, version: string, sourceCodeLink: string, issueTrackerLink: string) => (
|
||||
<Col md={6} className={'flex-column'}>
|
||||
<h5>{title}</h5>
|
||||
<CopyableField content={version}/>
|
||||
<ShowIf condition={!!sourceCodeLink}>
|
||||
<TranslatedExternalLink i18nKey={'landing.versionInfo.sourceCode'} className={'btn btn-sm btn-primary d-block mb-2'} href={sourceCodeLink}/>
|
||||
</ShowIf>
|
||||
<ShowIf condition={!!issueTrackerLink}>
|
||||
<TranslatedExternalLink i18nKey={'landing.versionInfo.issueTracker'} className={'btn btn-sm btn-primary d-block mb-2'} href={issueTrackerLink}/>
|
||||
</ShowIf>
|
||||
</Col>
|
||||
)
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Link id='version' to={'#'} className={'text-light'} onClick={handleShow}><Trans i18nKey={'landing.versionInfo.versionInfo'}/></Link>
|
||||
<Modal id='versionModal' show={show} onHide={handleClose} animation={true}>
|
||||
<Modal.Body className="text-dark">
|
||||
<h3><Trans i18nKey={'landing.versionInfo.title'}/></h3>
|
||||
<Row>
|
||||
{column(t('landing.versionInfo.serverVersion'), serverVersion.version, serverVersion.sourceCodeUrl, serverVersion.issueTrackerUrl)}
|
||||
{column(t('landing.versionInfo.clientVersion'), frontendVersion.version, frontendVersion.sourceCodeUrl, frontendVersion.issueTrackerUrl)}
|
||||
</Row>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button variant="secondary" onClick={handleClose}>
|
||||
<Trans i18nKey={'common.close'}/>
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</Modal>
|
||||
</Fragment>
|
||||
)
|
||||
}
|
22
src/components/landing-layout/landing-layout.tsx
Normal file
22
src/components/landing-layout/landing-layout.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import { Container } from 'react-bootstrap'
|
||||
import { DocumentTitle } from '../common/document-title/document-title'
|
||||
import { Footer } from './footer/footer'
|
||||
import { MotdBanner } from '../common/motd-banner/motd-banner'
|
||||
import { HeaderBar } from './navigation/header-bar/header-bar'
|
||||
|
||||
export const LandingLayout: React.FC = ({ children }) => {
|
||||
return (
|
||||
<Container className="text-white d-flex flex-column mvh-100">
|
||||
<DocumentTitle/>
|
||||
<MotdBanner/>
|
||||
<HeaderBar/>
|
||||
<div className={'d-flex flex-column justify-content-between flex-fill text-center'}>
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
<Footer/>
|
||||
</div>
|
||||
</Container>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
.header-nav {
|
||||
.nav-link {
|
||||
border-bottom: 2px solid transparent
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
border-bottom-color: #fff;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import React, { Fragment } from 'react'
|
||||
import { Navbar } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { ApplicationState } from '../../../../redux'
|
||||
import { HeaderNavLink } from '../header-nav-link'
|
||||
import { NewGuestNoteButton } from '../new-guest-note-button'
|
||||
import { NewUserNoteButton } from '../new-user-note-button'
|
||||
import { SignInButton } from '../sign-in-button'
|
||||
import { UserDropdown } from '../user-dropdown'
|
||||
import './header-bar.scss'
|
||||
|
||||
const HeaderBar: React.FC = () => {
|
||||
useTranslation()
|
||||
const user = useSelector((state: ApplicationState) => state.user)
|
||||
|
||||
return (
|
||||
<Navbar className="justify-content-between">
|
||||
<div className="nav header-nav">
|
||||
<HeaderNavLink to="/intro" id='navLinkIntro'>
|
||||
<Trans i18nKey="landing.navigation.intro"/>
|
||||
</HeaderNavLink>
|
||||
<HeaderNavLink to="/history" id='navLinkHistory'>
|
||||
<Trans i18nKey="landing.navigation.history"/>
|
||||
</HeaderNavLink>
|
||||
</div>
|
||||
<div className="d-inline-flex">
|
||||
{!user
|
||||
? <Fragment>
|
||||
<span className={'mx-1 d-flex'}>
|
||||
<NewGuestNoteButton/>
|
||||
</span>
|
||||
<SignInButton size="sm"/>
|
||||
</Fragment>
|
||||
: <Fragment>
|
||||
<span className={'mx-1 d-flex'}>
|
||||
<NewUserNoteButton/>
|
||||
</span>
|
||||
<UserDropdown/>
|
||||
</Fragment>
|
||||
}
|
||||
</div>
|
||||
</Navbar>
|
||||
)
|
||||
}
|
||||
|
||||
export { HeaderBar }
|
18
src/components/landing-layout/navigation/header-nav-link.tsx
Normal file
18
src/components/landing-layout/navigation/header-nav-link.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import React from 'react'
|
||||
import { Nav } from 'react-bootstrap'
|
||||
import { LinkContainer } from 'react-router-bootstrap'
|
||||
|
||||
export interface HeaderNavLinkProps {
|
||||
to: string
|
||||
id: string
|
||||
}
|
||||
|
||||
export const HeaderNavLink: React.FC<HeaderNavLinkProps> = ({ to, id, children }) => {
|
||||
return (
|
||||
<Nav.Item>
|
||||
<LinkContainer to={to}>
|
||||
<Nav.Link id={id} className="text-light" href={to}>{children}</Nav.Link>
|
||||
</LinkContainer>
|
||||
</Nav.Item>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import React from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { LinkContainer } from 'react-router-bootstrap'
|
||||
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
||||
|
||||
export const NewGuestNoteButton: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<LinkContainer to={'/new'} title={t('landing.navigation.newGuestNote')}>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
className="d-inline-flex align-items-center">
|
||||
<ForkAwesomeIcon icon="plus" className="mx-1"/>
|
||||
<span>
|
||||
<Trans i18nKey='landing.navigation.newGuestNote'/>
|
||||
</span>
|
||||
</Button>
|
||||
</LinkContainer>)
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import React from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { LinkContainer } from 'react-router-bootstrap'
|
||||
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
||||
|
||||
export const NewUserNoteButton: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<LinkContainer to={'/new'} title={t('landing.navigation.newNote')}>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="sm"
|
||||
className="d-inline-flex align-items-center">
|
||||
<ForkAwesomeIcon icon="plus" className="mx-1"/>
|
||||
<span>
|
||||
<Trans i18nKey='landing.navigation.newNote'/>
|
||||
</span>
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
)
|
||||
}
|
29
src/components/landing-layout/navigation/sign-in-button.tsx
Normal file
29
src/components/landing-layout/navigation/sign-in-button.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import React from 'react'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { ButtonProps } from 'react-bootstrap/Button'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { LinkContainer } from 'react-router-bootstrap'
|
||||
import { ApplicationState } from '../../../redux'
|
||||
import { ShowIf } from '../../common/show-if/show-if'
|
||||
|
||||
type SignInButtonProps = {
|
||||
className?: string
|
||||
} & Omit<ButtonProps, 'href'>
|
||||
|
||||
export const SignInButton: React.FC<SignInButtonProps> = ({ variant, ...props }) => {
|
||||
const { t } = useTranslation()
|
||||
const authProviders = useSelector((state: ApplicationState) => state.config.authProviders)
|
||||
return (
|
||||
<ShowIf condition={Object.values(authProviders).includes(true)}>
|
||||
<LinkContainer to="/login" title={t('login.signIn')}>
|
||||
<Button
|
||||
variant={variant || 'success'}
|
||||
{...props}
|
||||
>
|
||||
<Trans i18nKey="login.signIn"/>
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</ShowIf>
|
||||
)
|
||||
}
|
47
src/components/landing-layout/navigation/user-dropdown.tsx
Normal file
47
src/components/landing-layout/navigation/user-dropdown.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
import React from 'react'
|
||||
import { Dropdown } from 'react-bootstrap'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { LinkContainer } from 'react-router-bootstrap'
|
||||
import { ApplicationState } from '../../../redux'
|
||||
import { clearUser } from '../../../redux/user/methods'
|
||||
import { ForkAwesomeIcon } from '../../common/fork-awesome/fork-awesome-icon'
|
||||
import { UserAvatar } from '../../common/user-avatar/user-avatar'
|
||||
|
||||
export const UserDropdown: React.FC = () => {
|
||||
useTranslation()
|
||||
const user = useSelector((state: ApplicationState) => state.user)
|
||||
|
||||
if (!user) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown alignRight>
|
||||
<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 className='text-start'>
|
||||
<LinkContainer to={'/n/features'}>
|
||||
<Dropdown.Item dir='auto'>
|
||||
<ForkAwesomeIcon icon="bolt" fixedWidth={true} className="mx-2"/>
|
||||
<Trans i18nKey="editor.help.documents.features"/>
|
||||
</Dropdown.Item>
|
||||
</LinkContainer>
|
||||
<LinkContainer to={'/profile'}>
|
||||
<Dropdown.Item dir='auto'>
|
||||
<ForkAwesomeIcon icon="user" fixedWidth={true} className="mx-2"/>
|
||||
<Trans i18nKey="profile.userProfile"/>
|
||||
</Dropdown.Item>
|
||||
</LinkContainer>
|
||||
<Dropdown.Item dir='auto'
|
||||
onClick={() => {
|
||||
clearUser()
|
||||
}}>
|
||||
<ForkAwesomeIcon icon="sign-out" fixedWidth={true} className="mx-2"/>
|
||||
<Trans i18nKey="login.signOut"/>
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue