diff --git a/public/backend-config.json b/public/backend-config.json index dcb01bf57..61dff0024 100644 --- a/public/backend-config.json +++ b/public/backend-config.json @@ -22,5 +22,10 @@ "privacy": "test", "termsOfUse": "test", "imprint": "test" + }, + "version": { + "version": "mock", + "sourceCodeUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + "issueTrackerUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" } } diff --git a/public/locales/en.json b/public/locales/en.json index a1ab6d697..c133c4a11 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -115,7 +115,7 @@ "deleteUser": "Delete user", "exportUserData": "Export user data", "Help us translating on %s": "Help us translating on %s", - "sourceCode": "Source Code", + "sourceCode": "Read the source code", "Register": "Register", "poweredBy": "Powered by <0>", "Help us translating": "Help us translating", @@ -128,5 +128,12 @@ "username": "Username", "cards": "Cards", "table": "Table", - "errorOpenIdLogin": "Invalid OpenID provided" + "errorOpenIdLogin": "Invalid OpenID provided", + "versionInfo": "Version info", + "successfullyCopied": "Copied!", + "serverVersion": "Server version", + "clientVersion": "Client version", + "youAreUsing": "You are using", + "close": "Close", + "issueTracker": "Found a bug? Fill an issue!" } diff --git a/src/components/landing/layout/footer/powered-by-links.tsx b/src/components/landing/layout/footer/powered-by-links.tsx index 05a414a4d..2f18b6158 100644 --- a/src/components/landing/layout/footer/powered-by-links.tsx +++ b/src/components/landing/layout/footer/powered-by-links.tsx @@ -1,35 +1,36 @@ -import { Trans, useTranslation } from 'react-i18next' -import { TranslatedLink } from './translated-link' import React, { Fragment } from 'react' -import { ExternalLink } from './external-link' +import { Trans, useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import { ApplicationState } from '../../../../redux' +import { ExternalLink } from '../../../links/external-link' +import { TranslatedExternalLink } from '../../../links/translated-external-link' +import { VersionInfo } from '../version-info/version-info' export const PoweredByLinks: React.FC = () => { useTranslation() const defaultLinks = { - releases: '/s/release-notes', - sourceCode: 'https://github.com/codimd/server/tree/41b13e71b6b1d499238c04b15d65e3bd76442f1d' + releases: '/n/release-notes' } const config = useSelector((state: ApplicationState) => state.backendConfig) return (

- ]}/> + + + { Object.entries({ ...defaultLinks, ...(config.specialLinks) }).map(([i18nKey, href]) =>  |  - + ) } +  |  +

) } diff --git a/src/components/landing/layout/footer/social-links.tsx b/src/components/landing/layout/footer/social-links.tsx index e7a4007d7..2db3fff54 100644 --- a/src/components/landing/layout/footer/social-links.tsx +++ b/src/components/landing/layout/footer/social-links.tsx @@ -1,6 +1,6 @@ import React from 'react' import { Trans, useTranslation } from 'react-i18next' -import { ExternalLink } from './external-link' +import { ExternalLink } from '../../../links/external-link' const SocialLink: React.FC = () => { useTranslation() diff --git a/src/components/landing/layout/footer/translated-link.tsx b/src/components/landing/layout/footer/translated-link.tsx deleted file mode 100644 index 288b26ce1..000000000 --- a/src/components/landing/layout/footer/translated-link.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { Trans, useTranslation } from 'react-i18next' - -export interface TranslatedLinkProps { - href: string; - i18nKey: string; -} - -const TranslatedLink: React.FC = ({ href, i18nKey }) => { - useTranslation() - return ( - - - - ) -} - -export { TranslatedLink } diff --git a/src/components/landing/layout/version-info/version-info.tsx b/src/components/landing/layout/version-info/version-info.tsx new file mode 100644 index 000000000..7fcec0978 --- /dev/null +++ b/src/components/landing/layout/version-info/version-info.tsx @@ -0,0 +1,51 @@ +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 '../../../links/translated-external-link' +import { VersionInputField } from './version-input-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.backendConfig.version) + + const column = (title: string, version: string, sourceCodeLink: string, issueTrackerLink: string) => ( + +
{title}
+ + {sourceCodeLink + ? : null} + {issueTrackerLink + ? : null} + + ) + + return ( + + + + +

+ + {column(t('serverVersion'), serverVersion.version, serverVersion.sourceCodeUrl, serverVersion.issueTrackerUrl)} + {column(t('clientVersion'), frontendVersion.version, frontendVersion.sourceCodeUrl, frontendVersion.issueTrackerUrl)} + +
+ + + +
+
+ ) +} diff --git a/src/components/landing/layout/version-info/version-input-field.tsx b/src/components/landing/layout/version-info/version-input-field.tsx new file mode 100644 index 000000000..9b3364e04 --- /dev/null +++ b/src/components/landing/layout/version-info/version-input-field.tsx @@ -0,0 +1,43 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import React, { Fragment, useRef, useState } from 'react' +import { Button, FormControl, InputGroup, Overlay, Tooltip } from 'react-bootstrap' +import { Trans } from 'react-i18next' + +export interface VersionInputFieldProps { + version: string +} + +export const VersionInputField: React.FC = ({ version }) => { + const inputField = useRef(null) + const [showCopiedTooltip, setShowCopiedTooltip] = useState(false) + + const copyToClipboard = (content: string) => { + navigator.clipboard.writeText(content).then(() => { + setShowCopiedTooltip(true) + setTimeout(() => { setShowCopiedTooltip(false) }, 2000) + }).catch(() => { + console.error("couldn't copy") + }) + } + + return ( + + + {(props) => ( + + + + )} + + + + + + + + + + ) +} diff --git a/src/components/landing/layout/footer/external-link.tsx b/src/components/links/external-link.tsx similarity index 78% rename from src/components/landing/layout/footer/external-link.tsx rename to src/components/links/external-link.tsx index aefe62dc3..16bb0b730 100644 --- a/src/components/landing/layout/footer/external-link.tsx +++ b/src/components/links/external-link.tsx @@ -1,19 +1,20 @@ import React, { Fragment } from 'react' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { IconProp } from '../../../../utils/iconProp' +import { IconProp } from '../../utils/iconProp' export interface ExternalLinkProp { href: string; text: string; icon?: IconProp; + className?: string } -export const ExternalLink: React.FC = ({ href, text, icon }) => { +export const ExternalLink: React.FC = ({ href, text, icon, className = 'text-light' }) => { return ( + className={className}> { icon ? diff --git a/src/components/links/translated-external-link.tsx b/src/components/links/translated-external-link.tsx new file mode 100644 index 000000000..efd39d697 --- /dev/null +++ b/src/components/links/translated-external-link.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import { useTranslation } from 'react-i18next' +import { IconProp } from '../../utils/iconProp' +import { ExternalLink } from './external-link' + +export interface TranslatedLinkProps { + i18nKey: string; + href: string; + icon?: IconProp; + className?: string +} + +const TranslatedExternalLink: React.FC = ({ i18nKey, ...props }) => { + const { t } = useTranslation() + return ( + + ) +} + +export { TranslatedExternalLink } diff --git a/src/initializers/fontAwesome.ts b/src/initializers/fontAwesome.ts index 7f6dcfe06..57bf7b279 100644 --- a/src/initializers/fontAwesome.ts +++ b/src/initializers/fontAwesome.ts @@ -6,6 +6,7 @@ import { faClock, faCloudDownloadAlt, faComment, + faCopy, faDownload, faFileAlt, faGlobe, @@ -37,5 +38,5 @@ 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) + faDropbox, faTwitter, faUsers, faAddressCard, faSort, faDownload, faUpload, faTrash, faSync, faSortUp, faSortDown, faCopy) } diff --git a/src/redux/backend-config/reducers.ts b/src/redux/backend-config/reducers.ts index 463b35487..c54a83524 100644 --- a/src/redux/backend-config/reducers.ts +++ b/src/redux/backend-config/reducers.ts @@ -25,6 +25,11 @@ export const initialState: BackendConfigState = { privacy: '', termsOfUse: '', imprint: '' + }, + version: { + version: '', + sourceCodeUrl: '', + issueTrackerUrl: '' } } diff --git a/src/redux/backend-config/types.ts b/src/redux/backend-config/types.ts index 04bd92cbd..10842f91e 100644 --- a/src/redux/backend-config/types.ts +++ b/src/redux/backend-config/types.ts @@ -3,43 +3,50 @@ import { Action } from 'redux' export const SET_BACKEND_CONFIG_ACTION_TYPE = 'backend-config/set' export interface BackendConfigState { - allowAnonymous: boolean, - authProviders: AuthProvidersState, - customAuthNames: CustomAuthNames, - specialLinks: SpecialLinks, + allowAnonymous: boolean, + authProviders: AuthProvidersState, + customAuthNames: CustomAuthNames, + specialLinks: SpecialLinks, + version: BackendVersion, +} + +export interface BackendVersion { + version: string, + sourceCodeUrl: string + issueTrackerUrl: string } export interface AuthProvidersState { - facebook: boolean, - github: boolean, - twitter: boolean, - gitlab: boolean, - dropbox: boolean, - ldap: boolean, - google: boolean, - saml: boolean, - oauth2: boolean, - email: boolean, - openid: boolean, + facebook: boolean, + github: boolean, + twitter: boolean, + gitlab: boolean, + dropbox: boolean, + ldap: boolean, + google: boolean, + saml: boolean, + oauth2: boolean, + email: boolean, + openid: boolean, } export interface CustomAuthNames { - ldap: string; - oauth2: string; - saml: string; + ldap: string; + oauth2: string; + saml: string; } export interface SpecialLinks { - privacy: string, - termsOfUse: string, - imprint: string, + privacy: string, + termsOfUse: string, + imprint: string, } export interface SetBackendConfigAction extends Action { - type: string; - payload: { - state: BackendConfigState; - }; + type: string; + payload: { + state: BackendConfigState; + }; } export type BackendConfigActions = SetBackendConfigAction; diff --git a/src/version.json b/src/version.json new file mode 100644 index 000000000..557c4184a --- /dev/null +++ b/src/version.json @@ -0,0 +1,5 @@ +{ + "version": "0.0", + "sourceCodeUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + "issueTrackerUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" +}