Merge branch 'refactor/typescript' of https://github.com/tiddly-gittly/TiddlyGit-Desktop into refactor/typescript

This commit is contained in:
林一二 2021-03-30 11:27:09 +08:00
commit 4747158227
8 changed files with 175 additions and 132 deletions

View file

@ -100,8 +100,10 @@
"Hibernate": "Save CPU usage, memory and battery.",
"SelectLocal": "Select Local Image...",
"ResetDefaultIcon": "Reset Default Icon",
"NoRevert": "Caution! This operation can't be reverted.",
"URL": "Wiki URL",
"Port": "Local host server port"
"Port": "Local host server port",
"PathDescription": "Location of your local wiki folder."
},
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "Cannot find the workspace folder that was still there before! \nThe folders that should have existed here may have been removed! \nDo you want to remove the workspace?",
@ -114,7 +116,8 @@
"StorageServiceUserInfoNoFoundDetail": "Seems you haven't login to Your storage service, so we disable syncing for this wiki.",
"RestartMessage": "You need to restart the app for this change to take affect.",
"Later": "Later",
"RestartNow": "Restart Now"
"RestartNow": "Restart Now",
"MadeWithLove":"<0>Made with </0><1>❤</1><2> by </2>"
},
"Log": {
"CantSyncGitNotInitialized": "Unable to sync, this folder is not initialized as a Git repository",
@ -232,5 +235,6 @@
},
"Loading": "Loading",
"Yes": "Yes",
"No": "No"
"No": "No",
"LinOnetwo": "Lin Onetwo"
}

View file

@ -100,8 +100,10 @@
"DisableNotification": "阻止工作区的消息提醒",
"SelectLocal": "选择本地图片...",
"ResetDefaultIcon": "还原默认图标",
"NoRevert": "注意!这个操作无法撤销!",
"URL": "本地服务器地址",
"Port": "本地服务器端口"
"Port": "本地服务器端口",
"PathDescription": "本地维基文件夹的地址"
},
"Log": {
"StartGitInitialization": "开始初始化本地Git仓库",
@ -148,7 +150,8 @@
"StorageServiceUserInfoNoFoundDetail": "似乎你尚未登录存储备份服务,因此此 Wiki 的同步暂时禁用,直到你登录以提供有可用于同步的登录信息。",
"Later": "稍后",
"RestartMessage": "您需要重新启动本程序才能使此更改生效。",
"RestartNow": "现在重启"
"RestartNow": "现在重启",
"MadeWithLove":"<0>有</0><1> ❤ </1><2>的开发者:</2>"
},
"Cancel": "取消",
"Preference": {
@ -222,7 +225,7 @@
"SpellCheck": "拼写检查",
"SpellCheckLanguages": "首选拼写检查语言",
"Support": "支持",
"TiddlyWiki": "微分维基TiddlyWiki",
"TiddlyWiki": "滴粒维基TiddlyWiki",
"System": "系统",
"TranslatiumIntro": "像大佬一样翻译任何语言",
"HowToEnableNotifications": "<0>TiddlyGit支持原生通知功能。但在某些情况下要接收通知您需要手动配置一些Web应用设置。</0><1>了解详情</1><2>。</2>",

View file

@ -1 +1 @@
{}
{"LinOnetwo":"LinOnetwo"}

View file

@ -10,6 +10,7 @@ import { AsyncReturnType } from 'type-fest';
export function usePromiseValue<T, DefaultValueType = T | undefined>(
asyncValue: () => Promise<T>,
defaultValue?: AsyncReturnType<typeof asyncValue>,
dependency: unknown[] = [],
): T | DefaultValueType {
const [value, valueSetter] = useState<T | DefaultValueType>(defaultValue as T | DefaultValueType);
// use initial value
@ -17,7 +18,7 @@ export function usePromiseValue<T, DefaultValueType = T | undefined>(
void (async () => {
valueSetter(await asyncValue());
})();
}, []);
}, dependency);
return value;
}

View file

