diff --git a/.vscode/settings.json b/.vscode/settings.json index 96e076fd..85423009 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "cSpell.words": [ + "Asyncify", "dugite", "fullscreenable", "maximizable", diff --git a/package-lock.json b/package-lock.json index 14c540da..d492c96a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14892,6 +14892,12 @@ "prop-types": "^15.7.2" } }, + "react-async": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/react-async/-/react-async-10.0.1.tgz", + "integrity": "sha512-ORUz5ca0B57QgBIzEZM5SuhJ6xFjkvEEs0gylLNlWf06vuVcLZsjIw3wx58jJkZG38p+0nUAxRgFW2b7mnVZzA==", + "dev": true + }, "react-dom": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz", diff --git a/package.json b/package.json index 1849a274..013a0a5f 100755 --- a/package.json +++ b/package.json @@ -181,6 +181,7 @@ "prop-types": "15.7.2", "react": "17.0.1", "react-ace": "9.2.1", + "react-async": "^10.0.1", "react-dom": "17.0.1", "react-i18next": "^11.8.5", "react-redux": "7.2.2", diff --git a/src/components/dialog-about/index.tsx b/src/components/dialog-about/index.tsx deleted file mode 100644 index b4595f75..00000000 --- a/src/components/dialog-about/index.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React from 'react'; -import Button from '@material-ui/core/Button'; -import Typography from '@material-ui/core/Typography'; -import DialogContent from '@material-ui/core/DialogContent'; -import connectComponent from '../../helpers/connect-component'; - -const styles = (theme: any) => ({ - icon: { - height: 96, - width: 96, - }, - dialogContent: { - minWidth: 320, - textAlign: 'center', - }, - title: { - marginTop: theme.spacing(1), - }, - version: { - marginBottom: theme.spacing(2), - }, - versionSmallContainer: { - marginTop: theme.spacing(2), - marginBottom: theme.spacing(2), - }, - versionSmall: { - fontSize: '0.8rem', - }, - goToTheWebsiteButton: { - marginRight: theme.spacing(1), - }, - madeBy: { - marginTop: theme.spacing(2), - }, - link: { - fontWeight: 600, - cursor: 'pointer', - '&:hover': { - textDecoration: 'underline', - }, - }, -}); -interface AboutProps { - classes: any; -} -const About = (props: AboutProps) => { - const { classes } = props; - const versions = [ - { name: 'Electron Version', version: window.remote.getEnvironmentVersions().electron }, - { name: 'Node Version', version: window.remote.getEnvironmentVersions().node }, - { name: 'Chromium Version', version: window.remote.getEnvironmentVersions().chrome }, - ]; - return ( -
- - TiddlyGit - - TiddlyGit - - - {`Version v${window.remote.getAppVersion()}.`} - -
- {versions.map(({ name, version }) => ( - - {name}: {version} - - ))} -
- - -
- - - - Made with - - ❤ - - by - await window.service.native.open('https://onetwo.ren/wiki/')} - onKeyDown={async (event) => { - if (event.key !== 'Enter') { - return; - } - await window.service.native.open('https://onetwo.ren/wiki/'); - }} - role="link" - tabIndex={0} - className={classes.link}> - Lin Onetwo - - and - await window.service.native.open('https://webcatalog.app/?utm_source=tiddlygit_app')} - onKeyDown={async (event) => { - if (event.key !== 'Enter') { - return; - } - await window.service.native.open('https://webcatalog.app/?utm_source=tiddlygit_app'); - }} - role="link" - tabIndex={0} - className={classes.link}> - WebCatalog - - -
-
- ); -}; -export default connectComponent(About, null, null, styles); diff --git a/src/helpers/use-service-value.ts b/src/helpers/use-service-value.ts new file mode 100644 index 00000000..08c094d8 --- /dev/null +++ b/src/helpers/use-service-value.ts @@ -0,0 +1,18 @@ +import { useEffect, useState } from 'react'; + +/** + * Use value from service, especially constant value that never changes + * This will only update once, won't listen on later update. + * @param valuePromise A promise contain the value we want to use in React + * @param defaultValue empty array or undefined, as initial value + */ +export function usePromiseValue(valuePromise: Promise | (() => Promise), defaultValue?: T): T | undefined { + const [value, valueSetter] = useState(defaultValue); + useEffect(() => { + void (async () => { + valueSetter(typeof valuePromise === 'function' ? await valuePromise() : await valuePromise); + }); + }, []); + + return value; +} diff --git a/src/pages/About.tsx b/src/pages/About.tsx new file mode 100644 index 00000000..ec290740 --- /dev/null +++ b/src/pages/About.tsx @@ -0,0 +1,119 @@ +import React, { useState, useEffect } from 'react'; +import styled from 'styled-components'; +import { Button, DialogContent } from '@material-ui/core'; +import { usePromiseValue } from '@/helpers/use-service-value'; + +const Icon = styled.img` + height: 96; + width: 96; +`; + +const DialogContentSC = styled(DialogContent)` + min-width: 320; + text-align: 'center'; +`; + +const Title = styled.h6` + margin-top: 10px; +`; + +const Version = styled.p` + margin-bottom: 20px; +`; + +const VersionSmallContainer = styled.div` + margin-top: 20px; + margin-bottom: 20px; +`; + +const VersionSmall = styled.span` + font-size: 0.8rem; +`; + +const GoToTheWebsiteButton = styled(Button)` + margin-right: 10px; +`; + +const MadeBy = styled.div` + margin-top: 20px; +`; + +const Link = styled.span` + font-weight: 600; + cursor: pointer; + &:hover { + text-decoration: underline; + } +`; + +export default function About(): JSX.Element { + const versions = usePromiseValue(async () => { + const processVersions = (await window.service.context.get('environmentVersions')) as NodeJS.ProcessVersions; + return [ + { name: 'Electron Version', version: processVersions.electron }, + { name: 'Node Version', version: processVersions.node }, + { name: 'Chromium Version', version: processVersions.chrome }, + ]; + }, []); + + const iconPath = usePromiseValue(window.service.context.get('ICON_PATH') as Promise); + const appVersion = usePromiseValue(window.service.context.get('appVersion') as Promise); + + return ( +
+ + + TiddlyGit + {`Version v${appVersion ?? ' - '}.`} + + {versions?.map(({ name, version }) => ( + + {name}: {version} + + ))} + + + await window.service.native.open('https://github.com/tiddly-gittly/TiddlyGit-Desktop')}> + Website + +
+ await window.service.native.open('https://github.com/tiddly-gittly/TiddlyGit-Desktop/issues/new/choose')}> + Support + + + + Made with + + ❤ + + by + await window.service.native.open('https://onetwo.ren/wiki/')} + onKeyDown={async (event) => { + if (event.key !== 'Enter') { + return; + } + await window.service.native.open('https://onetwo.ren/wiki/'); + }} + role="link" + tabIndex={0}> + Lin Onetwo + + and + await window.service.native.open('https://webcatalog.app/?utm_source=tiddlygit_app')} + onKeyDown={async (event) => { + if (event.key !== 'Enter') { + return; + } + await window.service.native.open('https://webcatalog.app/?utm_source=tiddlygit_app'); + }} + role="link" + tabIndex={0}> + WebCatalog + + +
+
+ ); +} diff --git a/src/preload/common/require-nodejs.ts b/src/preload/common/require-nodejs.ts index 8f31c9e2..4d39c9d5 100644 --- a/src/preload/common/require-nodejs.ts +++ b/src/preload/common/require-nodejs.ts @@ -12,11 +12,6 @@ import { Channels } from '@/constants/channels'; // ipcRenderer.once(channel, listener); // }, // }, -// getPlatform: () => remote.process.platform, -// getAppVersion: () => remote.app.getVersion(), -// getAppName: () => remote.app.name, -// getOSVersion: () => remote.require('os').release(), -// getEnvironmentVersions: () => process.versions, // getGlobal: (key: any) => remote.getGlobal(key), // useCurrentWindow: (callback: any) => callback(remote.getCurrentWindow()), // getCurrentWindowID: () => remote.getCurrentWindow().id, diff --git a/src/renderer.tsx b/src/renderer.tsx index 3ac473f9..d7e834ea 100644 --- a/src/renderer.tsx +++ b/src/renderer.tsx @@ -22,7 +22,7 @@ import { initI18N } from './i18n'; import AppWrapper from './components/app-wrapper'; -const DialogAbout = React.lazy(async () => await import('./components/dialog-about')); +const AboutPage = React.lazy(async () => await import('./pages/About')); const DialogAddWorkspace = React.lazy(async () => await import('./components/dialog-add-workspace')); const DialogAuth = React.lazy(async () => await import('./components/dialog-auth')); const DialogCodeInjection = React.lazy(async () => await import('./components/dialog-code-injection')); @@ -41,7 +41,7 @@ const App = (): JSX.Element => { switch (window.meta.windowName) { case WindowNames.about: document.title = 'About'; - return ; + return ; case WindowNames.addWorkspace: document.title = 'Add Workspace'; return ; diff --git a/src/services/constants/index.ts b/src/services/constants/index.ts index a6598e17..8fbc026d 100644 --- a/src/services/constants/index.ts +++ b/src/services/constants/index.ts @@ -1,5 +1,8 @@ -import { injectable } from 'inversify'; +import { app } from 'electron'; +import process from 'process'; +import os from 'os'; import isDevelopment from 'electron-is-dev'; +import { injectable } from 'inversify'; import { IContextService, IContext, IPaths, IConstants } from './interface'; import * as paths from '@services/constants/paths'; @@ -9,6 +12,11 @@ export class ContextService implements IContextService { pathConstants: IPaths = paths; constants: IConstants = { isDevelopment, + platform: process.platform, + appVersion: app.getVersion(), + appName: app.name, + oSVersion: os.release(), + environmentVersions: process.versions, }; public get(key: K): IContext[K] { diff --git a/src/services/constants/interface.ts b/src/services/constants/interface.ts index 8f1dd532..61e33666 100644 --- a/src/services/constants/interface.ts +++ b/src/services/constants/interface.ts @@ -16,9 +16,14 @@ export interface IPaths { */ export interface IConstants { isDevelopment: boolean; + platform: string; + appVersion: string; + appName: string; + oSVersion: string; + environmentVersions: NodeJS.ProcessVersions; } -export type IContext = IPaths | IConstants; +export interface IContext extends IPaths, IConstants {} /** * Manage constant value like `isDevelopment` and many else, so you can know about about running environment in main and renderer process easily.