@ -1,35 +1,51 @@
import React from 'react';
import styled from 'styled-components';
import { Button, DialogContent } from '@material-ui/core';
import { Trans, useTranslation } from 'react-i18next';
import { Button, DialogContent as DialogContentRaw } from '@material-ui/core';
import { usePromiseValue } from '@/helpers/useServiceValue';
const Icon = styled.img`
height: 96;
width: 96;
const DialogContent = styled(DialogContentRaw)`
min-width: 320px;
text-align: 'center';
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const DialogContentSC = styled(DialogContent)`
min-width: 320;
text-align: 'center';
const Icon = styled.img`
height: 96px;
width: 96px;
`;
const Title = styled.h6`
margin-top: 10px;
`;
const Version = styled.p`
const TiddlyGitVersion = styled.p`
margin-top: 0;
margin-bottom: 20px;
text-align: center;
`;
const VersionSmallContainer = styled.div`
margin-top: 20px;
const DependenciesVersionsContainer = styled.div`
margin-top: 0px;
margin-bottom: 20px;
flex-direction: column;
justify-content: center;
align-items: center;
`;
const VersionSmall = styled.span`
const DependenciesVersions = styled.div`
font-size: 0.8rem;
text-align: center;
`;
const ButtonContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: center;
`;
const GoToTheWebsiteButton = styled(Button)`
margin-right: 10px;
`;
@ -47,6 +63,7 @@ const Link = styled.span`
`;
export default function About(): JSX.Element {
const { t } = useTranslation();
const versions = usePromiseValue(async () => {
const processVersions = (await window.service.context.get('environmentVersions')) as NodeJS.ProcessVersions;
return [
@ -61,60 +78,61 @@ export default function About(): JSX.Element {
const platform = usePromiseValue<string>(async () => (await window.service.context.get('platform')) as string);
return (
<div>
<DialogContentSC>
<Icon src={iconPath} alt="TiddlyGit" />
<Title>TiddlyGit ({platform ?? 'Unknown Platform'})</Title>
<Version>{`Version v${appVersion ?? ' - '}.`}</Version>
<VersionSmallContainer>
{versions?.map(({ name, version }) => (
<VersionSmall key={name}>
{name}: {version}
</VersionSmall>
))}
</VersionSmallContainer>
<DialogContent>
<Icon src={`file:///${iconPath ?? ''}`} alt="TiddlyGit" />
<Title>TiddlyGit ({platform ?? 'Unknown Platform'})</Title>
<TiddlyGitVersion>{`Version v${appVersion ?? ' - '}.`}</TiddlyGitVersion>
<DependenciesVersionsContainer>
{versions?.map(({ name, version }) => (
<DependenciesVersions key={name}>
{name}: {version}
</DependenciesVersions>
))}
</DependenciesVersionsContainer>
<ButtonContainer>
<GoToTheWebsiteButton onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TiddlyGit-Desktop')}>
Website
</GoToTheWebsiteButton>
<br />
<GoToTheWebsiteButton onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TiddlyGit-Desktop/issues/new/choose')}>
Support
</GoToTheWebsiteButton>
</ButtonContainer>
<MadeBy>
<MadeBy>
<Trans t={t} i18nKey="Dialog.MadeWithLove">
<span>Made with </span>
<span role="img" aria-label="love">
</span>
<span> by </span>
<Link
onClick={async () => 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
</Link>
<span> and </span>
<Link
onClick={async () => 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
</Link>
</MadeBy>
</DialogContentSC>
</div>
</Trans>
<Link
onClick={async () => 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}>
{t('LinOnetwo')}
</Link>
<span> && </span>
<Link
onClick={async () => 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}>
{t('Preference.WebCatalog')}
</Link>
</MadeBy>
</DialogContent>
);
}

View file

@ -1,18 +1,21 @@
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
/* eslint-disable unicorn/no-null */
import React, { useState, useEffect } from 'react';
import React from 'react';
import styled, { css } from 'styled-components';
import { useTranslation } from 'react-i18next';
import ButtonRaw from '@material-ui/core/Button';
import TextFieldRaw from '@material-ui/core/TextField';
import Divider from '@material-ui/core/Divider';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction';
import Switch from '@material-ui/core/Switch';
import Typography from '@material-ui/core/Typography';
import Autocomplete from '@material-ui/lab/Autocomplete';
import {
Tooltip,
Button as ButtonRaw,
TextField as TextFieldRaw,
Divider,
List as ListRaw,
ListItem,
ListItemText,
ListItemSecondaryAction,
Switch,
Typography,
} from '@material-ui/core';
import { Autocomplete } from '@material-ui/lab';
import defaultIcon from '../../images/default-icon.png';
import type { ISubWikiPluginContent } from '@services/wiki/update-plugin-content';
@ -20,11 +23,11 @@ import { WindowNames, WindowMeta } from '@services/windows/WindowProperties';
import { usePromiseValue } from '@/helpers/useServiceValue';
import { useWorkspaceObservable } from '@services/workspaces/hooks';
import { useForm } from './formHook';
import { IWorkspace } from '@services/workspaces/interface';
const Root = styled.div`
background: #fffff0;
height: 100vh;
width: 100vw;
height: 100%;
width: 100%;
padding: 20px;
display: flex;
flex-direction: column;
@ -37,18 +40,19 @@ const Button = styled(ButtonRaw)`
margin-left: 10px;
`;
const TextField = styled(TextFieldRaw)`
margin-bottom: 30px;
margin-bottom: 10px;
`;
TextField.defaultProps = {
fullWidth: true,
margin: 'dense',
variant: 'outlined',
size: 'small',
variant: 'filled',
InputLabelProps: {
shrink: true,
},
};
const AvatarFlex = styled.div`
display: 'flex';
display: flex;
`;
const AvatarLeft = styled.div`
padding-top: 10px;
@ -58,6 +62,10 @@ const AvatarLeft = styled.div`
`;
const AvatarRight = styled.div`
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: flex-start;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
@ -67,10 +75,9 @@ const AvatarRight = styled.div`
* border: theme.palette.type === 'dark' ? 'none': '1px solid rgba(0, 0, 0, 0.12)';
* */
const Avatar = styled.div<{ transparentBackground?: boolean }>`
height: 85;
width: 85;
background: white;
border-radius: 4;
height: 85px;
width: 85px;
border-radius: 4px;
color: #333;
font-size: 32px;
line-height: 64px;
@ -94,10 +101,8 @@ const AvatarPicture = styled.img`
height: 100%;
width: 100%;
`;
const ButtonBot = styled(ButtonRaw)`
margin-top: 10px;
`;
ButtonBot.defaultProps = {
const PictureButton = styled(ButtonRaw)``;
PictureButton.defaultProps = {
variant: 'outlined',
size: 'small',
};
@ -107,6 +112,12 @@ const Caption = styled(Typography)`
Caption.defaultProps = {
variant: 'caption',
};
const List = styled(ListRaw)`
& > li > div {
padding-top: 0;
padding-bottom: 0;
}
`;
const getValidIconPath = (iconPath?: string | null): string => {
if (typeof iconPath === 'string') {
@ -116,21 +127,12 @@ const getValidIconPath = (iconPath?: string | null): string => {
};
const workspaceID = (window.meta as WindowMeta[WindowNames.editWorkspace]).workspaceID as string;
const wikiPictureExtensions = ['jpg', 'jpeg', 'png', 'gif', 'tiff', 'tif', 'bmp', 'dib'];
export default function EditWorkspace(): JSX.Element {
const { t } = useTranslation();
const fileSystemPaths = usePromiseValue<ISubWikiPluginContent[]>(
async () => await window.service.wiki.getSubWikiPluginContent(mainWikiToLink),
[],
) as ISubWikiPluginContent[];
const originalWorkspace = useWorkspaceObservable(workspaceID);
const [workspace, workspaceSetter, onSave] = useForm(originalWorkspace);
if (workspaceID === undefined) {
return <Root>Error {workspaceID ?? '-'} not exists</Root>;
}
if (workspace === undefined) {
return <Root>Loading...</Root>;
}
const {
mainWikiToLink,
isSubWiki,
@ -143,13 +145,25 @@ export default function EditWorkspace(): JSX.Element {
disableAudio,
disableNotifications,
homeUrl,
} = workspace;
} = ((workspace ?? {}) as unknown) as IWorkspace;
const fileSystemPaths = usePromiseValue<ISubWikiPluginContent[]>(
async () => (mainWikiToLink ? await window.service.wiki.getSubWikiPluginContent(mainWikiToLink) : []),
[],
[mainWikiToLink],
) as ISubWikiPluginContent[];
if (workspaceID === undefined) {
return <Root>Error {workspaceID ?? '-'} not exists</Root>;
}
if (workspace === undefined) {
return <Root>Loading...</Root>;
}
return (
<Root>
<FlexGrow>
<TextField
id="outlined-full-width"
label={t('EditWorkspace.Path')}
helperText={t('EditWorkspace.PathDescription')}
placeholder="Optional"
value={name}
onChange={(event) => workspaceSetter({ ...workspace, name: event.target.value })}
@ -168,13 +182,15 @@ export default function EditWorkspace(): JSX.Element {
}}
/>
)}
<Autocomplete
freeSolo
options={fileSystemPaths?.map((fileSystemPath) => fileSystemPath.tagName)}
value={tagName}
onInputChange={(_, value) => workspaceSetter({ ...workspace, tagName: value })}
renderInput={(parameters) => <TextField {...parameters} label={t('AddWorkspace.TagName')} helperText={t('AddWorkspace.TagNameHelp')} />}
/>
{isSubWiki && (
<Autocomplete
freeSolo
options={fileSystemPaths?.map((fileSystemPath) => fileSystemPath.tagName)}
value={tagName}
onInputChange={(_, value) => workspaceSetter({ ...workspace, tagName: value })}
renderInput={(parameters) => <TextField {...parameters} label={t('AddWorkspace.TagName')} helperText={t('AddWorkspace.TagNameHelp')} />}
/>
)}
<AvatarFlex>
<AvatarLeft>
<Avatar transparentBackground={transparentBackground}>
@ -182,24 +198,25 @@ export default function EditWorkspace(): JSX.Element {
</Avatar>
</AvatarLeft>
<AvatarRight>
<Button
variant="outlined"
size="small"
onClick={async () => {
const filePaths = await window.service.native.pickFile([
{ name: 'Images', extensions: ['jpg', 'jpeg', 'png', 'gif', 'tiff', 'tif', 'bmp', 'dib'] },
]);
if (filePaths.length > 0) {
await window.service.workspace.update(workspaceID, { picturePath: filePaths[0] });
}
}}>
{t('EditWorkspace.SelectLocal')}
</Button>
<Caption>PNG, JPEG, GIF, TIFF or BMP.</Caption>
<Tooltip title={wikiPictureExtensions.join(', ')} placement="top">
<PictureButton
variant="outlined"
size="small"
onClick={async () => {
const filePaths = await window.service.native.pickFile([{ name: 'Images', extensions: wikiPictureExtensions }]);
if (filePaths.length > 0) {
await window.service.workspace.update(workspaceID, { picturePath: filePaths[0] });
}
}}>
{t('EditWorkspace.SelectLocal')}
</PictureButton>
</Tooltip>
<ButtonBot onClick={() => workspaceSetter({ ...workspace, picturePath: null })} disabled={!picturePath}>
{t('EditWorkspace.ResetDefaultIcon')}
</ButtonBot>
<Tooltip title={t('EditWorkspace.NoRevert') ?? ''} placement="bottom">
<PictureButton onClick={() => workspaceSetter({ ...workspace, picturePath: null })} disabled={!picturePath}>
{t('EditWorkspace.ResetDefaultIcon')}
</PictureButton>
</Tooltip>
</AvatarRight>
</AvatarFlex>
{!isSubWiki && (

View file

@ -1,5 +1,6 @@
import React, { useCallback } from 'react';
import styled, { css } from 'styled-components';
import { useTranslation } from 'react-i18next';
import { AsyncReturnType } from 'type-fest';
import { DndContext } from '@dnd-kit/core';
import { SortableContext, arrayMove, verticalListSortingStrategy } from '@dnd-kit/sortable';
@ -146,7 +147,7 @@ const Tip2 = styled.div`
user-select: none;
`;
const End = styled.div`
const SideBarEnd = styled.div`
display: flex;
flex-direction: column;
`;
@ -166,6 +167,7 @@ const SidebarContainer = ({ children }: { children: React.ReactNode }): JSX.Elem
};
export default function Main(): JSX.Element {
const { t } = useTranslation();
const workspacesList = useWorkspacesListObservable();
const [{ attachToMenubar, titleBar, sidebar, pauseNotifications, themeSource }, isFullScreen] = usePromiseValue<[Partial<IPreferences>, boolean | undefined]>(
async () => await Promise.all([window.service.preference.getPreferences(), window.service.window.isFullScreen()]),
@ -218,16 +220,14 @@ export default function Main(): JSX.Element {
)}
<WorkspaceSelector id="add" onClick={() => void window.service.window.open(WindowNames.addWorkspace)} />
</SidebarTop>
<End>
<IconButton aria-label="Notifications" onClick={async () => await window.service.window.open(WindowNames.notifications)}>
<SideBarEnd>
<IconButton aria-label={t('Preference.Notifications')} onClick={async () => await window.service.window.open(WindowNames.notifications)}>
{typeof pauseNotifications === 'string' && pauseNotifications.length > 0 ? <NotificationsPausedIcon /> : <NotificationsIcon />}
</IconButton>
{attachToMenubar === true && (
<IconButton aria-label="Preferences" onClick={async () => await window.service.window.open(WindowNames.preferences)}>
<SettingsIcon />
</IconButton>
)}
</End>
<IconButton aria-label={t('ContextMenu.Preferences')} onClick={async () => await window.service.window.open(WindowNames.preferences)}>
<SettingsIcon />
</IconButton>
</SideBarEnd>
</SidebarContainer>
)}
<ContentRoot>

View file

@ -43,7 +43,7 @@ export const windowDimension: Record<WindowNames, { width?: number; height?: num
},
[WindowNames.editWorkspace]: {
width: 420,
height: 700,
height: 600,
},
[WindowNames.preferences]: {
width: 820,
@ -51,7 +51,7 @@ export const windowDimension: Record<WindowNames, { width?: number; height?: num
},
[WindowNames.notifications]: {
width: 400,
height: 565,
height: 585,
},
[WindowNames.spellcheck]: {
width: 400,