mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-05 18:20:39 -08:00
lint: dprint
This commit is contained in:
parent
a3bd62846d
commit
830e11aa77
151 changed files with 2071 additions and 1541 deletions
|
|
@ -1,30 +1,30 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-expressions */
|
||||
import { setWorldConstructor, Given, Then } from '@cucumber/cucumber';
|
||||
import { Given, setWorldConstructor, Then } from '@cucumber/cucumber';
|
||||
import { delay } from 'bluebird';
|
||||
import { expect } from 'chai';
|
||||
import { TidGiWorld } from '../supports/world';
|
||||
|
||||
setWorldConstructor(TidGiWorld);
|
||||
|
||||
Given('the app is launched', async function (this: TidGiWorld) {
|
||||
Given('the app is launched', async function(this: TidGiWorld) {
|
||||
await delay(100);
|
||||
await this.start();
|
||||
const windowCount = await this.app?.client?.getWindowCount();
|
||||
expect(windowCount).equal(1);
|
||||
});
|
||||
|
||||
Then('the element {string} is on the page', async function (this: TidGiWorld, elementSelector: string) {
|
||||
Then('the element {string} is on the page', async function(this: TidGiWorld, elementSelector: string) {
|
||||
const result = await this.getElement(elementSelector);
|
||||
expect(result).to.not.be.undefined;
|
||||
this.updateContext({ previousElement: result });
|
||||
});
|
||||
Then('click on this element', async function (this: TidGiWorld) {
|
||||
Then('click on this element', async function(this: TidGiWorld) {
|
||||
expect(this.context?.previousElement).to.not.be.undefined;
|
||||
if (this.context?.previousElement !== undefined) {
|
||||
await this.context.previousElement.click();
|
||||
}
|
||||
});
|
||||
Then('click on {string} element', async function (this: TidGiWorld, elementSelector: string) {
|
||||
Then('click on {string} element', async function(this: TidGiWorld, elementSelector: string) {
|
||||
const result = await this.getElement(elementSelector);
|
||||
expect(result).to.not.be.undefined;
|
||||
if (result !== undefined) {
|
||||
|
|
@ -32,7 +32,7 @@ Then('click on {string} element', async function (this: TidGiWorld, elementSelec
|
|||
await result.click();
|
||||
}
|
||||
});
|
||||
Then('{string} window show up', async function (this: TidGiWorld, windowName: string) {
|
||||
Then('{string} window show up', async function(this: TidGiWorld, windowName: string) {
|
||||
// await delay(1000);
|
||||
const windowCount = await this.app?.client?.getWindowCount();
|
||||
expect(windowCount).equal(2);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import { After, Before } from '@cucumber/cucumber';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { DEFAULT_WIKI_FOLDER } from '../../src/constants/paths';
|
||||
import { SETTINGS_FOLDER } from '../../src/constants/appPaths';
|
||||
import { DEFAULT_WIKI_FOLDER } from '../../src/constants/paths';
|
||||
import { TidGiWorld } from './world';
|
||||
|
||||
Before(async function () {
|
||||
Before(async function() {
|
||||
// clear setting folder
|
||||
await fs.remove(SETTINGS_FOLDER);
|
||||
await fs.remove(DEFAULT_WIKI_FOLDER);
|
||||
});
|
||||
|
||||
After(async function (this: TidGiWorld, testCase) {
|
||||
After(async function(this: TidGiWorld, testCase) {
|
||||
// print logs if test failed
|
||||
// if (this.app !== undefined && testCase.result?.status === Status.FAILED) {
|
||||
// console.log('main:\n---\n');
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
const packageJson = require('./package.json');
|
||||
const beforeAsar = require('./scripts/beforeAsar')
|
||||
const beforeAsar = require('./scripts/beforeAsar');
|
||||
|
||||
const { version, description } = packageJson;
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ const config = {
|
|||
An unhandled rejection has occurred inside Forge:
|
||||
[object Object]
|
||||
*/
|
||||
{
|
||||
{
|
||||
name: '@reforged/maker-appimage',
|
||||
platforms: ['linux'],
|
||||
config: {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ const fs = require('fs-extra');
|
|||
const util = require('util');
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} buildPath /var/folders/qj/7j0zx32d0l75zmnrl1w3m3b80000gn/T/electron-packager/darwin-x64/TidGi-darwin-x64/Electron.app/Contents/Resources/app
|
||||
* @param {*} electronVersion 12.0.6
|
||||
* @param {*} platform darwin
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useState, useCallback, useEffect, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Root = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -66,11 +66,11 @@ export default function FindInPage(): JSX.Element | null {
|
|||
return (
|
||||
<Root>
|
||||
<InfoContainer>
|
||||
<Typography variant="body2">
|
||||
<Typography variant='body2'>
|
||||
<strong>{activeMatch}</strong>
|
||||
<span> / </span>
|
||||
<span>/</span>
|
||||
<strong>{matches}</strong>
|
||||
<span> {t('Menu.FindMatches')}</span>
|
||||
<span>{t('Menu.FindMatches')}</span>
|
||||
</Typography>
|
||||
</InfoContainer>
|
||||
<div>
|
||||
|
|
@ -79,7 +79,7 @@ export default function FindInPage(): JSX.Element | null {
|
|||
inputRef={inputReference}
|
||||
placeholder={t('Menu.Find')}
|
||||
value={text}
|
||||
margin="dense"
|
||||
margin='dense'
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
const value = event.target.value;
|
||||
if (typeof value !== 'string') return;
|
||||
|
|
@ -119,41 +119,45 @@ export default function FindInPage(): JSX.Element | null {
|
|||
/>
|
||||
</div>
|
||||
<Button
|
||||
size="small"
|
||||
size='small'
|
||||
disabled={text.length === 0 || matches < 1}
|
||||
onClick={() => {
|
||||
if (text.length > 0) {
|
||||
void window.service.window.findInPage(text, false);
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t('Menu.FindPrevious')}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
size='small'
|
||||
disabled={text.length === 0 || matches < 1}
|
||||
onClick={() => {
|
||||
if (text.length > 0) {
|
||||
void window.service.window.findInPage(text, true);
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t('Menu.FindNext')}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
size='small'
|
||||
disabled={text.length === 0}
|
||||
onClick={() => {
|
||||
if (text.length > 0) {
|
||||
void window.service.window.findInPage(text, true);
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t('Menu.Find')}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
size='small'
|
||||
onClick={() => {
|
||||
void window.service.window.stopFindInPage(true);
|
||||
openSetter(false);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t('Menu.Close')}
|
||||
</Button>
|
||||
</Root>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import React from 'react';
|
||||
import Menu from '@material-ui/core/Menu';
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
buttonElement: React.ReactElement;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React, { useCallback, useState } from 'react';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import { Snackbar, Button, IconButton, Tooltip } from '@material-ui/core';
|
||||
import { Button, IconButton, Snackbar, Tooltip } from '@material-ui/core';
|
||||
import { Close as CloseIcon } from '@material-ui/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
|
||||
const progressAnimation = keyframes`
|
||||
from {
|
||||
|
|
@ -65,7 +65,7 @@ export function useRestartSnackbar(waitBeforeCountDown = 1000, waitBeforeRestart
|
|||
|
||||
return [
|
||||
requestRestartCountDown,
|
||||
<div key="RestartSnackbar">
|
||||
<div key='RestartSnackbar'>
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
|
||||
open={opened}
|
||||
|
|
@ -88,14 +88,15 @@ export function useRestartSnackbar(waitBeforeCountDown = 1000, waitBeforeRestart
|
|||
<RestartButton
|
||||
key={currentWaitBeforeRestart}
|
||||
currentWaitBeforeRestart={currentWaitBeforeRestart}
|
||||
color="secondary"
|
||||
size="small"
|
||||
onClick={handleCloseAndRestart}>
|
||||
color='secondary'
|
||||
size='small'
|
||||
onClick={handleCloseAndRestart}
|
||||
>
|
||||
{t('Dialog.RestartNow')}
|
||||
</RestartButton>
|
||||
<Tooltip title={<span>{t('Dialog.Later')}</span>}>
|
||||
<IconButton size="small" aria-label="close" color="inherit" onClick={handleCancelRestart}>
|
||||
<CloseIcon fontSize="small" />
|
||||
<IconButton size='small' aria-label='close' color='inherit' onClick={handleCancelRestart}>
|
||||
<CloseIcon fontSize='small' />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -4,20 +4,20 @@ export const RootStyle = styled.div`
|
|||
.Mui-selected,
|
||||
.Mui-checked {
|
||||
${({ theme }) =>
|
||||
theme.palette.mode === 'dark'
|
||||
? css`
|
||||
theme.palette.mode === 'dark'
|
||||
? css`
|
||||
color: ${theme.palette.primary.light} !important;
|
||||
`
|
||||
: ''};
|
||||
: ''};
|
||||
}
|
||||
.Mui-disabled {
|
||||
${({ theme }) =>
|
||||
theme.palette.mode === 'dark'
|
||||
? css`
|
||||
theme.palette.mode === 'dark'
|
||||
? css`
|
||||
color: ${theme.palette.primary.dark} !important;
|
||||
-webkit-text-fill-color: ${theme.palette.primary.light};
|
||||
`
|
||||
: ''};
|
||||
: ''};
|
||||
}
|
||||
|
||||
label,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
import Promise from 'bluebird';
|
||||
import React, { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useQuery, useMutation, GraphQLClient, ClientContext } from 'graphql-hooks';
|
||||
import { trim } from 'lodash';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
||||
import Promise from 'bluebird';
|
||||
import { ClientContext, GraphQLClient, useMutation, useQuery } from 'graphql-hooks';
|
||||
import { trim } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { TextField, LinearProgress, List, ListItem, ListItemIcon, ListItemText, Button } from '@material-ui/core';
|
||||
import { Folder as FolderIcon, Cached as CachedIcon, CreateNewFolder as CreateNewFolderIcon } from '@material-ui/icons';
|
||||
import { Button, LinearProgress, List, ListItem, ListItemIcon, ListItemText, TextField } from '@material-ui/core';
|
||||
import { Cached as CachedIcon, CreateNewFolder as CreateNewFolderIcon, Folder as FolderIcon } from '@material-ui/icons';
|
||||
|
||||
import { GITHUB_GRAPHQL_API } from '@/constants/auth';
|
||||
import { useUserInfoObservable } from '@services/auth/hooks';
|
||||
|
|
@ -80,7 +80,7 @@ export default function SearchGithubRepo(props: Props): JSX.Element {
|
|||
[],
|
||||
);
|
||||
useEffect(() => {
|
||||
graphqlClient.setHeader('Authorization', accessToken !== undefined ? `Bearer ${accessToken}` : '');
|
||||
graphqlClient.setHeader('Authorization', accessToken === undefined ? '' : `Bearer ${accessToken}`);
|
||||
}, [accessToken, graphqlClient]);
|
||||
|
||||
if (githubUsername === '' || githubUsername === undefined || accessToken === '' || accessToken === undefined) {
|
||||
|
|
@ -132,7 +132,9 @@ function SearchGithubRepoResultList({
|
|||
const timeoutHandle = setTimeout(async () => {
|
||||
await refetchDebounced();
|
||||
}, 100);
|
||||
return () => clearTimeout(timeoutHandle);
|
||||
return () => {
|
||||
clearTimeout(timeoutHandle);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [githubUsername, accessToken]);
|
||||
// try refetch on error
|
||||
|
|
@ -143,7 +145,9 @@ function SearchGithubRepoResultList({
|
|||
await refetchDebounced();
|
||||
retryIntervalSetter(retryInterval * 10);
|
||||
}, retryInterval);
|
||||
return () => clearTimeout(timeoutHandle);
|
||||
return () => {
|
||||
clearTimeout(timeoutHandle);
|
||||
};
|
||||
}
|
||||
return () => {};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -189,11 +193,18 @@ function SearchGithubRepoResultList({
|
|||
value={githubRepoSearchString}
|
||||
helperText={helperText}
|
||||
/>
|
||||
{(loading || isCreatingRepo) && <LinearProgress variant="query" />}
|
||||
{(loading || isCreatingRepo) && <LinearProgress variant='query' />}
|
||||
|
||||
<List component="nav" aria-label="main mailbox folders">
|
||||
<List component='nav' aria-label='main mailbox folders'>
|
||||
{repoList.map(({ name, url }) => (
|
||||
<ListItem button key={url} onClick={() => onSelectRepo(url, name)} selected={trim(githubWikiUrl) === trim(url)}>
|
||||
<ListItem
|
||||
button
|
||||
key={url}
|
||||
onClick={() => {
|
||||
onSelectRepo(url, name);
|
||||
}}
|
||||
selected={trim(githubWikiUrl) === trim(url)}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<FolderIcon />
|
||||
</ListItemIcon>
|
||||
|
|
@ -220,20 +231,19 @@ function SearchGithubRepoResultList({
|
|||
isCreatingRepoSetter(false);
|
||||
githubWikiUrlSetter(wikiUrlToCreate);
|
||||
}}
|
||||
selected={isCreateNewRepo}>
|
||||
selected={isCreateNewRepo}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<CreateNewFolderIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={`${
|
||||
isCreateMainWorkspace ? t('AddWorkspace.CreatePublicRepository') : t('AddWorkspace.CreatePrivateRepository')
|
||||
} ${githubRepoSearchString}`}
|
||||
primary={`${isCreateMainWorkspace ? t('AddWorkspace.CreatePublicRepository') : t('AddWorkspace.CreatePrivateRepository')} ${githubRepoSearchString}`}
|
||||
/>
|
||||
</ListItem>
|
||||
)}
|
||||
</List>
|
||||
{repoList.length === 0 && (
|
||||
<ReloadButton color="secondary" endIcon={<CachedIcon />} onClick={async () => await refetchDebounced()}>
|
||||
<ReloadButton color='secondary' endIcon={<CachedIcon />} onClick={async () => await refetchDebounced()}>
|
||||
{t('AddWorkspace.Reload')}
|
||||
</ReloadButton>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { TextField, Button } from '@material-ui/core';
|
||||
import { Button, TextField } from '@material-ui/core';
|
||||
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { useUserInfoObservable } from '@services/auth/hooks';
|
||||
import { useAuth } from './gitTokenHooks';
|
||||
import { getServiceBranchTypes, getServiceEmailTypes, getServiceTokenTypes, getServiceUserNameTypes } from '@services/auth/interface';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { useAuth } from './gitTokenHooks';
|
||||
|
||||
const AuthingLoginButton = styled(Button)`
|
||||
width: 100%;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { AuthenticationClient } from 'authing-js-sdk';
|
||||
import { APP_DOMAIN, APP_ID } from '@/constants/auth';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { APP_ID, APP_DOMAIN } from '@/constants/auth';
|
||||
import { AuthenticationClient } from 'authing-js-sdk';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
export function useAuth(storageService: SupportedStorageServices): [() => Promise<void>, () => Promise<void>] {
|
||||
const authing = useMemo(
|
||||
|
|
@ -42,7 +42,9 @@ export function useAuth(storageService: SupportedStorageServices): [() => Promis
|
|||
}
|
||||
}
|
||||
},
|
||||
onError: (code, message) => onFailure(new Error(message + String(code))),
|
||||
onError: (code, message) => {
|
||||
onFailure(new Error(message + String(code)));
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
onFailure(error as Error);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled, { DefaultTheme, keyframes } from 'styled-components';
|
||||
import { Tab as TabRaw, ListItemText as ListItemTextRaw } from '@material-ui/core';
|
||||
import { TabPanel as TabPanelRaw, TabContext, TabList as TabListRaw } from '@material-ui/lab';
|
||||
import { ListItemText as ListItemTextRaw, Tab as TabRaw } from '@material-ui/core';
|
||||
import { TabContext, TabList as TabListRaw, TabPanel as TabPanelRaw } from '@material-ui/lab';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled, { DefaultTheme, keyframes } from 'styled-components';
|
||||
|
||||
import { GitTokenForm } from './GitTokenForm';
|
||||
|
||||
|
|
@ -45,7 +45,8 @@ const TabsContainer = styled.div`
|
|||
min-width: 160px;
|
||||
}
|
||||
`;
|
||||
const backgroundColorShift = ({ theme }: { theme: DefaultTheme }) => keyframes`
|
||||
const backgroundColorShift = ({ theme }: { theme: DefaultTheme }) =>
|
||||
keyframes`
|
||||
from {background-color: ${theme.palette.background.default};}
|
||||
to {background-color: ${theme.palette.background.default};}
|
||||
`;
|
||||
|
|
@ -85,14 +86,17 @@ export function TokenForm({ storageProvider, storageProviderSetter }: Props): JS
|
|||
<TabContext value={currentTab}>
|
||||
<TabsContainer>
|
||||
<TabList
|
||||
onChange={(_event, newValue) => currentTabSetter(newValue as SupportedStorageServices)}
|
||||
orientation="vertical"
|
||||
variant="scrollable"
|
||||
onChange={(_event, newValue) => {
|
||||
currentTabSetter(newValue as SupportedStorageServices);
|
||||
}}
|
||||
orientation='vertical'
|
||||
variant='scrollable'
|
||||
value={currentTab}
|
||||
aria-label="Vertical tabs example">
|
||||
<Tab label="GitHub" value={SupportedStorageServices.github} />
|
||||
<Tab label="GitLab" value={SupportedStorageServices.gitlab} />
|
||||
<Tab label="Gitee" value={SupportedStorageServices.gitee} />
|
||||
aria-label='Vertical tabs example'
|
||||
>
|
||||
<Tab label='GitHub' value={SupportedStorageServices.github} />
|
||||
<Tab label='GitLab' value={SupportedStorageServices.gitlab} />
|
||||
<Tab label='Gitee' value={SupportedStorageServices.gitee} />
|
||||
</TabList>
|
||||
<TabPanel value={SupportedStorageServices.github}>
|
||||
<GitTokenForm storageService={SupportedStorageServices.github} />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useCallback, MouseEvent, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSortable } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { WorkspaceSelector } from './WorkspaceSelector';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
import { getWorkspaceMenuTemplate, openWorkspaceTagTiddler } from '@services/workspaces/getWorkspaceMenuTemplate';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
import { MouseEvent, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { WorkspaceSelector } from './WorkspaceSelector';
|
||||
|
||||
import defaultIcon from '@/images/default-icon.png';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { DndContext, useSensor, useSensors, PointerSensor } from '@dnd-kit/core';
|
||||
import { SortableContext, arrayMove, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||
import { DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
|
||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||
import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||
import { IWorkspace, IWorkspaceWithMetadata } from '@services/workspaces/interface';
|
||||
import { SortableWorkspaceSelector } from './SortableWorkspaceSelector';
|
||||
|
||||
|
|
@ -38,7 +38,8 @@ export function SortableWorkspaceSelectorList({ workspacesList, sidebarShortcutH
|
|||
});
|
||||
|
||||
await window.service.workspace.setWorkspaces(newWorkspaces);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<SortableContext items={workspaceIDs} strategy={verticalListSortingStrategy}>
|
||||
{workspacesList
|
||||
.sort((a, b) => a.order - b.order)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import Promise from 'bluebird';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import BadgeRaw from '@material-ui/core/Badge';
|
||||
import Promise from 'bluebird';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled, { css, keyframes } from 'styled-components';
|
||||
|
||||
import defaultIcon from '../../images/default-icon.png';
|
||||
import { getAssetsFileUrl } from '@/helpers/url';
|
||||
import defaultIcon from '../../images/default-icon.png';
|
||||
|
||||
Promise.config({ cancellation: true });
|
||||
|
||||
|
|
@ -28,21 +28,21 @@ const Root = styled.div<{ active?: boolean; hibernated?: boolean; workspaceClick
|
|||
border: 0;
|
||||
border-color: transparent;
|
||||
${({ hibernated }) =>
|
||||
hibernated === true &&
|
||||
css`
|
||||
hibernated === true &&
|
||||
css`
|
||||
opacity: 0.4;
|
||||
`}
|
||||
${({ active }) =>
|
||||
active === true &&
|
||||
css`
|
||||
active === true &&
|
||||
css`
|
||||
opacity: 1;
|
||||
`}
|
||||
box-sizing: border-box;
|
||||
border-left: ${({ workspaceCount }) => (workspaceCount > 1 ? '3px' : '0')} solid
|
||||
${({ active, theme }) => (active === true ? theme.palette.text.primary : 'transparent')};
|
||||
${({ workspaceClickedLoading }) =>
|
||||
workspaceClickedLoading === true &&
|
||||
css`
|
||||
workspaceClickedLoading === true &&
|
||||
css`
|
||||
&:hover {
|
||||
cursor: wait;
|
||||
}
|
||||
|
|
@ -69,15 +69,15 @@ const Avatar = styled.div<IAvatarProps>`
|
|||
text-transform: uppercase;
|
||||
overflow: hidden;
|
||||
${({ large }) =>
|
||||
large === true &&
|
||||
css`
|
||||
large === true &&
|
||||
css`
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
line-height: 44px;
|
||||
`}
|
||||
${({ transparent }) =>
|
||||
transparent === true &&
|
||||
css`
|
||||
transparent === true &&
|
||||
css`
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
|
|
@ -91,9 +91,9 @@ const Avatar = styled.div<IAvatarProps>`
|
|||
color: ${({ theme }) => theme.palette.common.black};
|
||||
}
|
||||
${({ addAvatar }: IAvatarProps) =>
|
||||
addAvatar
|
||||
? ''
|
||||
: css`
|
||||
addAvatar
|
||||
? ''
|
||||
: css`
|
||||
background-color: transparent;
|
||||
`}
|
||||
`;
|
||||
|
|
@ -102,8 +102,8 @@ const AvatarPicture = styled.img<{ large?: boolean }>`
|
|||
height: calc(36px - 2px);
|
||||
width: calc(36px - 2px);
|
||||
${({ large }) =>
|
||||
large === true &&
|
||||
css`
|
||||
large === true &&
|
||||
css`
|
||||
height: 44px;
|
||||
width: 44px;
|
||||
`}
|
||||
|
|
@ -119,8 +119,8 @@ const ShortcutText = styled.p<{ active?: boolean }>`
|
|||
word-break: break-all;
|
||||
text-align: center;
|
||||
${({ active }) =>
|
||||
active === true &&
|
||||
css`
|
||||
active === true &&
|
||||
css`
|
||||
text-decoration: underline;
|
||||
text-underline-offset: 0.2em;
|
||||
`}
|
||||
|
|
@ -172,28 +172,32 @@ export function WorkspaceSelector({
|
|||
active={active}
|
||||
onClick={workspaceClickedLoading ? () => {} : onClick}
|
||||
workspaceClickedLoading={workspaceClickedLoading}
|
||||
workspaceCount={workspaceCount}>
|
||||
<Badge color="secondary" badgeContent={badgeCount} max={99}>
|
||||
workspaceCount={workspaceCount}
|
||||
>
|
||||
<Badge color='secondary' badgeContent={badgeCount} max={99}>
|
||||
{!hideSideBarIcon && (
|
||||
<Avatar
|
||||
large={!showSidebarShortcutHints}
|
||||
transparent={transparentBackground}
|
||||
addAvatar={id === 'add'}
|
||||
highlightAdd={index === 0}
|
||||
id={id === 'add' || id === 'guide' ? 'add-workspace-button' : `workspace-avatar-${id}`}>
|
||||
{id === 'add' ? (
|
||||
'+'
|
||||
) : id === 'guide' ? (
|
||||
'※'
|
||||
) : (
|
||||
<AvatarPicture alt="Icon" large={!showSidebarShortcutHints} src={getAssetsFileUrl(picturePath ?? defaultIcon)} draggable={false} />
|
||||
)}
|
||||
id={id === 'add' || id === 'guide' ? 'add-workspace-button' : `workspace-avatar-${id}`}
|
||||
>
|
||||
{id === 'add'
|
||||
? (
|
||||
'+'
|
||||
)
|
||||
: (id === 'guide'
|
||||
? (
|
||||
'※'
|
||||
)
|
||||
: <AvatarPicture alt='Icon' large={!showSidebarShortcutHints} src={getAssetsFileUrl(picturePath ?? defaultIcon)} draggable={false} />)}
|
||||
</Avatar>
|
||||
)}
|
||||
</Badge>
|
||||
{(showSidebarShortcutHints || hideSideBarIcon) && (
|
||||
<ShortcutText active={active}>
|
||||
{id === 'add' ? t('WorkspaceSelector.Add') : id === 'guide' ? t('WorkspaceSelector.Guide') : shortWorkspaceName}
|
||||
{id === 'add' ? t('WorkspaceSelector.Add') : (id === 'guide' ? t('WorkspaceSelector.Guide') : shortWorkspaceName)}
|
||||
</ShortcutText>
|
||||
)}
|
||||
</Root>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
export * from './WorkspaceSelector';
|
||||
export * from './SortableWorkspaceSelector';
|
||||
export * from './SortableWorkspaceSelectorList';
|
||||
export * from './WorkspaceSelector';
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import { SVGContainer } from './SVGContainer';
|
|||
|
||||
function CommandPaletteSVG(): JSX.Element {
|
||||
return (
|
||||
<svg width="22pt" height="22pt" viewBox="0 0 512 512" style={{ transform: 'rotate(225deg)' }} fill="currentColor">
|
||||
<svg width='22pt' height='22pt' viewBox='0 0 512 512' style={{ transform: 'rotate(225deg)' }} fill='currentColor'>
|
||||
<path
|
||||
d="M224 96l16-32 32-16-32-16-16-32-16 32-32 16 32 16 16 32zM80 160l26.66-53.33L160 80l-53.34-26.67L80 0 53.34 53.33 0 80l53.34 26.67L80 160zm0-96c8.84 0 16 7.16 16 16s-7.16 16-16 16-16-7.16-16-16 7.16-16 16-16zm352 224l-26.66 53.33L352 368l53.34 26.67L432 448l26.66-53.33L512 368l-53.34-26.67L432 288zm0 96c-8.84 0-16-7.16-16-16s7.16-16 16-16 16 7.16 16 16-7.16 16-16 16zm70.63-306.04L434.04 9.37C427.79 3.12 419.6 0 411.41 0s-16.38 3.12-22.63 9.37L9.37 388.79c-12.5 12.5-12.5 32.76 0 45.25l68.59 68.59c6.25 6.25 14.44 9.37 22.63 9.37s16.38-3.12 22.63-9.37l379.41-379.41c12.49-12.5 12.49-32.76 0-45.26zM100.59 480L32 411.41l258.38-258.4 68.6 68.6L100.59 480zm281.02-281.02l-68.6-68.6L411.38 32h.03L480 100.59l-98.39 98.39z"
|
||||
fillRule="evenodd"
|
||||
d='M224 96l16-32 32-16-32-16-16-32-16 32-32 16 32 16 16 32zM80 160l26.66-53.33L160 80l-53.34-26.67L80 0 53.34 53.33 0 80l53.34 26.67L80 160zm0-96c8.84 0 16 7.16 16 16s-7.16 16-16 16-16-7.16-16-16 7.16-16 16-16zm352 224l-26.66 53.33L352 368l53.34 26.67L432 448l26.66-53.33L512 368l-53.34-26.67L432 288zm0 96c-8.84 0-16-7.16-16-16s7.16-16 16-16 16 7.16 16 16-7.16 16-16 16zm70.63-306.04L434.04 9.37C427.79 3.12 419.6 0 411.41 0s-16.38 3.12-22.63 9.37L9.37 388.79c-12.5 12.5-12.5 32.76 0 45.25l68.59 68.59c6.25 6.25 14.44 9.37 22.63 9.37s16.38-3.12 22.63-9.37l379.41-379.41c12.49-12.5 12.49-32.76 0-45.26zM100.59 480L32 411.41l258.38-258.4 68.6 68.6L100.59 480zm281.02-281.02l-68.6-68.6L411.38 32h.03L480 100.59l-98.39 98.39z'
|
||||
fillRule='evenodd'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { sourcePath } from './paths';
|
|||
export const USER_DATA_FOLDER = app.getPath('userData');
|
||||
export const SETTINGS_FOLDER = isDevelopmentOrTest
|
||||
? path.resolve(sourcePath, '..', developmentSettingFolderName)
|
||||
: // eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
path.resolve(USER_DATA_FOLDER, 'settings');
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
: path.resolve(USER_DATA_FOLDER, 'settings');
|
||||
export const LOCAL_GIT_DIRECTORY = path.resolve(isDevelopmentOrTest ? path.join(sourcePath, '..') : process.resourcesPath, 'node_modules', 'dugite', 'git');
|
||||
export const LOG_FOLDER = isDevelopmentOrTest ? path.resolve(sourcePath, '..', 'logs') : path.resolve(USER_DATA_FOLDER, 'logs');
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@
|
|||
const electron = require('electron');
|
||||
const { isPackaged } = require('electron-is-packaged');
|
||||
|
||||
export const isElectronDevelopment =
|
||||
!isPackaged && (process.env.NODE_ENV === 'development' || (typeof electron === 'string' || electron.app === undefined ? false : !electron.app.isPackaged));
|
||||
export const isElectronDevelopment = !isPackaged &&
|
||||
(process.env.NODE_ENV === 'development' || (typeof electron === 'string' || electron.app === undefined ? false : !electron.app.isPackaged));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { LOCALIZATION_FOLDER } from '@/constants/paths';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { LOCALIZATION_FOLDER } from '@/constants/paths';
|
||||
|
||||
export const supportedLanguagesMap = fs.readJsonSync(path.join(LOCALIZATION_FOLDER, 'supportedLanguages.json')) as Record<string, string>;
|
||||
export const tiddlywikiLanguagesMap = fs.readJsonSync(path.join(LOCALIZATION_FOLDER, 'tiddlywikiLanguages.json')) as Record<string, string | undefined>;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import path from 'path';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { isMac } from '../helpers/system';
|
||||
import { isDevelopmentOrTest } from './environment';
|
||||
import { developmentWikiFolderName, localizationFolderName } from './fileNames';
|
||||
import { isMac } from '../helpers/system';
|
||||
|
||||
/** src folder */
|
||||
export const sourcePath = path.resolve(__dirname, '..');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export enum WikiStateKey {
|
||||
titleBarOpened = 'titleBarOpened',
|
||||
sideBarOpened = 'sideBarOpened',
|
||||
titleBarOpened = 'titleBarOpened',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import fs from 'fs-extra';
|
||||
import settings from 'electron-settings';
|
||||
import { parse as bestEffortJsonParser } from 'best-effort-json-parser';
|
||||
import { SETTINGS_FOLDER } from '@/constants/appPaths';
|
||||
import { logger } from '@services/libs/log';
|
||||
import { parse as bestEffortJsonParser } from 'best-effort-json-parser';
|
||||
import settings from 'electron-settings';
|
||||
import fs from 'fs-extra';
|
||||
import { isWin } from './system';
|
||||
|
||||
export function fixSettingFileWhenError(jsonError: Error): void {
|
||||
logger.error('Setting file format bad: ' + jsonError.message);
|
||||
const jsonContent = fs.readFileSync(settings.file(), 'utf-8');
|
||||
const jsonContent = fs.readFileSync(settings.file(), 'utf8');
|
||||
logger.info('Try to fix JSON content.');
|
||||
try {
|
||||
const repaired = bestEffortJsonParser(jsonContent) as Record<string, unknown>;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { platform, type, networkInterfaces } from 'os';
|
||||
import ip from 'ipaddr.js';
|
||||
import { networkInterfaces, platform, type } from 'os';
|
||||
|
||||
/**
|
||||
* Copy from https://github.com/sindresorhus/internal-ip, to fi xsilverwind/default-gateway 's bug
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { app } from 'electron';
|
||||
import { logger } from '@services/libs/log';
|
||||
import { app } from 'electron';
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export function extractDomain(fullUrl: string | undefined): string | undefined {
|
||||
const matches = /^([a-zA-Z\-]+):\/\/([^#/?]+)(?:[#/?]|$)/i.exec(fullUrl ?? '');
|
||||
const domain = matches !== null ? matches[1] : undefined;
|
||||
const matches = /^([a-z\-]+):\/\/([^#/?]+)(?:[#/?]|$)/i.exec(fullUrl ?? '');
|
||||
const domain = matches === null ? undefined : matches[1];
|
||||
// https://stackoverflow.com/a/9928725
|
||||
return typeof domain === 'string' ? domain.replace(/^(www\.)/, '') : undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { AsyncReturnType } from 'type-fest';
|
||||
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { AsyncReturnType } from 'type-fest';
|
||||
|
||||
/**
|
||||
* Use value from service, especially constant value that never changes
|
||||
|
|
|
|||
35
src/main.ts
35
src/main.ts
|
|
@ -1,34 +1,34 @@
|
|||
/* eslint-disable unicorn/prefer-top-level-await */
|
||||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
import { uninstall } from './helpers/installV8Cache';
|
||||
import 'source-map-support/register';
|
||||
import 'reflect-metadata';
|
||||
import './helpers/singleInstance';
|
||||
import './helpers/configSetting';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { ipcMain, protocol, powerMonitor, app } from 'electron';
|
||||
import { app, ipcMain, powerMonitor, protocol } from 'electron';
|
||||
import settings from 'electron-settings';
|
||||
import unhandled from 'electron-unhandled';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
import { buildLanguageMenu } from '@services/menu/buildLanguageMenu';
|
||||
import { MainChannel } from '@/constants/channels';
|
||||
import { isTest } from '@/constants/environment';
|
||||
import { container } from '@services/container';
|
||||
import { logger } from '@services/libs/log';
|
||||
import { initRendererI18NHandler } from '@services/libs/i18n';
|
||||
import { logger } from '@services/libs/log';
|
||||
import { buildLanguageMenu } from '@services/menu/buildLanguageMenu';
|
||||
|
||||
import { bindServiceAndProxy } from '@services/libs/bindServiceAndProxy';
|
||||
import serviceIdentifier from '@services/serviceIdentifier';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { bindServiceAndProxy } from '@services/libs/bindServiceAndProxy';
|
||||
|
||||
import type { IPreferenceService } from './services/preferences/interface';
|
||||
import type { IWikiService } from './services/wiki/interface';
|
||||
import type { IWindowService } from './services/windows/interface';
|
||||
import type { IWorkspaceViewService } from './services/workspacesView/interface';
|
||||
import type { IUpdaterService } from '@services/updater/interface';
|
||||
import { reportErrorToGithubWithTemplates } from '@services/native/reportError';
|
||||
import type { IUpdaterService } from '@services/updater/interface';
|
||||
import { IWikiGitWorkspaceService } from '@services/wikiGitWorkspace/interface';
|
||||
import { isLinux, isMac } from './helpers/system';
|
||||
import type { IPreferenceService } from './services/preferences/interface';
|
||||
import type { IWindowService } from './services/windows/interface';
|
||||
import type { IWorkspaceViewService } from './services/workspacesView/interface';
|
||||
|
||||
logger.info('App booting');
|
||||
|
||||
|
|
@ -82,9 +82,10 @@ ipcMain.once(MainChannel.commonInitFinished, () => {
|
|||
*/
|
||||
const whenCommonInitFinished = async (): Promise<void> => {
|
||||
if (commonInitFinished) {
|
||||
return await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
return;
|
||||
}
|
||||
return await new Promise((resolve) => {
|
||||
await new Promise<void>((resolve) => {
|
||||
ipcMain.once(MainChannel.commonInitFinished, () => {
|
||||
commonInitFinished = true;
|
||||
resolve();
|
||||
|
|
@ -112,8 +113,8 @@ const commonInit = async (): Promise<void> => {
|
|||
// if user want a menubar, we create a new window for that
|
||||
await Promise.all([
|
||||
windowService.open(WindowNames.main),
|
||||
preferenceService.get('attachToMenubar').then((attachToMenubar) => {
|
||||
attachToMenubar && windowService.open(WindowNames.menuBar);
|
||||
preferenceService.get('attachToMenubar').then(async (attachToMenubar) => {
|
||||
attachToMenubar && await windowService.open(WindowNames.menuBar);
|
||||
}),
|
||||
]);
|
||||
// perform wiki startup and git sync for each workspace
|
||||
|
|
@ -160,7 +161,9 @@ app.on('ready', async () => {
|
|||
}
|
||||
await updaterService.checkForUpdates();
|
||||
})
|
||||
.catch((error) => console.error(error));
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
powerMonitor.on('shutdown', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Button, DialogContent as DialogContentRaw } from '@material-ui/core';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { Button, DialogContent as DialogContentRaw } from '@material-ui/core';
|
||||
import iconPath from '../../build-resources/icon.png';
|
||||
|
||||
const DialogContent = styled(DialogContentRaw)`
|
||||
|
|
@ -81,11 +81,11 @@ export default function About(): JSX.Element {
|
|||
|
||||
return (
|
||||
<DialogContent>
|
||||
<div id="test" data-usage="For spectron automating testing" />
|
||||
<div id='test' data-usage='For spectron automating testing' />
|
||||
<Helmet>
|
||||
<title>{t('ContextMenu.About')}</title>
|
||||
</Helmet>
|
||||
<Icon src={iconPath} alt="TidGi" />
|
||||
<Icon src={iconPath} alt='TidGi' />
|
||||
<Title>TidGi ({platform ?? 'Unknown Platform'})</Title>
|
||||
<TidGiVersion>{`Version v${appVersion ?? ' - '}.`}</TidGiVersion>
|
||||
<DependenciesVersionsContainer>
|
||||
|
|
@ -97,45 +97,59 @@ export default function About(): JSX.Element {
|
|||
</DependenciesVersionsContainer>
|
||||
|
||||
<ButtonContainer>
|
||||
<GoToTheWebsiteButton onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TidGi-Desktop')}>
|
||||
<GoToTheWebsiteButton
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://github.com/tiddly-gittly/TidGi-Desktop');
|
||||
}}
|
||||
>
|
||||
Website
|
||||
</GoToTheWebsiteButton>
|
||||
<GoToTheWebsiteButton onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TidGi-Desktop/issues/new/choose')}>
|
||||
<GoToTheWebsiteButton
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://github.com/tiddly-gittly/TidGi-Desktop/issues/new/choose');
|
||||
}}
|
||||
>
|
||||
Support
|
||||
</GoToTheWebsiteButton>
|
||||
</ButtonContainer>
|
||||
|
||||
<MadeBy>
|
||||
<Trans t={t} i18nKey="Dialog.MadeWithLove">
|
||||
<span>Made with </span>
|
||||
<span role="img" aria-label="love">
|
||||
<Trans t={t} i18nKey='Dialog.MadeWithLove'>
|
||||
<span>Made with</span>
|
||||
<span role='img' aria-label='love'>
|
||||
❤
|
||||
</span>
|
||||
<span> by </span>
|
||||
<span>by</span>
|
||||
</Trans>
|
||||
<Link
|
||||
onClick={async () => await window.service.native.open('https://onetwo.ren/wiki/')}
|
||||
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}>
|
||||
role='link'
|
||||
tabIndex={0}
|
||||
>
|
||||
{t('LinOnetwo')}
|
||||
</Link>
|
||||
<span> && </span>
|
||||
<span>&&</span>
|
||||
<Link
|
||||
onClick={async () => await window.service.native.open('https://webcatalog.app/?utm_source=tidgi_app')}
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://webcatalog.app/?utm_source=tidgi_app');
|
||||
}}
|
||||
onKeyDown={async (event) => {
|
||||
if (event.key !== 'Enter') {
|
||||
return;
|
||||
}
|
||||
await window.service.native.open('https://webcatalog.app/?utm_source=tidgi_app');
|
||||
}}
|
||||
role="link"
|
||||
tabIndex={0}>
|
||||
role='link'
|
||||
tabIndex={0}
|
||||
>
|
||||
{t('Preference.WebCatalog')}
|
||||
</Link>
|
||||
</MadeBy>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Typography, LinearProgress, Snackbar } from '@material-ui/core';
|
||||
import { LinearProgress, Snackbar, Typography } from '@material-ui/core';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
|
||||
import { CloseButton, ReportErrorFabButton, WikiLocation } from './FormComponents';
|
||||
import { useCloneWiki, useValidateCloneWiki } from './useCloneWiki';
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
import { useValidateCloneWiki, useCloneWiki } from './useCloneWiki';
|
||||
import { useWikiCreationProgress } from './useIndicator';
|
||||
import { WikiLocation, CloseButton, ReportErrorFabButton } from './FormComponents';
|
||||
|
||||
export function CloneWikiDoneButton({ form, isCreateMainWorkspace, errorInWhichComponentSetter }: IWikiWorkspaceFormProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -21,7 +21,7 @@ export function CloneWikiDoneButton({ form, isCreateMainWorkspace, errorInWhichC
|
|||
if (hasError) {
|
||||
return (
|
||||
<>
|
||||
<CloseButton variant="contained" disabled>
|
||||
<CloseButton variant='contained' disabled>
|
||||
{wikiCreationMessage}
|
||||
</CloseButton>
|
||||
{wikiCreationMessage !== undefined && <ReportErrorFabButton message={wikiCreationMessage} />}
|
||||
|
|
@ -30,29 +30,37 @@ export function CloneWikiDoneButton({ form, isCreateMainWorkspace, errorInWhichC
|
|||
}
|
||||
return (
|
||||
<>
|
||||
{inProgressOrError && <LinearProgress color="secondary" />}
|
||||
<Snackbar open={logPanelOpened} autoHideDuration={5000} onClose={() => logPanelSetter(false)}>
|
||||
<Alert severity="info">{wikiCreationMessage}</Alert>
|
||||
{inProgressOrError && <LinearProgress color='secondary' />}
|
||||
<Snackbar
|
||||
open={logPanelOpened}
|
||||
autoHideDuration={5000}
|
||||
onClose={() => {
|
||||
logPanelSetter(false);
|
||||
}}
|
||||
>
|
||||
<Alert severity='info'>{wikiCreationMessage}</Alert>
|
||||
</Snackbar>
|
||||
|
||||
{isCreateMainWorkspace ? (
|
||||
<CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.CloneWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
</CloseButton>
|
||||
) : (
|
||||
<CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.CloneWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.AndLinkToMainWorkspace')}
|
||||
</Typography>
|
||||
</CloseButton>
|
||||
)}
|
||||
{isCreateMainWorkspace
|
||||
? (
|
||||
<CloseButton variant='contained' color='secondary' disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.CloneWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
</CloseButton>
|
||||
)
|
||||
: (
|
||||
<CloseButton variant='contained' color='secondary' disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.CloneWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.AndLinkToMainWorkspace')}
|
||||
</Typography>
|
||||
</CloseButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,13 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import React, { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Typography, MenuItem } from '@material-ui/core';
|
||||
import { MenuItem, Typography } from '@material-ui/core';
|
||||
import { Folder as FolderIcon } from '@material-ui/icons';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
CreateContainer,
|
||||
LocationPickerContainer,
|
||||
LocationPickerInput,
|
||||
LocationPickerButton,
|
||||
SoftLinkToMainWikiSelect,
|
||||
SubWikiTagAutoComplete,
|
||||
} from './FormComponents';
|
||||
import { CreateContainer, LocationPickerButton, LocationPickerContainer, LocationPickerInput, SoftLinkToMainWikiSelect, SubWikiTagAutoComplete } from './FormComponents';
|
||||
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
import { useValidateCloneWiki } from './useCloneWiki';
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
|
||||
export function CloneWikiForm({ form, isCreateMainWorkspace, errorInWhichComponent, errorInWhichComponentSetter }: IWikiWorkspaceFormProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -24,7 +17,9 @@ export function CloneWikiForm({ form, isCreateMainWorkspace, errorInWhichCompone
|
|||
<LocationPickerContainer>
|
||||
<LocationPickerInput
|
||||
error={errorInWhichComponent.parentFolderLocation}
|
||||
onChange={(event) => form.parentFolderLocationSetter(event.target.value)}
|
||||
onChange={(event) => {
|
||||
form.parentFolderLocationSetter(event.target.value);
|
||||
}}
|
||||
label={t('AddWorkspace.WorkspaceParentFolder')}
|
||||
value={form.parentFolderLocation}
|
||||
/>
|
||||
|
|
@ -37,8 +32,9 @@ export function CloneWikiForm({ form, isCreateMainWorkspace, errorInWhichCompone
|
|||
form.parentFolderLocationSetter(filePaths[0]);
|
||||
}
|
||||
}}
|
||||
endIcon={<FolderIcon />}>
|
||||
<Typography variant="button" display="inline">
|
||||
endIcon={<FolderIcon />}
|
||||
>
|
||||
<Typography variant='button' display='inline'>
|
||||
{t('AddWorkspace.Choose')}
|
||||
</Typography>
|
||||
</LocationPickerButton>
|
||||
|
|
@ -46,7 +42,9 @@ export function CloneWikiForm({ form, isCreateMainWorkspace, errorInWhichCompone
|
|||
<LocationPickerContainer>
|
||||
<LocationPickerInput
|
||||
error={errorInWhichComponent.wikiFolderName}
|
||||
onChange={(event) => form.wikiFolderNameSetter(event.target.value)}
|
||||
onChange={(event) => {
|
||||
form.wikiFolderNameSetter(event.target.value);
|
||||
}}
|
||||
label={t('AddWorkspace.WorkspaceFolderNameToCreate')}
|
||||
helperText={`${t('AddWorkspace.CloneWiki')}${form.wikiFolderLocation ?? ''}`}
|
||||
value={form.wikiFolderName}
|
||||
|
|
@ -57,16 +55,15 @@ export function CloneWikiForm({ form, isCreateMainWorkspace, errorInWhichCompone
|
|||
<SoftLinkToMainWikiSelect
|
||||
error={errorInWhichComponent.mainWikiToLink}
|
||||
label={t('AddWorkspace.MainWorkspaceLocation')}
|
||||
helperText={
|
||||
form.mainWikiToLink.wikiFolderLocation &&
|
||||
helperText={form.mainWikiToLink.wikiFolderLocation &&
|
||||
`${t('AddWorkspace.SubWorkspaceWillLinkTo')}
|
||||
${form.mainWikiToLink.wikiFolderLocation}/tiddlers/${form.wikiFolderName}`
|
||||
}
|
||||
${form.mainWikiToLink.wikiFolderLocation}/tiddlers/${form.wikiFolderName}`}
|
||||
value={form.mainWikiToLinkIndex}
|
||||
onChange={(event) => {
|
||||
const index = event.target.value as unknown as number;
|
||||
form.mainWikiToLinkSetter(form.mainWorkspaceList[index]);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{form.mainWorkspaceList.map((workspace, index) => (
|
||||
<MenuItem key={index} value={index}>
|
||||
{workspace.name}
|
||||
|
|
@ -76,7 +73,9 @@ export function CloneWikiForm({ form, isCreateMainWorkspace, errorInWhichCompone
|
|||
<SubWikiTagAutoComplete
|
||||
options={form.fileSystemPaths.map((fileSystemPath) => fileSystemPath.tagName)}
|
||||
value={form.tagName}
|
||||
onInputChange={(_, value) => form.tagNameSetter(value)}
|
||||
onInputChange={(_, value) => {
|
||||
form.tagNameSetter(value);
|
||||
}}
|
||||
renderInput={(parameters) => (
|
||||
<LocationPickerInput
|
||||
{...parameters}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import FormControlLabel from '@material-ui/core/FormControlLabel';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import SwitchRaw from '@material-ui/core/Switch';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
|
|
@ -36,10 +36,17 @@ export function MainSubWikiDescription({
|
|||
return (
|
||||
<Container elevation={0} square>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={isCreateMainWorkspace} onChange={(event) => isCreateMainWorkspaceSetter(event.target.checked)} />}
|
||||
control={
|
||||
<Switch
|
||||
checked={isCreateMainWorkspace}
|
||||
onChange={(event) => {
|
||||
isCreateMainWorkspaceSetter(event.target.checked);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={label}
|
||||
/>
|
||||
<Typography variant="body2" display="inline">
|
||||
<Typography variant='body2' display='inline'>
|
||||
{description}
|
||||
</Typography>
|
||||
</Container>
|
||||
|
|
@ -63,10 +70,17 @@ export function SyncedWikiDescription({
|
|||
return (
|
||||
<Container elevation={0} square>
|
||||
<FormControlLabel
|
||||
control={<Switch checked={isCreateSyncedWorkspace} onChange={(event) => isCreateSyncedWorkspaceSetter(event.target.checked)} />}
|
||||
control={
|
||||
<Switch
|
||||
checked={isCreateSyncedWorkspace}
|
||||
onChange={(event) => {
|
||||
isCreateSyncedWorkspaceSetter(event.target.checked);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
label={label}
|
||||
/>
|
||||
<Typography variant="body2" display="inline">
|
||||
<Typography variant='body2' display='inline'>
|
||||
{description}
|
||||
</Typography>
|
||||
</Container>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Typography, LinearProgress, Snackbar } from '@material-ui/core';
|
||||
import { LinearProgress, Snackbar, Typography } from '@material-ui/core';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
|
||||
import { CloseButton, ReportErrorFabButton, WikiLocation } from './FormComponents';
|
||||
import { useExistedWiki, useValidateExistedWiki } from './useExistedWiki';
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
import { useValidateExistedWiki, useExistedWiki } from './useExistedWiki';
|
||||
import { useWikiCreationProgress } from './useIndicator';
|
||||
import { WikiLocation, CloseButton, ReportErrorFabButton } from './FormComponents';
|
||||
|
||||
export function ExistedWikiDoneButton({
|
||||
form,
|
||||
|
|
@ -26,7 +26,7 @@ export function ExistedWikiDoneButton({
|
|||
if (hasError) {
|
||||
return (
|
||||
<>
|
||||
<CloseButton variant="contained" disabled>
|
||||
<CloseButton variant='contained' disabled>
|
||||
{wikiCreationMessage}
|
||||
</CloseButton>
|
||||
{wikiCreationMessage !== undefined && <ReportErrorFabButton message={wikiCreationMessage} />}
|
||||
|
|
@ -35,29 +35,37 @@ export function ExistedWikiDoneButton({
|
|||
}
|
||||
return (
|
||||
<>
|
||||
{inProgressOrError && <LinearProgress color="secondary" />}
|
||||
<Snackbar open={logPanelOpened} autoHideDuration={5000} onClose={() => logPanelSetter(false)}>
|
||||
<Alert severity="info">{wikiCreationMessage}</Alert>
|
||||
{inProgressOrError && <LinearProgress color='secondary' />}
|
||||
<Snackbar
|
||||
open={logPanelOpened}
|
||||
autoHideDuration={5000}
|
||||
onClose={() => {
|
||||
logPanelSetter(false);
|
||||
}}
|
||||
>
|
||||
<Alert severity='info'>{wikiCreationMessage}</Alert>
|
||||
</Snackbar>
|
||||
|
||||
{isCreateMainWorkspace ? (
|
||||
<CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.ImportWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
</CloseButton>
|
||||
) : (
|
||||
<CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.ImportWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.AndLinkToMainWorkspace')}
|
||||
</Typography>
|
||||
</CloseButton>
|
||||
)}
|
||||
{isCreateMainWorkspace
|
||||
? (
|
||||
<CloseButton variant='contained' color='secondary' disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.ImportWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
</CloseButton>
|
||||
)
|
||||
: (
|
||||
<CloseButton variant='contained' color='secondary' disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.ImportWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.AndLinkToMainWorkspace')}
|
||||
</Typography>
|
||||
</CloseButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,13 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { MenuItem, Typography } from '@material-ui/core';
|
||||
import { Folder as FolderIcon } from '@material-ui/icons';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Typography, MenuItem } from '@material-ui/core';
|
||||
import { Folder as FolderIcon } from '@material-ui/icons';
|
||||
|
||||
import {
|
||||
CreateContainer,
|
||||
LocationPickerContainer,
|
||||
LocationPickerInput,
|
||||
LocationPickerButton,
|
||||
SoftLinkToMainWikiSelect,
|
||||
SubWikiTagAutoComplete,
|
||||
} from './FormComponents';
|
||||
import { CreateContainer, LocationPickerButton, LocationPickerContainer, LocationPickerInput, SoftLinkToMainWikiSelect, SubWikiTagAutoComplete } from './FormComponents';
|
||||
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
import { useValidateExistedWiki } from './useExistedWiki';
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
|
||||
export function ExistedWikiForm({
|
||||
form,
|
||||
|
|
@ -73,8 +66,9 @@ export function ExistedWikiForm({
|
|||
onLocationChange(filePaths[0]);
|
||||
}
|
||||
}}
|
||||
endIcon={<FolderIcon />}>
|
||||
<Typography variant="button" display="inline">
|
||||
endIcon={<FolderIcon />}
|
||||
>
|
||||
<Typography variant='button' display='inline'>
|
||||
{t('AddWorkspace.Choose')}
|
||||
</Typography>
|
||||
</LocationPickerButton>
|
||||
|
|
@ -84,16 +78,15 @@ export function ExistedWikiForm({
|
|||
<SoftLinkToMainWikiSelect
|
||||
error={errorInWhichComponent.mainWikiToLink}
|
||||
label={t('AddWorkspace.MainWorkspaceLocation')}
|
||||
helperText={
|
||||
mainWikiToLink.wikiFolderLocation &&
|
||||
helperText={mainWikiToLink.wikiFolderLocation &&
|
||||
`${t('AddWorkspace.SubWorkspaceWillLinkTo')}
|
||||
${mainWikiToLink.wikiFolderLocation}/tiddlers/${wikiFolderName}`
|
||||
}
|
||||
${mainWikiToLink.wikiFolderLocation}/tiddlers/${wikiFolderName}`}
|
||||
value={mainWikiToLinkIndex}
|
||||
onChange={(event) => {
|
||||
const index = event.target.value as unknown as number;
|
||||
mainWikiToLinkSetter(mainWorkspaceList[index]);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{mainWorkspaceList.map((workspace, index) => (
|
||||
<MenuItem key={index} value={index}>
|
||||
{workspace.name}
|
||||
|
|
@ -103,7 +96,9 @@ export function ExistedWikiForm({
|
|||
<SubWikiTagAutoComplete
|
||||
options={fileSystemPaths.map((fileSystemPath) => fileSystemPath.tagName)}
|
||||
value={tagName}
|
||||
onInputChange={(_, value) => tagNameSetter(value)}
|
||||
onInputChange={(_, value) => {
|
||||
tagNameSetter(value);
|
||||
}}
|
||||
renderInput={(parameters) => (
|
||||
<LocationPickerInput
|
||||
{...parameters}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import styled, { css } from 'styled-components';
|
||||
import { Paper, Button, TextField, Autocomplete, Typography, Fab, Tooltip } from '@material-ui/core';
|
||||
import { Autocomplete, Button, Fab, Paper, TextField, Tooltip, Typography } from '@material-ui/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
export const CreateContainer = styled(Paper)`
|
||||
padding: 10px;
|
||||
|
|
@ -28,9 +28,9 @@ export const LocationPickerButton = styled(Button)`
|
|||
`;
|
||||
export const CloseButton = styled(Button)`
|
||||
${({ disabled }) =>
|
||||
disabled === true
|
||||
? ''
|
||||
: css`
|
||||
disabled === true
|
||||
? ''
|
||||
: css`
|
||||
white-space: nowrap;
|
||||
`}
|
||||
width: 100%;
|
||||
|
|
@ -61,12 +61,13 @@ export function ReportErrorButton(props: { message: string }): JSX.Element {
|
|||
return (
|
||||
<Tooltip title={(t('Dialog.ReportBugDetail') ?? '') + (t('Menu.ReportBugViaGithub') ?? '')}>
|
||||
<Button
|
||||
color="secondary"
|
||||
color='secondary'
|
||||
onClick={() => {
|
||||
const error = new Error(props.message);
|
||||
error.stack = 'ReportErrorButton';
|
||||
void window.service.native.openNewGitHubIssue(error);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t('Dialog.ReportBug')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
|
@ -85,12 +86,13 @@ export function ReportErrorFabButton(props: { message: string }): JSX.Element {
|
|||
return (
|
||||
<Tooltip title={(t('Dialog.ReportBugDetail') ?? '') + (t('Menu.ReportBugViaGithub') ?? '')}>
|
||||
<AbsoluteFab
|
||||
color="default"
|
||||
color='default'
|
||||
onClick={() => {
|
||||
const error = new Error(props.message);
|
||||
error.stack = 'ReportErrorButton';
|
||||
void window.service.native.openNewGitHubIssue(error);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{t('Dialog.ReportBug')}
|
||||
</AbsoluteFab>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,14 @@ export function GitRepoUrlForm({
|
|||
return (
|
||||
<CreateContainer elevation={2} square>
|
||||
<LocationPickerContainer>
|
||||
<LocationPickerInput error={error} onChange={(event) => gitRepoUrlSetter(event.target.value)} label={t('AddWorkspace.GitRepoUrl')} value={gitRepoUrl} />
|
||||
<LocationPickerInput
|
||||
error={error}
|
||||
onChange={(event) => {
|
||||
gitRepoUrlSetter(event.target.value);
|
||||
}}
|
||||
label={t('AddWorkspace.GitRepoUrl')}
|
||||
value={gitRepoUrl}
|
||||
/>
|
||||
</LocationPickerContainer>
|
||||
{storageProvider === SupportedStorageServices.github && (
|
||||
<SearchGithubRepo
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Typography, LinearProgress, Snackbar } from '@material-ui/core';
|
||||
import { LinearProgress, Snackbar, Typography } from '@material-ui/core';
|
||||
import { CloseButton, ReportErrorFabButton, WikiLocation } from './FormComponents';
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
import { useValidateHtmlWiki, useImportHtmlWiki } from './useImportHtmlWiki';
|
||||
import { useImportHtmlWiki, useValidateHtmlWiki } from './useImportHtmlWiki';
|
||||
import { useWikiCreationProgress } from './useIndicator';
|
||||
import { WikiLocation, CloseButton, ReportErrorFabButton } from './FormComponents';
|
||||
|
||||
export function ImportHtmlWikiDoneButton({
|
||||
form,
|
||||
|
|
@ -33,7 +33,7 @@ export function ImportHtmlWikiDoneButton({
|
|||
if (hasError) {
|
||||
return (
|
||||
<>
|
||||
<CloseButton variant="contained" disabled>
|
||||
<CloseButton variant='contained' disabled>
|
||||
{wikiCreationMessage}
|
||||
</CloseButton>
|
||||
{wikiCreationMessage !== undefined && <ReportErrorFabButton message={wikiCreationMessage} />}
|
||||
|
|
@ -42,14 +42,20 @@ export function ImportHtmlWikiDoneButton({
|
|||
}
|
||||
return (
|
||||
<>
|
||||
{inProgressOrError && <LinearProgress color="secondary" />}
|
||||
{inProgressOrError && <LinearProgress color='secondary' />}
|
||||
{/* 这个好像是log面板 */}
|
||||
<Snackbar open={logPanelOpened} autoHideDuration={5000} onClose={() => logPanelSetter(false)}>
|
||||
<Alert severity="info">{wikiCreationMessage}</Alert>
|
||||
<Snackbar
|
||||
open={logPanelOpened}
|
||||
autoHideDuration={5000}
|
||||
onClose={() => {
|
||||
logPanelSetter(false);
|
||||
}}
|
||||
>
|
||||
<Alert severity='info'>{wikiCreationMessage}</Alert>
|
||||
</Snackbar>
|
||||
|
||||
<CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant="body1" display="inline">
|
||||
<CloseButton variant='contained' color='secondary' disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.ImportWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiHtmlPath}</WikiLocation>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import { Folder as FolderIcon } from '@material-ui/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useValidateHtmlWiki } from './useImportHtmlWiki';
|
||||
|
||||
import { CreateContainer, LocationPickerContainer, LocationPickerInput, LocationPickerButton } from './FormComponents';
|
||||
import { CreateContainer, LocationPickerButton, LocationPickerContainer, LocationPickerInput } from './FormComponents';
|
||||
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
|
||||
|
|
@ -45,8 +45,9 @@ export function ImportHtmlWikiForm({
|
|||
}
|
||||
}
|
||||
}}
|
||||
endIcon={<FolderIcon />}>
|
||||
<Typography variant="button" display="inline">
|
||||
endIcon={<FolderIcon />}
|
||||
>
|
||||
<Typography variant='button' display='inline'>
|
||||
{t('AddWorkspace.Choose')}
|
||||
</Typography>
|
||||
</LocationPickerButton>
|
||||
|
|
@ -54,7 +55,9 @@ export function ImportHtmlWikiForm({
|
|||
<LocationPickerContainer>
|
||||
<LocationPickerInput
|
||||
error={errorInWhichComponent.parentFolderLocation}
|
||||
onChange={(event) => form.parentFolderLocationSetter(event.target.value)}
|
||||
onChange={(event) => {
|
||||
form.parentFolderLocationSetter(event.target.value);
|
||||
}}
|
||||
label={t('AddWorkspace.WorkspaceParentFolder')}
|
||||
value={parentFolderLocation}
|
||||
/>
|
||||
|
|
@ -67,8 +70,9 @@ export function ImportHtmlWikiForm({
|
|||
form.parentFolderLocationSetter(filePaths[0]);
|
||||
}
|
||||
}}
|
||||
endIcon={<FolderIcon />}>
|
||||
<Typography variant="button" display="inline">
|
||||
endIcon={<FolderIcon />}
|
||||
>
|
||||
<Typography variant='button' display='inline'>
|
||||
{t('AddWorkspace.Choose')}
|
||||
</Typography>
|
||||
</LocationPickerButton>
|
||||
|
|
@ -76,7 +80,9 @@ export function ImportHtmlWikiForm({
|
|||
<LocationPickerContainer>
|
||||
<LocationPickerInput
|
||||
error={errorInWhichComponent.wikiFolderName}
|
||||
onChange={(event) => wikiFolderNameSetter(event.target.value)}
|
||||
onChange={(event) => {
|
||||
wikiFolderNameSetter(event.target.value);
|
||||
}}
|
||||
label={t('AddWorkspace.ExtractedWikiFolderName')}
|
||||
helperText={`${t('AddWorkspace.CreateWiki')}${wikiFolderLocation ?? ''}`}
|
||||
value={wikiFolderName}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Typography, LinearProgress, Snackbar } from '@material-ui/core';
|
||||
import { LinearProgress, Snackbar, Typography } from '@material-ui/core';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
|
||||
import { CloseButton, ReportErrorFabButton, WikiLocation } from './FormComponents';
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
import { useValidateNewWiki, useNewWiki } from './useNewWiki';
|
||||
import { useWikiCreationProgress } from './useIndicator';
|
||||
import { WikiLocation, CloseButton, ReportErrorFabButton } from './FormComponents';
|
||||
import { useNewWiki, useValidateNewWiki } from './useNewWiki';
|
||||
|
||||
export function NewWikiDoneButton({
|
||||
form,
|
||||
|
|
@ -27,7 +27,7 @@ export function NewWikiDoneButton({
|
|||
if (hasError) {
|
||||
return (
|
||||
<>
|
||||
<CloseButton variant="contained" disabled={true}>
|
||||
<CloseButton variant='contained' disabled={true}>
|
||||
{wikiCreationMessage}
|
||||
</CloseButton>
|
||||
{wikiCreationMessage !== undefined && <ReportErrorFabButton message={wikiCreationMessage} />}
|
||||
|
|
@ -36,29 +36,37 @@ export function NewWikiDoneButton({
|
|||
}
|
||||
return (
|
||||
<>
|
||||
{inProgressOrError && <LinearProgress color="secondary" />}
|
||||
<Snackbar open={logPanelOpened} autoHideDuration={5000} onClose={() => logPanelSetter(false)}>
|
||||
<Alert severity="info">{wikiCreationMessage}</Alert>
|
||||
{inProgressOrError && <LinearProgress color='secondary' />}
|
||||
<Snackbar
|
||||
open={logPanelOpened}
|
||||
autoHideDuration={5000}
|
||||
onClose={() => {
|
||||
logPanelSetter(false);
|
||||
}}
|
||||
>
|
||||
<Alert severity='info'>{wikiCreationMessage}</Alert>
|
||||
</Snackbar>
|
||||
|
||||
{isCreateMainWorkspace ? (
|
||||
<CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.CreateWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
</CloseButton>
|
||||
) : (
|
||||
<CloseButton variant="contained" color="secondary" disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.CreateWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
<Typography variant="body1" display="inline">
|
||||
{t('AddWorkspace.AndLinkToMainWorkspace')}
|
||||
</Typography>
|
||||
</CloseButton>
|
||||
)}
|
||||
{isCreateMainWorkspace
|
||||
? (
|
||||
<CloseButton variant='contained' color='secondary' disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.CreateWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
</CloseButton>
|
||||
)
|
||||
: (
|
||||
<CloseButton variant='contained' color='secondary' disabled={inProgressOrError} onClick={onSubmit}>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.CreateWiki')}
|
||||
</Typography>
|
||||
<WikiLocation>{form.wikiFolderLocation}</WikiLocation>
|
||||
<Typography variant='body1' display='inline'>
|
||||
{t('AddWorkspace.AndLinkToMainWorkspace')}
|
||||
</Typography>
|
||||
</CloseButton>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,9 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Typography, MenuItem } from '@material-ui/core';
|
||||
import { MenuItem, Typography } from '@material-ui/core';
|
||||
import { Folder as FolderIcon } from '@material-ui/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
CreateContainer,
|
||||
LocationPickerContainer,
|
||||
LocationPickerInput,
|
||||
LocationPickerButton,
|
||||
SoftLinkToMainWikiSelect,
|
||||
SubWikiTagAutoComplete,
|
||||
} from './FormComponents';
|
||||
import { CreateContainer, LocationPickerButton, LocationPickerContainer, LocationPickerInput, SoftLinkToMainWikiSelect, SubWikiTagAutoComplete } from './FormComponents';
|
||||
|
||||
import type { IWikiWorkspaceFormProps } from './useForm';
|
||||
import { useValidateNewWiki } from './useNewWiki';
|
||||
|
|
@ -29,7 +22,9 @@ export function NewWikiForm({
|
|||
<LocationPickerContainer>
|
||||
<LocationPickerInput
|
||||
error={errorInWhichComponent.parentFolderLocation}
|
||||
onChange={(event) => form.parentFolderLocationSetter(event.target.value)}
|
||||
onChange={(event) => {
|
||||
form.parentFolderLocationSetter(event.target.value);
|
||||
}}
|
||||
label={t('AddWorkspace.WorkspaceParentFolder')}
|
||||
value={form.parentFolderLocation}
|
||||
/>
|
||||
|
|
@ -42,8 +37,9 @@ export function NewWikiForm({
|
|||
form.parentFolderLocationSetter(filePaths[0]);
|
||||
}
|
||||
}}
|
||||
endIcon={<FolderIcon />}>
|
||||
<Typography variant="button" display="inline">
|
||||
endIcon={<FolderIcon />}
|
||||
>
|
||||
<Typography variant='button' display='inline'>
|
||||
{t('AddWorkspace.Choose')}
|
||||
</Typography>
|
||||
</LocationPickerButton>
|
||||
|
|
@ -51,7 +47,9 @@ export function NewWikiForm({
|
|||
<LocationPickerContainer>
|
||||
<LocationPickerInput
|
||||
error={errorInWhichComponent.wikiFolderName}
|
||||
onChange={(event) => form.wikiFolderNameSetter(event.target.value)}
|
||||
onChange={(event) => {
|
||||
form.wikiFolderNameSetter(event.target.value);
|
||||
}}
|
||||
label={t('AddWorkspace.WorkspaceFolderNameToCreate')}
|
||||
helperText={`${t('AddWorkspace.CreateWiki')}${form.wikiFolderLocation ?? ''}`}
|
||||
value={form.wikiFolderName}
|
||||
|
|
@ -72,7 +70,8 @@ export function NewWikiForm({
|
|||
onChange={(event) => {
|
||||
const index = event.target.value as unknown as number;
|
||||
form.mainWikiToLinkSetter(form.mainWorkspaceList[index]);
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{form.mainWorkspaceList.map((workspace, index) => (
|
||||
<MenuItem key={index} value={index}>
|
||||
{workspace.name}
|
||||
|
|
@ -82,7 +81,9 @@ export function NewWikiForm({
|
|||
<SubWikiTagAutoComplete
|
||||
options={form.fileSystemPaths.map((fileSystemPath) => fileSystemPath.tagName)}
|
||||
value={form.tagName}
|
||||
onInputChange={(_, value) => form.tagNameSetter(value)}
|
||||
onInputChange={(_, value) => {
|
||||
form.tagNameSetter(value);
|
||||
}}
|
||||
renderInput={(parameters) => (
|
||||
<LocationPickerInput
|
||||
error={errorInWhichComponent.tagName}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,29 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Accordion as AccordionRaw, AccordionSummary, AccordionDetails, AppBar, Paper as PaperRaw, Tab as TabRaw } from '@material-ui/core';
|
||||
import { TabPanel as TabPanelRaw, TabContext, TabList as TabListRaw } from '@material-ui/lab';
|
||||
import { Accordion as AccordionRaw, AccordionDetails, AccordionSummary, AppBar, Paper as PaperRaw, Tab as TabRaw } from '@material-ui/core';
|
||||
import { ExpandMore as ExpandMoreIcon } from '@material-ui/icons';
|
||||
import { TabContext, TabList as TabListRaw, TabPanel as TabPanelRaw } from '@material-ui/lab';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
|
||||
import { MainSubWikiDescription, SyncedWikiDescription } from './Description';
|
||||
|
||||
import { NewWikiForm } from './NewWikiForm';
|
||||
import { NewWikiDoneButton } from './NewWikiDoneButton';
|
||||
import { ExistedWikiForm } from './ExistedWikiForm';
|
||||
import { ExistedWikiDoneButton } from './ExistedWikiDoneButton';
|
||||
import { CloneWikiForm } from './CloneWikiForm';
|
||||
import { CloneWikiDoneButton } from './CloneWikiDoneButton';
|
||||
import { IErrorInWhichComponent, useIsCreateSyncedWorkspace, useWikiWorkspaceForm } from './useForm';
|
||||
import { CloneWikiForm } from './CloneWikiForm';
|
||||
import { ExistedWikiDoneButton } from './ExistedWikiDoneButton';
|
||||
import { ExistedWikiForm } from './ExistedWikiForm';
|
||||
import { NewWikiDoneButton } from './NewWikiDoneButton';
|
||||
import { NewWikiForm } from './NewWikiForm';
|
||||
import { IErrorInWhichComponent, useWikiWorkspaceForm } from './useForm';
|
||||
|
||||
import { TokenForm } from '@/components/TokenForm';
|
||||
import { GitRepoUrlForm } from './GitRepoUrlForm';
|
||||
import { LocationPickerContainer, LocationPickerInput } from './FormComponents';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { ImportHtmlWikiForm } from './ImportHtmlWikiForm';
|
||||
import { LocationPickerContainer, LocationPickerInput } from './FormComponents';
|
||||
import { GitRepoUrlForm } from './GitRepoUrlForm';
|
||||
import { ImportHtmlWikiDoneButton } from './ImportHtmlWikiDoneButton';
|
||||
import { ImportHtmlWikiForm } from './ImportHtmlWikiForm';
|
||||
|
||||
enum CreateWorkspaceTabs {
|
||||
CloneOnlineWiki = 'CloneOnlineWiki',
|
||||
|
|
@ -108,19 +108,22 @@ export function AddWorkspace(): JSX.Element {
|
|||
|
||||
return (
|
||||
<TabContext value={currentTab}>
|
||||
<div id="test" data-usage="For spectron automating testing" />
|
||||
<div id='test' data-usage='For spectron automating testing' />
|
||||
<Helmet>
|
||||
<title>
|
||||
{t('AddWorkspace.AddWorkspace')} {wikiFolderName}
|
||||
</title>
|
||||
</Helmet>
|
||||
<AppBar position="static">
|
||||
<AppBar position='static'>
|
||||
<Paper square>
|
||||
<TabList
|
||||
onChange={(_event, newValue) => currentTabSetter(newValue as CreateWorkspaceTabs)}
|
||||
variant="scrollable"
|
||||
onChange={(_event, newValue) => {
|
||||
currentTabSetter(newValue as CreateWorkspaceTabs);
|
||||
}}
|
||||
variant='scrollable'
|
||||
value={currentTab}
|
||||
aria-label={t('AddWorkspace.SwitchCreateNewOrOpenExisted')}>
|
||||
aria-label={t('AddWorkspace.SwitchCreateNewOrOpenExisted')}
|
||||
>
|
||||
<Tab label={t('AddWorkspace.CreateNewWiki')} value={CreateWorkspaceTabs.CreateNewWiki} />
|
||||
<Tab label={t(`AddWorkspace.CloneOnlineWiki`)} value={CreateWorkspaceTabs.CloneOnlineWiki} />
|
||||
<Tab label={t('AddWorkspace.OpenLocalWiki')} value={CreateWorkspaceTabs.OpenLocalWiki} />
|
||||
|
|
|
|||
|
|
@ -74,9 +74,9 @@ export function useExistedWiki(
|
|||
const parentFolderLocationForExistedFolder = await window.service.native.path('dirname', form.wikiFolderLocation);
|
||||
if (!wikiFolderNameForExistedFolder || !parentFolderLocationForExistedFolder) {
|
||||
throw new Error(
|
||||
`Undefined folder name: parentFolderLocationForExistedFolder: ${
|
||||
`Undefined folder name: parentFolderLocationForExistedFolder: ${parentFolderLocationForExistedFolder ?? '-'}, parentFolderLocationForExistedFolder: ${
|
||||
parentFolderLocationForExistedFolder ?? '-'
|
||||
}, parentFolderLocationForExistedFolder: ${parentFolderLocationForExistedFolder ?? '-'}`,
|
||||
}`,
|
||||
);
|
||||
}
|
||||
await window.service.wiki.ensureWikiExist(form.wikiFolderLocation, false);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ import type { INewWikiRequiredFormData } from './useNewWiki';
|
|||
export function useIsCreateSyncedWorkspace(): [boolean, React.Dispatch<React.SetStateAction<boolean>>] {
|
||||
const [isCreateSyncedWorkspace, isCreateSyncedWorkspaceSetter] = useState(false);
|
||||
useEffect(() => {
|
||||
void window.service.auth.getRandomStorageServiceUserInfo().then((result) => isCreateSyncedWorkspaceSetter(result !== undefined));
|
||||
void window.service.auth.getRandomStorageServiceUserInfo().then((result) => {
|
||||
isCreateSyncedWorkspaceSetter(result !== undefined);
|
||||
});
|
||||
}, []);
|
||||
return [isCreateSyncedWorkspace, isCreateSyncedWorkspaceSetter];
|
||||
}
|
||||
|
|
@ -29,7 +31,9 @@ export function useWikiWorkspaceForm(options?: { fromExisted: boolean }) {
|
|||
const [wikiPort, wikiPortSetter] = useState(5212);
|
||||
useEffect(() => {
|
||||
// only update default port on component mount
|
||||
void window.service.workspace.countWorkspaces().then((workspaceCount) => wikiPortSetter(wikiPort + workspaceCount));
|
||||
void window.service.workspace.countWorkspaces().then((workspaceCount) => {
|
||||
wikiPortSetter(wikiPort + workspaceCount);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
|
|
@ -79,7 +83,7 @@ export function useWikiWorkspaceForm(options?: { fromExisted: boolean }) {
|
|||
useEffect(() => {
|
||||
void (async function getDefaultExistedWikiFolderPathEffect() {
|
||||
const desktopPathAsDefaultExistedWikiFolderPath = await window.service.context.get('DEFAULT_WIKI_FOLDER');
|
||||
wikiFolderNameSetter(mainWorkspaceList[mainWorkspaceList.length - 1]?.wikiFolderLocation ?? 'wiki');
|
||||
wikiFolderNameSetter(mainWorkspaceList.at(-1)?.wikiFolderLocation ?? 'wiki');
|
||||
parentFolderLocationSetter(desktopPathAsDefaultExistedWikiFolderPath);
|
||||
})();
|
||||
// we only do this on component init
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { useCallback, useEffect, useState } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { IErrorInWhichComponent, IWikiWorkspaceForm } from './useForm';
|
||||
import { updateErrorInWhichComponentSetterByErrorMessage } from './useIndicator';
|
||||
import { useValidateNewWiki, useNewWiki } from './useNewWiki';
|
||||
import { useNewWiki, useValidateNewWiki } from './useNewWiki';
|
||||
|
||||
export function useValidateHtmlWiki(
|
||||
isCreateMainWorkspace: boolean,
|
||||
|
|
@ -16,14 +16,14 @@ export function useValidateHtmlWiki(
|
|||
const [hasError, hasErrorSetter] = useState<boolean>(false);
|
||||
useValidateNewWiki(isCreateMainWorkspace, isCreateSyncedWorkspace, form, errorInWhichComponentSetter);
|
||||
useEffect(() => {
|
||||
if (!form.wikiHtmlPath) {
|
||||
wikiCreationMessageSetter(`${t('AddWorkspace.NotFilled')}:${t('AddWorkspace.LocalWikiHtml')}`);
|
||||
errorInWhichComponentSetter({ wikiHtmlPath: true });
|
||||
hasErrorSetter(true);
|
||||
} else {
|
||||
if (form.wikiHtmlPath) {
|
||||
wikiCreationMessageSetter('');
|
||||
errorInWhichComponentSetter({});
|
||||
hasErrorSetter(false);
|
||||
} else {
|
||||
wikiCreationMessageSetter(`${t('AddWorkspace.NotFilled')}:${t('AddWorkspace.LocalWikiHtml')}`);
|
||||
errorInWhichComponentSetter({ wikiHtmlPath: true });
|
||||
hasErrorSetter(true);
|
||||
}
|
||||
}, [t, form.wikiHtmlPath, form.parentFolderLocation, form.wikiFolderName, errorInWhichComponentSetter]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IErrorInWhichComponent } from './useForm';
|
||||
|
||||
export function useWikiCreationProgress(
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ConditionalExcept } from 'type-fest';
|
||||
import { callWikiInitialization } from './useCallWikiInitialization';
|
||||
import { IErrorInWhichComponent, IWikiWorkspaceForm, workspaceConfigFromForm } from './useForm';
|
||||
import { updateErrorInWhichComponentSetterByErrorMessage } from './useIndicator';
|
||||
import { ConditionalExcept } from 'type-fest';
|
||||
|
||||
export function useValidateNewWiki(
|
||||
isCreateMainWorkspace: boolean,
|
||||
|
|
|
|||
|
|
@ -2,42 +2,42 @@
|
|||
/* eslint-disable unicorn/no-useless-undefined */
|
||||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable unicorn/no-null */
|
||||
import React, { useCallback } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import {
|
||||
Tooltip,
|
||||
Button as ButtonRaw,
|
||||
TextField as TextFieldRaw,
|
||||
Divider,
|
||||
Link,
|
||||
List as ListRaw,
|
||||
ListItem as ListItemRaw,
|
||||
ListItemText as ListItemTextRaw,
|
||||
ListItemSecondaryAction,
|
||||
Switch,
|
||||
Typography,
|
||||
Link,
|
||||
ListItemText as ListItemTextRaw,
|
||||
Paper,
|
||||
Switch,
|
||||
TextField as TextFieldRaw,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@material-ui/core';
|
||||
import { Autocomplete } from '@material-ui/lab';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled, { css } from 'styled-components';
|
||||
import defaultIcon from '../../images/default-icon.png';
|
||||
|
||||
import type { ISubWikiPluginContent } from '@services/wiki/plugin/subWikiPlugin';
|
||||
import { WindowNames, WindowMeta } from '@services/windows/WindowProperties';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import type { ISubWikiPluginContent } from '@services/wiki/plugin/subWikiPlugin';
|
||||
import { WindowMeta, WindowNames } from '@services/windows/WindowProperties';
|
||||
import { useWorkspaceObservable } from '@services/workspaces/hooks';
|
||||
import { useForm } from './useForm';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
import { useForm } from './useForm';
|
||||
|
||||
import { useRestartSnackbar } from '@/components/RestartSnackbar';
|
||||
import { defaultServerIP } from '@/constants/urls';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { SyncedWikiDescription } from '../AddWorkspace/Description';
|
||||
import { TokenForm } from '@/components/TokenForm';
|
||||
import { GitRepoUrlForm } from '../AddWorkspace/GitRepoUrlForm';
|
||||
import { isEqual } from 'lodash';
|
||||
import { defaultServerIP } from '@/constants/urls';
|
||||
import { useActualIp } from '@services/native/hooks';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { isEqual } from 'lodash';
|
||||
import { SyncedWikiDescription } from '../AddWorkspace/Description';
|
||||
import { GitRepoUrlForm } from '../AddWorkspace/GitRepoUrlForm';
|
||||
|
||||
const Root = styled(Paper)`
|
||||
height: 100%;
|
||||
|
|
@ -88,7 +88,7 @@ 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: 85px;
|
||||
width: 85px;
|
||||
|
|
@ -103,14 +103,14 @@ const Avatar = styled.div<{ transparentBackground?: boolean }>`
|
|||
|
||||
overflow: hidden;
|
||||
${({ transparentBackground }) => {
|
||||
if (transparentBackground === true) {
|
||||
return css`
|
||||
if (transparentBackground === true) {
|
||||
return css`
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
`;
|
||||
}
|
||||
}}
|
||||
}
|
||||
}}
|
||||
`;
|
||||
const AvatarPicture = styled.img`
|
||||
height: 100%;
|
||||
|
|
@ -218,7 +218,7 @@ export default function EditWorkspace(): JSX.Element {
|
|||
const isCreateSyncedWorkspace = storageService !== SupportedStorageServices.local;
|
||||
return (
|
||||
<Root>
|
||||
<div id="test" data-usage="For spectron automating testing" />
|
||||
<div id='test' data-usage='For spectron automating testing' />
|
||||
{RestartSnackbar}
|
||||
<Helmet>
|
||||
<title>
|
||||
|
|
@ -227,21 +227,25 @@ export default function EditWorkspace(): JSX.Element {
|
|||
</Helmet>
|
||||
<FlexGrow>
|
||||
<TextField
|
||||
id="outlined-full-width"
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.Name')}
|
||||
helperText={t('EditWorkspace.NameDescription')}
|
||||
placeholder="Optional"
|
||||
placeholder='Optional'
|
||||
value={name}
|
||||
onChange={(event) => workspaceSetter({ ...workspace, name: event.target.value })}
|
||||
onChange={(event) => {
|
||||
workspaceSetter({ ...workspace, name: event.target.value });
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
id="outlined-full-width"
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.Path')}
|
||||
helperText={t('EditWorkspace.PathDescription')}
|
||||
placeholder="Optional"
|
||||
placeholder='Optional'
|
||||
disabled
|
||||
value={wikiFolderLocation}
|
||||
onChange={(event) => workspaceSetter({ ...workspace, wikiFolderLocation: event.target.value })}
|
||||
onChange={(event) => {
|
||||
workspaceSetter({ ...workspace, wikiFolderLocation: event.target.value });
|
||||
}}
|
||||
/>
|
||||
<TextField
|
||||
helperText={t('AddWorkspace.WorkspaceUserNameDetail')}
|
||||
|
|
@ -257,17 +261,22 @@ export default function EditWorkspace(): JSX.Element {
|
|||
{!isSubWiki && (
|
||||
<>
|
||||
<TextField
|
||||
id="outlined-full-width"
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.Port')}
|
||||
helperText={
|
||||
<span>
|
||||
{t('EditWorkspace.URL')}{' '}
|
||||
<Link onClick={async () => actualIP && (await window.service.native.open(actualIP))} style={{ cursor: 'pointer' }}>
|
||||
<Link
|
||||
onClick={async () => {
|
||||
actualIP && (await window.service.native.open(actualIP));
|
||||
}}
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
{actualIP}
|
||||
</Link>
|
||||
</span>
|
||||
}
|
||||
placeholder="Optional"
|
||||
placeholder='Optional'
|
||||
value={port}
|
||||
onChange={async (event) => {
|
||||
if (!Number.isNaN(Number.parseInt(event.target.value))) {
|
||||
|
|
@ -282,7 +291,7 @@ export default function EditWorkspace(): JSX.Element {
|
|||
/>
|
||||
{rememberLastPageVisited && (
|
||||
<TextField
|
||||
id="outlined-full-width"
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.LastVisitState')}
|
||||
helperText={t('Preference.RememberLastVisitState')}
|
||||
placeholder={actualIP}
|
||||
|
|
@ -312,26 +321,32 @@ export default function EditWorkspace(): JSX.Element {
|
|||
<AvatarFlex>
|
||||
<AvatarLeft>
|
||||
<Avatar transparentBackground={transparentBackground}>
|
||||
<AvatarPicture alt="Icon" src={getValidIconPath(picturePath)} />
|
||||
<AvatarPicture alt='Icon' src={getValidIconPath(picturePath)} />
|
||||
</Avatar>
|
||||
</AvatarLeft>
|
||||
<AvatarRight>
|
||||
<Tooltip title={wikiPictureExtensions.join(', ')} placement="top">
|
||||
<Tooltip title={wikiPictureExtensions.join(', ')} placement='top'>
|
||||
<PictureButton
|
||||
variant="outlined"
|
||||
size="small"
|
||||
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>
|
||||
|
||||
<Tooltip title={t('EditWorkspace.NoRevert') ?? ''} placement="bottom">
|
||||
<PictureButton onClick={() => workspaceSetter({ ...workspace, picturePath: null })} disabled={!picturePath}>
|
||||
<Tooltip title={t('EditWorkspace.NoRevert') ?? ''} placement='bottom'>
|
||||
<PictureButton
|
||||
onClick={() => {
|
||||
workspaceSetter({ ...workspace, picturePath: null });
|
||||
}}
|
||||
disabled={!picturePath}
|
||||
>
|
||||
{t('EditWorkspace.ResetDefaultIcon')}
|
||||
</PictureButton>
|
||||
</Tooltip>
|
||||
|
|
@ -371,10 +386,12 @@ export default function EditWorkspace(): JSX.Element {
|
|||
<ListItemText primary={t('EditWorkspace.SyncOnInterval')} secondary={t('EditWorkspace.SyncOnIntervalDescription')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={syncOnInterval}
|
||||
onChange={(event) => workspaceSetter({ ...workspace, syncOnInterval: event.target.checked })}
|
||||
onChange={(event) => {
|
||||
workspaceSetter({ ...workspace, syncOnInterval: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
|
|
@ -382,10 +399,12 @@ export default function EditWorkspace(): JSX.Element {
|
|||
<ListItemText primary={t('EditWorkspace.SyncOnStartup')} secondary={t('EditWorkspace.SyncOnStartupDescription')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={syncOnStartup}
|
||||
onChange={(event) => workspaceSetter({ ...workspace, syncOnStartup: event.target.checked })}
|
||||
onChange={(event) => {
|
||||
workspaceSetter({ ...workspace, syncOnStartup: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
|
|
@ -401,10 +420,12 @@ export default function EditWorkspace(): JSX.Element {
|
|||
<ListItemText primary={t('EditWorkspace.BackupOnInterval')} secondary={t('EditWorkspace.BackupOnIntervalDescription')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={backupOnInterval}
|
||||
onChange={(event) => workspaceSetter({ ...workspace, backupOnInterval: event.target.checked })}
|
||||
onChange={(event) => {
|
||||
workspaceSetter({ ...workspace, backupOnInterval: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
|
|
@ -418,10 +439,12 @@ export default function EditWorkspace(): JSX.Element {
|
|||
<ListItemText primary={t('EditWorkspace.HibernateTitle')} secondary={t('EditWorkspace.HibernateDescription')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={hibernateWhenUnused}
|
||||
onChange={(event) => workspaceSetter({ ...workspace, hibernateWhenUnused: event.target.checked })}
|
||||
onChange={(event) => {
|
||||
workspaceSetter({ ...workspace, hibernateWhenUnused: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
|
|
@ -429,10 +452,12 @@ export default function EditWorkspace(): JSX.Element {
|
|||
<ListItemText primary={t('EditWorkspace.DisableNotificationTitle')} secondary={t('EditWorkspace.DisableNotification')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={disableNotifications}
|
||||
onChange={(event) => workspaceSetter({ ...workspace, disableNotifications: event.target.checked })}
|
||||
onChange={(event) => {
|
||||
workspaceSetter({ ...workspace, disableNotifications: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
|
|
@ -440,10 +465,12 @@ export default function EditWorkspace(): JSX.Element {
|
|||
<ListItemText primary={t('EditWorkspace.DisableAudioTitle')} secondary={t('EditWorkspace.DisableAudio')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={disableAudio}
|
||||
onChange={(event) => workspaceSetter({ ...workspace, disableAudio: event.target.checked })}
|
||||
onChange={(event) => {
|
||||
workspaceSetter({ ...workspace, disableAudio: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
|
|
@ -452,10 +479,10 @@ export default function EditWorkspace(): JSX.Element {
|
|||
</FlexGrow>
|
||||
{!isEqual(workspace, originalWorkspace) && (
|
||||
<div>
|
||||
<Button color="primary" variant="contained" disableElevation onClick={requestSaveAndRestart}>
|
||||
<Button color='primary' variant='contained' disableElevation onClick={requestSaveAndRestart}>
|
||||
{t('EditWorkspace.Save')}
|
||||
</Button>
|
||||
<Button variant="contained" disableElevation onClick={() => void window.remote.closeCurrentWindow()}>
|
||||
<Button variant='contained' disableElevation onClick={() => void window.remote.closeCurrentWindow()}>
|
||||
{t('EditWorkspace.Cancel')}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import usePreviousValue from 'beautiful-react-hooks/usePreviousValue';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
import usePreviousValue from 'beautiful-react-hooks/usePreviousValue';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
|
||||
export function useForm(
|
||||
originalWorkspace?: IWorkspace,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
/* eslint-disable unicorn/no-null */
|
||||
import { Accordion, AccordionDetails, AccordionSummary, Button, Typography } from '@material-ui/core';
|
||||
import { useCallback } from 'react';
|
||||
import { Button, Accordion, AccordionSummary, AccordionDetails, Typography } from '@material-ui/core';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { IWorkspaceWithMetadata, IWorkspaceMetaData } from '@services/workspaces/interface';
|
||||
import { IWorkspaceMetaData, IWorkspaceWithMetadata } from '@services/workspaces/interface';
|
||||
import { ReportErrorButton } from '../AddWorkspace/FormComponents';
|
||||
|
||||
const HelperTextsList = styled.ul`
|
||||
|
|
@ -38,15 +38,20 @@ export function WikiErrorMessages(props: IWikiErrorMessagesProps): JSX.Element {
|
|||
<WikiErrorMessagesContainer>
|
||||
<Accordion>
|
||||
<AccordionSummary>
|
||||
<Typography align="left" variant="h5">
|
||||
<Typography align='left' variant='h5'>
|
||||
{t('Error.WikiRuntimeError')} {t('ClickForDetails')}
|
||||
</Typography>
|
||||
</AccordionSummary>
|
||||
<AccordionDetails>
|
||||
<Typography align="left" variant="body2">
|
||||
<Typography align='left' variant='body2'>
|
||||
{t('Error.WikiRuntimeErrorDescription')}
|
||||
</Typography>
|
||||
<Button variant="outlined" onClick={async () => await window.service.native.open(wikiLogs.filePath, true)}>
|
||||
<Button
|
||||
variant='outlined'
|
||||
onClick={async () => {
|
||||
await window.service.native.open(wikiLogs.filePath, true);
|
||||
}}
|
||||
>
|
||||
{t('Preference.OpenLogFolder')}
|
||||
</Button>
|
||||
|
||||
|
|
@ -89,22 +94,22 @@ export function ViewLoadErrorMessages(props: IViewLoadErrorMessagesProps): JSX.E
|
|||
|
||||
return (
|
||||
<WikiErrorMessagesContainer>
|
||||
<Typography align="left" variant="h5">
|
||||
<Typography align='left' variant='h5'>
|
||||
{t('AddWorkspace.WikiNotStarted')}
|
||||
</Typography>
|
||||
<Typography align="left" variant="body2">
|
||||
<Typography align='left' variant='body2'>
|
||||
{props.activeWorkspaceMetadata.didFailLoadErrorMessage}
|
||||
</Typography>
|
||||
|
||||
<br />
|
||||
<Trans t={t} i18nKey="AddWorkspace.MainPageReloadTip">
|
||||
<Typography align="left" variant="body2" component="div">
|
||||
<Trans t={t} i18nKey='AddWorkspace.MainPageReloadTip'>
|
||||
<Typography align='left' variant='body2' component='div'>
|
||||
<>
|
||||
Try:
|
||||
<HelperTextsList>
|
||||
<li>
|
||||
Click{' '}
|
||||
<b onClick={requestReload} onKeyPress={requestReload} role="button" tabIndex={0} style={{ cursor: 'pointer' }}>
|
||||
<b onClick={requestReload} onKeyPress={requestReload} role='button' tabIndex={0} style={{ cursor: 'pointer' }}>
|
||||
Reload
|
||||
</b>{' '}
|
||||
button below or press <b>CMD_or_Ctrl + R</b> to reload the page.
|
||||
|
|
@ -112,11 +117,16 @@ export function ViewLoadErrorMessages(props: IViewLoadErrorMessagesProps): JSX.E
|
|||
<li>
|
||||
Check the{' '}
|
||||
<b
|
||||
onClick={async () => await window.service.native.open(await window.service.context.get('LOG_FOLDER'), true)}
|
||||
onKeyPress={async () => await window.service.native.open(await window.service.context.get('LOG_FOLDER'), true)}
|
||||
role="button"
|
||||
onClick={async () => {
|
||||
await window.service.native.open(await window.service.context.get('LOG_FOLDER'), true);
|
||||
}}
|
||||
onKeyPress={async () => {
|
||||
await window.service.native.open(await window.service.context.get('LOG_FOLDER'), true);
|
||||
}}
|
||||
role='button'
|
||||
tabIndex={0}
|
||||
style={{ cursor: 'pointer' }}>
|
||||
style={{ cursor: 'pointer' }}
|
||||
>
|
||||
Log Folder
|
||||
</b>{' '}
|
||||
to see what happened.
|
||||
|
|
@ -128,12 +138,10 @@ export function ViewLoadErrorMessages(props: IViewLoadErrorMessagesProps): JSX.E
|
|||
</Trans>
|
||||
|
||||
<ButtonGroup>
|
||||
<Button variant="outlined" onClick={requestReload}>
|
||||
<Button variant='outlined' onClick={requestReload}>
|
||||
{t('AddWorkspace.Reload')}
|
||||
</Button>
|
||||
{typeof props.activeWorkspaceMetadata.didFailLoadErrorMessage === 'string' && (
|
||||
<ReportErrorButton message={props.activeWorkspaceMetadata.didFailLoadErrorMessage} />
|
||||
)}
|
||||
{typeof props.activeWorkspaceMetadata.didFailLoadErrorMessage === 'string' && <ReportErrorButton message={props.activeWorkspaceMetadata.didFailLoadErrorMessage} />}
|
||||
</ButtonGroup>
|
||||
</WikiErrorMessagesContainer>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { IPreferences } from '@services/preferences/interface';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
|
||||
import arrowWhite from '@/images/arrow-white.png';
|
||||
import arrowBlack from '@/images/arrow-black.png';
|
||||
import arrowWhite from '@/images/arrow-white.png';
|
||||
|
||||
const Arrow = styled.div<{ image: string }>`
|
||||
height: 202px;
|
||||
|
|
@ -64,31 +64,37 @@ export interface IProps {
|
|||
export function NewUserMessage(props: IProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<AddWorkspaceGuideInfoContainer onClick={async () => await window.service.window.open(WindowNames.addWorkspace)}>
|
||||
{props.sidebar ? (
|
||||
<>
|
||||
<Arrow image={props.themeSource === 'dark' ? arrowWhite : arrowBlack} />
|
||||
<TipWithSidebar id="new-user-tip">
|
||||
<Trans t={t} i18nKey="AddWorkspace.MainPageTipWithSidebar">
|
||||
<Tip2Text>Click</Tip2Text>
|
||||
<Avatar>+</Avatar>
|
||||
<Tip2Text>to get started!</Tip2Text>
|
||||
</Trans>
|
||||
</TipWithSidebar>
|
||||
</>
|
||||
) : (
|
||||
<TipWithoutSidebar id="new-user-tip">
|
||||
<Tip2Text>
|
||||
<Trans t={t} i18nKey="AddWorkspace.MainPageTipWithoutSidebar">
|
||||
<span>Click </span>
|
||||
<strong>Workspaces > Add Workspace</strong>
|
||||
<span>Or </span>
|
||||
<strong>Click Here</strong>
|
||||
<span> to get started!</span>
|
||||
</Trans>
|
||||
</Tip2Text>
|
||||
</TipWithoutSidebar>
|
||||
)}
|
||||
<AddWorkspaceGuideInfoContainer
|
||||
onClick={async () => {
|
||||
await window.service.window.open(WindowNames.addWorkspace);
|
||||
}}
|
||||
>
|
||||
{props.sidebar
|
||||
? (
|
||||
<>
|
||||
<Arrow image={props.themeSource === 'dark' ? arrowWhite : arrowBlack} />
|
||||
<TipWithSidebar id='new-user-tip'>
|
||||
<Trans t={t} i18nKey='AddWorkspace.MainPageTipWithSidebar'>
|
||||
<Tip2Text>Click</Tip2Text>
|
||||
<Avatar>+</Avatar>
|
||||
<Tip2Text>to get started!</Tip2Text>
|
||||
</Trans>
|
||||
</TipWithSidebar>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<TipWithoutSidebar id='new-user-tip'>
|
||||
<Tip2Text>
|
||||
<Trans t={t} i18nKey='AddWorkspace.MainPageTipWithoutSidebar'>
|
||||
<span>Click</span>
|
||||
<strong>Workspaces > Add Workspace</strong>
|
||||
<span>Or</span>
|
||||
<strong>Click Here</strong>
|
||||
<span>to get started!</span>
|
||||
</Trans>
|
||||
</Tip2Text>
|
||||
</TipWithoutSidebar>
|
||||
)}
|
||||
</AddWorkspaceGuideInfoContainer>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,34 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable @typescript-eslint/promise-function-async */
|
||||
import React, { useState } from 'react';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled, { css } from 'styled-components';
|
||||
import is, { isNot } from 'typescript-styled-is';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import SimpleBar from 'simplebar-react';
|
||||
import 'simplebar/dist/simplebar.min.css';
|
||||
|
||||
import { Typography, Tooltip, IconButton as IconButtonRaw } from '@material-ui/core';
|
||||
import { IconButton as IconButtonRaw, Tooltip, Typography } from '@material-ui/core';
|
||||
import { Settings as SettingsIcon, Upgrade as UpgradeIcon } from '@material-ui/icons';
|
||||
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { IUpdaterStatus } from '@services/updater/interface';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { useUpdaterObservable } from '@services/updater/hooks';
|
||||
|
||||
import FindInPage from '../../components/FindInPage';
|
||||
import { latestStableUpdateUrl } from '@/constants/urls';
|
||||
import FindInPage from '../../components/FindInPage';
|
||||
|
||||
import { WorkspaceSelector, SortableWorkspaceSelectorList } from '@/components/WorkspaceIconAndSelector';
|
||||
import { useWorkspacesListObservable } from '@services/workspaces/hooks';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { CommandPaletteIcon } from '@/components/icon/CommandPaletteSVG';
|
||||
import { SortableWorkspaceSelectorList, WorkspaceSelector } from '@/components/WorkspaceIconAndSelector';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { useWorkspacesListObservable } from '@services/workspaces/hooks';
|
||||
import { Languages } from '../Preferences/sections/Languages';
|
||||
import { TiddlyWiki } from '../Preferences/sections/TiddlyWiki';
|
||||
import { ViewLoadErrorMessages, WikiErrorMessages } from './ErrorMessage';
|
||||
import { NewUserMessage } from './NewUserMessage';
|
||||
import { WikiErrorMessages, ViewLoadErrorMessages } from './ErrorMessage';
|
||||
import { useAutoCreateFirstWorkspace } from './useAutoCreateFirstWorkspace';
|
||||
|
||||
const OuterRoot = styled.div`
|
||||
|
|
@ -91,11 +91,11 @@ const SidebarTop = styled.div<{ titleBar?: boolean }>`
|
|||
flex: 1;
|
||||
width: 100%;
|
||||
${({ titleBar }) =>
|
||||
titleBar === true
|
||||
? css`
|
||||
titleBar === true
|
||||
? css`
|
||||
padding-top: 0;
|
||||
`
|
||||
: css`
|
||||
: css`
|
||||
padding-top: 30px;
|
||||
`}
|
||||
`;
|
||||
|
|
@ -167,13 +167,12 @@ export default function Main(): JSX.Element {
|
|||
if (preferences === undefined) return <div>{t('Loading')}</div>;
|
||||
|
||||
const { sidebar, themeSource, sidebarShortcutHints, hideSideBarIcon } = preferences;
|
||||
const hasError =
|
||||
typeof activeWorkspaceMetadata?.didFailLoadErrorMessage === 'string' &&
|
||||
const hasError = typeof activeWorkspaceMetadata?.didFailLoadErrorMessage === 'string' &&
|
||||
activeWorkspaceMetadata?.didFailLoadErrorMessage.length > 0 &&
|
||||
activeWorkspaceMetadata?.isLoading === false;
|
||||
return (
|
||||
<OuterRoot>
|
||||
<div id="test" data-usage="For spectron automating testing" />
|
||||
<div id='test' data-usage='For spectron automating testing' />
|
||||
<Helmet>
|
||||
<title>{t('Menu.TidGi')}</title>
|
||||
</Helmet>
|
||||
|
|
@ -181,13 +180,11 @@ export default function Main(): JSX.Element {
|
|||
{sidebar && (
|
||||
<SidebarContainer>
|
||||
<SidebarTop titleBar={titleBar}>
|
||||
{workspacesList === undefined ? (
|
||||
<div>{t('Loading')}</div>
|
||||
) : (
|
||||
<SortableWorkspaceSelectorList sidebarShortcutHints={sidebarShortcutHints} workspacesList={workspacesList} hideSideBarIcon={hideSideBarIcon} />
|
||||
)}
|
||||
{workspacesList === undefined
|
||||
? <div>{t('Loading')}</div>
|
||||
: <SortableWorkspaceSelectorList sidebarShortcutHints={sidebarShortcutHints} workspacesList={workspacesList} hideSideBarIcon={hideSideBarIcon} />}
|
||||
<WorkspaceSelector
|
||||
id="add"
|
||||
id='add'
|
||||
hideSideBarIcon={hideSideBarIcon}
|
||||
index={workspacesList?.length ?? 0}
|
||||
showSidebarShortcutHints={sidebarShortcutHints}
|
||||
|
|
@ -196,7 +193,7 @@ export default function Main(): JSX.Element {
|
|||
{workspacesList === undefined ||
|
||||
(workspacesList.length === 0 && (
|
||||
<WorkspaceSelector
|
||||
id="guide"
|
||||
id='guide'
|
||||
hideSideBarIcon={hideSideBarIcon}
|
||||
index={workspacesList?.length ? workspacesList.length ?? 0 + 1 : 1}
|
||||
active={activeWorkspace?.id === undefined}
|
||||
|
|
@ -209,10 +206,13 @@ export default function Main(): JSX.Element {
|
|||
{(workspacesList?.length ?? 0) > 0 && (
|
||||
<>
|
||||
<IconButton
|
||||
id="open-command-palette-button"
|
||||
id='open-command-palette-button'
|
||||
aria-label={t('SideBar.CommandPalette')}
|
||||
onClick={async () => await window.service.wiki.requestWikiSendActionMessage('open-command-palette')}>
|
||||
<Tooltip title={<span>{t('SideBar.CommandPalette')}</span>} placement="top">
|
||||
onClick={async () => {
|
||||
await window.service.wiki.requestWikiSendActionMessage('open-command-palette');
|
||||
}}
|
||||
>
|
||||
<Tooltip title={<span>{t('SideBar.CommandPalette')}</span>} placement='top'>
|
||||
<CommandPaletteIcon />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
|
|
@ -220,19 +220,25 @@ export default function Main(): JSX.Element {
|
|||
)}
|
||||
{updaterMetaData?.status === IUpdaterStatus.updateAvailable && (
|
||||
<IconButton
|
||||
id="update-available"
|
||||
id='update-available'
|
||||
aria-label={t('SideBar.UpdateAvailable')}
|
||||
onClick={async () => await window.service.native.open(updaterMetaData.info?.latestReleasePageUrl ?? latestStableUpdateUrl)}>
|
||||
<Tooltip title={<span>{t('SideBar.UpdateAvailable')}</span>} placement="top">
|
||||
onClick={async () => {
|
||||
await window.service.native.open(updaterMetaData.info?.latestReleasePageUrl ?? latestStableUpdateUrl);
|
||||
}}
|
||||
>
|
||||
<Tooltip title={<span>{t('SideBar.UpdateAvailable')}</span>} placement='top'>
|
||||
<UpgradeIcon />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
)}
|
||||
<IconButton
|
||||
id="open-preferences-button"
|
||||
id='open-preferences-button'
|
||||
aria-label={t('SideBar.Preferences')}
|
||||
onClick={async () => await window.service.window.open(WindowNames.preferences)}>
|
||||
<Tooltip title={<span>{t('SideBar.Preferences')}</span>} placement="top">
|
||||
onClick={async () => {
|
||||
await window.service.window.open(WindowNames.preferences);
|
||||
}}
|
||||
>
|
||||
<Tooltip title={<span>{t('SideBar.Preferences')}</span>} placement='top'>
|
||||
<SettingsIcon />
|
||||
</Tooltip>
|
||||
</IconButton>
|
||||
|
|
@ -247,9 +253,9 @@ export default function Main(): JSX.Element {
|
|||
<ViewLoadErrorMessages activeWorkspace={activeWorkspace} activeWorkspaceMetadata={activeWorkspaceMetadata} />
|
||||
)}
|
||||
{Array.isArray(workspacesList) && workspacesList.length > 0 && activeWorkspaceMetadata?.isLoading === true && (
|
||||
<Typography color="textSecondary">{t('Loading')}</Typography>
|
||||
<Typography color='textSecondary'>{t('Loading')}</Typography>
|
||||
)}
|
||||
{wikiCreationMessage && <Typography color="textSecondary">{wikiCreationMessage}</Typography>}
|
||||
{wikiCreationMessage && <Typography color='textSecondary'>{wikiCreationMessage}</Typography>}
|
||||
{Array.isArray(workspacesList) && workspacesList.length === 0 && <NewUserMessage sidebar={sidebar} themeSource={themeSource} />}
|
||||
</InnerContentRoot>
|
||||
<Languages languageSelectorOnly />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
/* eslint-disable @typescript-eslint/promise-function-async */
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IWorkspaceWithMetadata } from '@services/workspaces/interface';
|
||||
import { INewWikiRequiredFormData, useNewWiki } from '../AddWorkspace/useNewWiki';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { useWikiWorkspaceForm } from '../AddWorkspace/useForm';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { IWorkspaceWithMetadata } from '@services/workspaces/interface';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useWikiWorkspaceForm } from '../AddWorkspace/useForm';
|
||||
import { INewWikiRequiredFormData, useNewWiki } from '../AddWorkspace/useNewWiki';
|
||||
|
||||
export function useAutoCreateFirstWorkspace(workspacesList: IWorkspaceWithMetadata[] | undefined, wikiCreationMessageSetter: (m: string) => void): void {
|
||||
const form = useWikiWorkspaceForm();
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@
|
|||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
/* eslint-disable unicorn/no-useless-undefined */
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import ListSubheader from '@material-ui/core/ListSubheader';
|
||||
import Container from '@material-ui/core/Container';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import ListRaw from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
import ListSubheader from '@material-ui/core/ListSubheader';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
import Container from '@material-ui/core/Container';
|
||||
import TextField from '@material-ui/core/TextField';
|
||||
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
|
||||
|
|
@ -23,12 +23,12 @@ import { WindowNames } from '@services/windows/WindowProperties';
|
|||
import PopUpMenuItem from '@/components/PopUpMenuItem';
|
||||
|
||||
// https://www.sketchappsources.com/free-source/2501-iphone-app-background-sketch-freebie-resource.html
|
||||
import nightBackgroundPng from '../../images/night-background.png';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { useNotificationInfoObservable } from '@services/notifications/hooks';
|
||||
import { quickShortcuts } from './quickShortcuts';
|
||||
import { PreferenceSections } from '@services/preferences/interface';
|
||||
import { formatDate } from '@services/libs/formatDate';
|
||||
import { useNotificationInfoObservable } from '@services/notifications/hooks';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { PreferenceSections } from '@services/preferences/interface';
|
||||
import nightBackgroundPng from '../../images/night-background.png';
|
||||
import { quickShortcuts } from './quickShortcuts';
|
||||
|
||||
const Root = styled(Container)`
|
||||
width: 100%;
|
||||
|
|
@ -90,7 +90,7 @@ export default function Notifications(): JSX.Element {
|
|||
</PausingHeader>
|
||||
<ListItem button>
|
||||
<ListItemText
|
||||
primary="Resume notifications"
|
||||
primary='Resume notifications'
|
||||
onClick={async () => {
|
||||
if (pauseNotificationsInfo === undefined) {
|
||||
return;
|
||||
|
|
@ -114,19 +114,31 @@ export default function Notifications(): JSX.Element {
|
|||
<>
|
||||
<Divider />
|
||||
<PopUpMenuItem
|
||||
id="adjustTime"
|
||||
id='adjustTime'
|
||||
buttonElement={
|
||||
<ListItem button>
|
||||
<ListItemText primary="Adjust time" />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ListItemText primary='Adjust time' />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
}>
|
||||
}
|
||||
>
|
||||
{quickShortcuts.map((shortcut) => (
|
||||
<MenuItem dense key={shortcut.name} onClick={() => pauseNotification(shortcut.calcDate())}>
|
||||
<MenuItem
|
||||
dense
|
||||
key={shortcut.name}
|
||||
onClick={() => {
|
||||
pauseNotification(shortcut.calcDate());
|
||||
}}
|
||||
>
|
||||
{shortcut.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
<MenuItem dense onClick={() => showDateTimePickerSetter(true)}>
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={() => {
|
||||
showDateTimePickerSetter(true);
|
||||
}}
|
||||
>
|
||||
Custom...
|
||||
</MenuItem>
|
||||
</PopUpMenuItem>
|
||||
|
|
@ -147,19 +159,30 @@ export default function Notifications(): JSX.Element {
|
|||
}
|
||||
|
||||
return (
|
||||
<List subheader={<ListSubheader component="div">Pause notifications</ListSubheader>}>
|
||||
<List subheader={<ListSubheader component='div'>Pause notifications</ListSubheader>}>
|
||||
{quickShortcuts.map((shortcut) => (
|
||||
<ListItem button key={shortcut.name} onClick={() => pauseNotification(shortcut.calcDate())}>
|
||||
<ListItem
|
||||
button
|
||||
key={shortcut.name}
|
||||
onClick={() => {
|
||||
pauseNotification(shortcut.calcDate());
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={shortcut.name} />
|
||||
</ListItem>
|
||||
))}
|
||||
<ListItem button onClick={() => showDateTimePickerSetter(true)}>
|
||||
<ListItemText primary="Custom..." />
|
||||
<ListItem
|
||||
button
|
||||
onClick={() => {
|
||||
showDateTimePickerSetter(true);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary='Custom...' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem button>
|
||||
<ListItemText
|
||||
primary="Pause notifications by schedule..."
|
||||
primary='Pause notifications by schedule...'
|
||||
onClick={async () => {
|
||||
await window.service.window.open(WindowNames.preferences, { gotoTab: PreferenceSections.notifications });
|
||||
void window.remote.closeCurrentWindow();
|
||||
|
|
@ -172,7 +195,7 @@ export default function Notifications(): JSX.Element {
|
|||
|
||||
return (
|
||||
<Root>
|
||||
<div id="test" data-usage="For spectron automating testing" />
|
||||
<div id='test' data-usage='For spectron automating testing' />
|
||||
<Helmet>
|
||||
<title>{t('ContextMenu.Notifications')}</title>
|
||||
</Helmet>
|
||||
|
|
@ -184,10 +207,14 @@ export default function Notifications(): JSX.Element {
|
|||
if (tilDate === null) return;
|
||||
pauseNotification(tilDate);
|
||||
}}
|
||||
label="Custom"
|
||||
label='Custom'
|
||||
open={showDateTimePicker}
|
||||
onOpen={() => showDateTimePickerSetter(true)}
|
||||
onClose={() => showDateTimePickerSetter(false)}
|
||||
onOpen={() => {
|
||||
showDateTimePickerSetter(true);
|
||||
}}
|
||||
onClose={() => {
|
||||
showDateTimePickerSetter(false);
|
||||
}}
|
||||
disablePast
|
||||
showTodayButton
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { addMinutes, addHours, addDays, addWeeks } from 'date-fns';
|
||||
import { addDays, addHours, addMinutes, addWeeks } from 'date-fns';
|
||||
|
||||
export const quickShortcuts = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
import { InputLabel as InputLabelRaw, ListItem as ListItemRaw, ListItemText as ListItemTextRaw, Paper as PaperRaw, TextField as TextFieldRaw, Typography } from '@material-ui/core';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import {
|
||||
Paper as PaperRaw,
|
||||
Typography,
|
||||
TextField as TextFieldRaw,
|
||||
ListItem as ListItemRaw,
|
||||
ListItemText as ListItemTextRaw,
|
||||
InputLabel as InputLabelRaw,
|
||||
} from '@material-ui/core';
|
||||
|
||||
export const Paper = styled(PaperRaw)`
|
||||
margin-top: 5px;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { Divider as DividerRaw, List, ListItem, ListItemIcon as ListItemIconRaw, ListItemText } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
import { Divider as DividerRaw, List, ListItem, ListItemIcon as ListItemIconRaw, ListItemText } from '@material-ui/core';
|
||||
|
||||
import { ISectionProps } from './useSections';
|
||||
import { PreferenceSections } from '@services/preferences/interface';
|
||||
import { ISectionProps } from './useSections';
|
||||
|
||||
const SideBar = styled.div`
|
||||
position: fixed;
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { useRestartSnackbar } from '@/components/RestartSnackbar';
|
||||
|
||||
import { IPossibleWindowMeta, WindowMeta, WindowNames } from '@services/windows/WindowProperties';
|
||||
import { usePreferenceSections } from './useSections';
|
||||
import { SectionSideBar } from './SectionsSideBar';
|
||||
import { TiddlyWiki } from './sections/TiddlyWiki';
|
||||
import { Sync } from './sections/Sync';
|
||||
import { General } from './sections/General';
|
||||
import { System } from './sections/System';
|
||||
import { Notifications } from './sections/Notifications';
|
||||
import { Languages } from './sections/Languages';
|
||||
import { Downloads } from './sections/Downloads';
|
||||
import { Network } from './sections/Network';
|
||||
import { PrivacyAndSecurity } from './sections/PrivacyAndSecurity';
|
||||
import { DeveloperTools } from './sections/DeveloperTools';
|
||||
import { Performance } from './sections/Performance';
|
||||
import { Updates } from './sections/Updates';
|
||||
import { Downloads } from './sections/Downloads';
|
||||
import { FriendLinks } from './sections/FriendLinks';
|
||||
import { General } from './sections/General';
|
||||
import { Languages } from './sections/Languages';
|
||||
import { Miscellaneous } from './sections/Miscellaneous';
|
||||
import { Network } from './sections/Network';
|
||||
import { Notifications } from './sections/Notifications';
|
||||
import { Performance } from './sections/Performance';
|
||||
import { PrivacyAndSecurity } from './sections/PrivacyAndSecurity';
|
||||
import { Sync } from './sections/Sync';
|
||||
import { System } from './sections/System';
|
||||
import { TiddlyWiki } from './sections/TiddlyWiki';
|
||||
import { Updates } from './sections/Updates';
|
||||
import { SectionSideBar } from './SectionsSideBar';
|
||||
import { usePreferenceSections } from './useSections';
|
||||
|
||||
const Root = styled.div`
|
||||
padding: 20px;
|
||||
|
|
@ -47,7 +47,7 @@ export default function Preferences(): JSX.Element {
|
|||
|
||||
return (
|
||||
<Root>
|
||||
<div id="test" data-usage="For spectron automating testing" />
|
||||
<div id='test' data-usage='For spectron automating testing' />
|
||||
{RestartSnackbar}
|
||||
|
||||
<Helmet>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Divider, List } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function DeveloperTools(props: ISectionProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -24,9 +24,7 @@ export function DeveloperTools(props: ISectionProps): JSX.Element {
|
|||
<SectionTitle ref={props.sections.developers.ref}>{t('Preference.DeveloperTools')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{LOG_FOLDER === undefined || SETTINGS_FOLDER === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{LOG_FOLDER === undefined || SETTINGS_FOLDER === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem
|
||||
button
|
||||
|
|
@ -34,9 +32,10 @@ export function DeveloperTools(props: ISectionProps): JSX.Element {
|
|||
if (LOG_FOLDER !== undefined) {
|
||||
void window.service.native.open(LOG_FOLDER, true);
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('Preference.OpenLogFolder')} secondary={t('Preference.OpenLogFolderDetail')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem
|
||||
|
|
@ -45,14 +44,20 @@ export function DeveloperTools(props: ISectionProps): JSX.Element {
|
|||
if (SETTINGS_FOLDER !== undefined) {
|
||||
void window.service.native.open(SETTINGS_FOLDER, true);
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('Preference.OpenMetaDataFolder')} secondary={t('Preference.OpenMetaDataFolderDetail')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem button onClick={async () => await window.service.preference.resetWithConfirm()}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.preference.resetWithConfirm();
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('Preference.RestorePreferences')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Divider, List, ListItemSecondaryAction, Switch } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function Downloads(props: Required<ISectionProps>): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -17,9 +17,7 @@ export function Downloads(props: Required<ISectionProps>): JSX.Element {
|
|||
<SectionTitle ref={props.sections.downloads.ref}>{t('Preference.Downloads')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem
|
||||
button
|
||||
|
|
@ -34,17 +32,18 @@ export function Downloads(props: Required<ISectionProps>): JSX.Element {
|
|||
.catch((error: Error) => {
|
||||
console.log(error); // eslint-disable-line no-console
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('Preference.DownloadLocation')} secondary={preference.downloadPath} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText primary={t('Preference.AskDownloadLocation')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.askForDownloadPath}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('askForDownloadPath', event.target.checked);
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Divider, List } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
|
||||
import webcatalogLogo from '@/images/webcatalog-logo.svg';
|
||||
import translatiumLogo from '@/images/translatium-logo.svg';
|
||||
import webcatalogLogo from '@/images/webcatalog-logo.svg';
|
||||
|
||||
const Logo = styled.img`
|
||||
height: 28px;
|
||||
|
|
@ -22,19 +22,34 @@ export function FriendLinks(props: ISectionProps): JSX.Element {
|
|||
<SectionTitle ref={props.sections.friendLinks.ref}>{t('Preference.FriendLinks')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
<ListItem button onClick={async () => await window.service.native.open('https://github.com/webcatalog/webcatalog-engine')}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://github.com/webcatalog/webcatalog-engine');
|
||||
}}
|
||||
>
|
||||
<ListItemText secondary={t('Preference.WebCatalogEngineIntro')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem button onClick={async () => await window.service.native.open('https://webcatalogapp.com?utm_source=tidgi_app')}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://webcatalogapp.com?utm_source=tidgi_app');
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={<Logo src={webcatalogLogo} alt={t('Preference.WebCatalog')} />} secondary={t('Preference.WebCatalogIntro')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem button onClick={async () => await window.service.native.open('https://translatiumapp.com?utm_source=tidgi_app')}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://translatiumapp.com?utm_source=tidgi_app');
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={<Logo src={translatiumLogo} alt={t('Preference.Translatium')} />} secondary={t('Preference.TranslatiumIntro')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { Divider, List, ListItemSecondaryAction, MenuItem, Switch } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Divider, Switch, List, ListItemSecondaryAction, MenuItem } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import PopUpMenuItem from '@/components/PopUpMenuItem';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { IPreferences } from '@services/preferences/interface';
|
||||
import { ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
const getThemeString = (theme: IPreferences['themeSource']): string => {
|
||||
if (theme === 'light') return 'Light';
|
||||
|
|
@ -27,16 +27,14 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<SectionTitle ref={props.sections.general.ref}>{t('Preference.General')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined || platform === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined || platform === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem>
|
||||
<ListItemText primary={t('Preference.RememberLastVisitState')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.rememberLastPageVisited}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('rememberLastPageVisited', event.target.checked);
|
||||
|
|
@ -47,20 +45,36 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
</ListItem>
|
||||
<Divider />
|
||||
<PopUpMenuItem
|
||||
id="theme"
|
||||
id='theme'
|
||||
buttonElement={
|
||||
<ListItem button>
|
||||
<ListItemText primary={t('Preference.Theme')} secondary={getThemeString(preference.themeSource)} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
}>
|
||||
<MenuItem dense onClick={async () => await window.service.preference.set('themeSource', 'system')}>
|
||||
}
|
||||
>
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={async () => {
|
||||
await window.service.preference.set('themeSource', 'system');
|
||||
}}
|
||||
>
|
||||
{t('Preference.SystemDefaultTheme')}
|
||||
</MenuItem>
|
||||
<MenuItem dense onClick={async () => await window.service.preference.set('themeSource', 'light')}>
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={async () => {
|
||||
await window.service.preference.set('themeSource', 'light');
|
||||
}}
|
||||
>
|
||||
{t('Preference.LightTheme')}
|
||||
</MenuItem>
|
||||
<MenuItem dense onClick={async () => await window.service.preference.set('themeSource', 'dark')}>
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={async () => {
|
||||
await window.service.preference.set('themeSource', 'dark');
|
||||
}}
|
||||
>
|
||||
{t('Preference.DarkTheme')}
|
||||
</MenuItem>
|
||||
</PopUpMenuItem>
|
||||
|
|
@ -69,8 +83,8 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.ShowSideBar')} secondary={t('Preference.ShowSideBarDetail')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.sidebar}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('sidebar', event.target.checked);
|
||||
|
|
@ -83,8 +97,8 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.HideSideBarIcon')} secondary={t('Preference.ShowSideBarDetail')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.hideSideBarIcon}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('hideSideBarIcon', event.target.checked);
|
||||
|
|
@ -97,8 +111,8 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.ShowSideBarShortcut')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.sidebarShortcutHints}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('sidebarShortcutHints', event.target.checked);
|
||||
|
|
@ -113,8 +127,8 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.ShowTitleBar')} secondary={t('Preference.ShowTitleBarDetail')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.titleBar}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('titleBar', event.target.checked);
|
||||
|
|
@ -134,8 +148,8 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.HideMenuBar')} secondary={t('Preference.HideMenuBarDetail')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.hideMenuBar}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('hideMenuBar', event.target.checked);
|
||||
|
|
@ -151,8 +165,8 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.AlwaysOnTop')} secondary={t('Preference.AlwaysOnTopDetail')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.alwaysOnTop}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('alwaysOnTop', event.target.checked);
|
||||
|
|
@ -165,12 +179,12 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItem>
|
||||
<ListItemText
|
||||
primary={platform === 'win32' ? t('Preference.AttachToTaskbar') : t('Preference.AttachToMenuBar')}
|
||||
secondary={platform !== 'linux' ? t('Preference.AttachToMenuBarTip') : undefined}
|
||||
secondary={platform === 'linux' ? undefined : t('Preference.AttachToMenuBarTip')}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.attachToMenubar}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('attachToMenubar', event.target.checked);
|
||||
|
|
@ -183,8 +197,8 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.MenubarAlwaysOnTop')} secondary={t('Preference.MenubarAlwaysOnTopDetail')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.menuBarAlwaysOnTop}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('menuBarAlwaysOnTop', event.target.checked);
|
||||
|
|
@ -200,7 +214,7 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText
|
||||
primary={t('Preference.SwipeWithThreeFingersToNavigate')}
|
||||
secondary={
|
||||
<Trans t={t} i18nKey="Preference.SwipeWithThreeFingersToNavigateDescription">
|
||||
<Trans t={t} i18nKey='Preference.SwipeWithThreeFingersToNavigateDescription'>
|
||||
Navigate between pages with 3-finger gestures. Swipe left to go back or swipe right to go forward.
|
||||
<br />
|
||||
To enable it, you also need to change
|
||||
|
|
@ -214,8 +228,8 @@ export function General(props: Required<ISectionProps>): JSX.Element {
|
|||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.swipeToNavigate}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('swipeToNavigate', event.target.checked);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Divider, List, ListItemSecondaryAction, MenuItem, Select, Switch } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText, InputLabel } from '../PreferenceComponents';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { hunspellLanguagesMap } from '@/constants/hunspellLanguages';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { InputLabel, ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function Languages(props: Partial<ISectionProps> & { languageSelectorOnly?: boolean }): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -24,23 +24,22 @@ export function Languages(props: Partial<ISectionProps> & { languageSelectorOnly
|
|||
<SectionTitle ref={props.sections?.languages?.ref}>{t('Preference.Languages')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined || platform === undefined || supportedLanguagesMap === undefined || preference.language === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined || platform === undefined || supportedLanguagesMap === undefined || preference.language === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem sx={{ justifyContent: 'space-between' }}>
|
||||
<ListItemText primary={t('Preference.ChooseLanguage')} />
|
||||
<InputLabel sx={{ flex: 1 }} id="demo-simple-select-label">
|
||||
<InputLabel sx={{ flex: 1 }} id='demo-simple-select-label'>
|
||||
{t('Preference.Languages')}
|
||||
</InputLabel>
|
||||
<Select
|
||||
sx={{ flex: 2 }}
|
||||
labelId="demo-simple-select-label"
|
||||
labelId='demo-simple-select-label'
|
||||
value={preference.language}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('language', event.target.value);
|
||||
}}
|
||||
autoWidth>
|
||||
autoWidth
|
||||
>
|
||||
{Object.keys(supportedLanguagesMap).map((languageID) => (
|
||||
<MenuItem value={languageID} key={languageID}>
|
||||
{supportedLanguagesMap[languageID]}
|
||||
|
|
@ -55,8 +54,8 @@ export function Languages(props: Partial<ISectionProps> & { languageSelectorOnly
|
|||
<ListItemText primary={t('Preference.SpellCheck')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.spellcheck}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('spellcheck', event.target.checked);
|
||||
|
|
@ -68,12 +67,17 @@ export function Languages(props: Partial<ISectionProps> & { languageSelectorOnly
|
|||
{platform !== 'darwin' && (
|
||||
<>
|
||||
<Divider />
|
||||
<ListItem button onClick={async () => await window.service.window.open(WindowNames.spellcheck)}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.window.open(WindowNames.spellcheck);
|
||||
}}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('Preference.SpellCheckLanguages')}
|
||||
secondary={preference.spellcheckLanguages.map((code) => hunspellLanguagesMap[code]).join(' | ')}
|
||||
/>
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Divider, List } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
|
||||
|
|
@ -16,24 +16,44 @@ export function Miscellaneous(props: ISectionProps): JSX.Element {
|
|||
<SectionTitle ref={props.sections.misc.ref}>{t('Preference.Miscellaneous')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
<ListItem button onClick={async () => await window.service.window.open(WindowNames.about)}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.window.open(WindowNames.about);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('ContextMenu.About')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem button onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TidGi-desktop/')}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://github.com/tiddly-gittly/TidGi-desktop/');
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('Preference.WebSite')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem button onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TidGi-desktop/issues')}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://github.com/tiddly-gittly/TidGi-desktop/issues');
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('Preference.Support')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem button onClick={() => window.service.native.quit()}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={() => {
|
||||
window.service.native.quit();
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('ContextMenu.Quit')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
</List>
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { List } from '@material-ui/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem } from '../PreferenceComponents';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { ListItem, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function Network(props: ISectionProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -15,9 +15,7 @@ export function Network(props: ISectionProps): JSX.Element {
|
|||
<SectionTitle ref={props.sections.network.ref}>{t('Preference.Network')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem></ListItem>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import semver from 'semver';
|
||||
|
||||
import TimePicker from '@material-ui/lab/TimePicker';
|
||||
import { Divider, List, ListItemSecondaryAction, Switch } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import TimePicker from '@material-ui/lab/TimePicker';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Link, ListItem, ListItemVertical, ListItemText, Paper, SectionTitle, TextField, TimePickerContainer } from '../PreferenceComponents';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { Link, ListItem, ListItemText, ListItemVertical, Paper, SectionTitle, TextField, TimePickerContainer } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function Notifications(props: Required<ISectionProps>): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -29,42 +29,57 @@ export function Notifications(props: Required<ISectionProps>): JSX.Element {
|
|||
<SectionTitle ref={props.sections.notifications.ref}>{t('Preference.Notifications')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined || platform === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined || platform === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem button onClick={async () => await window.service.window.open(WindowNames.notifications)}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.window.open(WindowNames.notifications);
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('Preference.NotificationsDetail')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItemVertical>
|
||||
<ListItemText primary={t('Preference.NotificationsDisableSchedule')} />
|
||||
<TimePickerContainer>
|
||||
<TimePicker
|
||||
label="from"
|
||||
label='from'
|
||||
renderInput={(timeProps) => <TextField {...timeProps} />}
|
||||
value={new Date(preference.pauseNotificationsByScheduleFrom)}
|
||||
onChange={async (d) => await window.service.preference.set('pauseNotificationsByScheduleFrom', (d ?? '').toString())}
|
||||
onClose={async () => await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: false })}
|
||||
onOpen={async () => await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: true })}
|
||||
onChange={async (d) => {
|
||||
await window.service.preference.set('pauseNotificationsByScheduleFrom', (d ?? '').toString());
|
||||
}}
|
||||
onClose={async () => {
|
||||
await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: false });
|
||||
}}
|
||||
onOpen={async () => {
|
||||
await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: true });
|
||||
}}
|
||||
disabled={!preference.pauseNotificationsBySchedule}
|
||||
/>
|
||||
<TimePicker
|
||||
label="to"
|
||||
label='to'
|
||||
renderInput={(timeProps) => <TextField {...timeProps} />}
|
||||
value={new Date(preference.pauseNotificationsByScheduleTo)}
|
||||
onChange={async (d) => await window.service.preference.set('pauseNotificationsByScheduleTo', (d ?? '').toString())}
|
||||
onClose={async () => await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: false })}
|
||||
onOpen={async () => await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: true })}
|
||||
onChange={async (d) => {
|
||||
await window.service.preference.set('pauseNotificationsByScheduleTo', (d ?? '').toString());
|
||||
}}
|
||||
onClose={async () => {
|
||||
await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: false });
|
||||
}}
|
||||
onOpen={async () => {
|
||||
await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: true });
|
||||
}}
|
||||
disabled={!preference.pauseNotificationsBySchedule}
|
||||
/>
|
||||
</TimePickerContainer>
|
||||
({window.Intl.DateTimeFormat().resolvedOptions().timeZone})
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.pauseNotificationsBySchedule}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('pauseNotificationsBySchedule', event.target.checked);
|
||||
|
|
@ -77,8 +92,8 @@ export function Notifications(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.NotificationsMuteAudio')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.pauseNotificationsMuteAudio}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('pauseNotificationsMuteAudio', event.target.checked);
|
||||
|
|
@ -88,11 +103,11 @@ export function Notifications(props: Required<ISectionProps>): JSX.Element {
|
|||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText primary="Show unread count badge" />
|
||||
<ListItemText primary='Show unread count badge' />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.unreadCountBadge}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('unreadCountBadge', event.target.checked);
|
||||
|
|
@ -109,14 +124,15 @@ export function Notifications(props: Required<ISectionProps>): JSX.Element {
|
|||
title: t('Preference.TestNotification'),
|
||||
body: t('Preference.ItIsWorking'),
|
||||
});
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('Preference.TestNotification')}
|
||||
secondary={(() => {
|
||||
// only show this message on macOS Catalina 10.15 & above
|
||||
if (platform === 'darwin' && oSVersion !== undefined && semver.gte(oSVersion, '10.15.0')) {
|
||||
return (
|
||||
<Trans t={t} i18nKey="Preference.TestNotificationDescription">
|
||||
<Trans t={t} i18nKey='Preference.TestNotificationDescription'>
|
||||
<span>
|
||||
If notifications dont show up, make sure you enable notifications in
|
||||
<b>macOS Preferences → Notifications → TidGi</b>.
|
||||
|
|
@ -126,25 +142,25 @@ export function Notifications(props: Required<ISectionProps>): JSX.Element {
|
|||
}
|
||||
})()}
|
||||
/>
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
secondary={
|
||||
<Trans t={t} i18nKey="Preference.HowToEnableNotifications">
|
||||
<Trans t={t} i18nKey='Preference.HowToEnableNotifications'>
|
||||
<span>
|
||||
TidGi supports notifications out of the box. But for some cases, to receive notifications, you will need to manually configure
|
||||
additional web app settings.
|
||||
TidGi supports notifications out of the box. But for some cases, to receive notifications, you will need to manually configure additional web app settings.
|
||||
</span>
|
||||
<Link
|
||||
onClick={async () =>
|
||||
await window.service.native.open('https://github.com/atomery/webcatalog/wiki/How-to-Enable-Notifications-in-Web-Apps')
|
||||
}
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://github.com/atomery/webcatalog/wiki/How-to-Enable-Notifications-in-Web-Apps');
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== 'Enter') return;
|
||||
void window.service.native.open('https://github.com/atomery/webcatalog/wiki/How-to-Enable-Notifications-in-Web-Apps');
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Learn more
|
||||
</Link>
|
||||
<span>.</span>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { Divider, List, ListItemSecondaryAction, Switch } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Divider, List, ListItemSecondaryAction, Switch } from '@material-ui/core';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function Performance(props: Required<ISectionProps>): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -16,16 +16,14 @@ export function Performance(props: Required<ISectionProps>): JSX.Element {
|
|||
<SectionTitle ref={props.sections.performance.ref}>{t('Preference.Performance')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem>
|
||||
<ListItemText primary={t('Preference.HibernateAllUnusedWorkspaces')} secondary={t('Preference.HibernateAllUnusedWorkspacesDescription')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.hibernateUnusedWorkspacesAtLaunch}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('hibernateUnusedWorkspacesAtLaunch', event.target.checked);
|
||||
|
|
@ -39,8 +37,8 @@ export function Performance(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={t('Preference.hardwareAcceleration')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.useHardwareAcceleration}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('useHardwareAcceleration', event.target.checked);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import { Divider, List, ListItemSecondaryAction, Switch } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import React from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Link, Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { Link, ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function PrivacyAndSecurity(props: Required<ISectionProps>): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -17,16 +17,14 @@ export function PrivacyAndSecurity(props: Required<ISectionProps>): JSX.Element
|
|||
<SectionTitle ref={props.sections.privacy.ref}>{t('Preference.PrivacyAndSecurity')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem>
|
||||
<ListItemText primary={t('Preference.ShareBrowsingData')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.shareWorkspaceBrowsingData}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('shareWorkspaceBrowsingData', event.target.checked);
|
||||
|
|
@ -40,16 +38,17 @@ export function PrivacyAndSecurity(props: Required<ISectionProps>): JSX.Element
|
|||
<ListItemText
|
||||
primary={t('Preference.IgnoreCertificateErrors')}
|
||||
secondary={
|
||||
<Trans t={t} i18nKey="Preference.IgnoreCertificateErrorsDescription">
|
||||
<span>Not recommended. </span>
|
||||
<Trans t={t} i18nKey='Preference.IgnoreCertificateErrorsDescription'>
|
||||
<span>Not recommended.</span>
|
||||
<Link
|
||||
onClick={async () =>
|
||||
await window.service.native.open('https://groups.google.com/a/chromium.org/d/msg/security-dev/mB2KJv_mMzM/ddMteO9RjXEJ')
|
||||
}
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://groups.google.com/a/chromium.org/d/msg/security-dev/mB2KJv_mMzM/ddMteO9RjXEJ');
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key !== 'Enter') return;
|
||||
void window.service.native.open('https://groups.google.com/a/chromium.org/d/msg/security-dev/mB2KJv_mMzM/ddMteO9RjXEJ');
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
Learn more
|
||||
</Link>
|
||||
.
|
||||
|
|
@ -58,8 +57,8 @@ export function PrivacyAndSecurity(props: Required<ISectionProps>): JSX.Element
|
|||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.ignoreCertificateErrors}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('ignoreCertificateErrors', event.target.checked);
|
||||
|
|
@ -69,15 +68,23 @@ export function PrivacyAndSecurity(props: Required<ISectionProps>): JSX.Element
|
|||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem button onClick={async () => await window.service.workspaceView.clearBrowsingDataWithConfirm()}>
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => {
|
||||
await window.service.workspaceView.clearBrowsingDataWithConfirm();
|
||||
}}
|
||||
>
|
||||
<ListItemText primary={t('Preference.ClearBrowsingData')} secondary={t('Preference.ClearBrowsingDataDescription')} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem
|
||||
button
|
||||
onClick={async () => await window.service.native.open('https://github.com/tiddly-gittly/TidGi-Desktop/blob/master/PrivacyPolicy.md')}>
|
||||
<ListItemText primary="Privacy Policy" />
|
||||
onClick={async () => {
|
||||
await window.service.native.open('https://github.com/tiddly-gittly/TidGi-Desktop/blob/master/PrivacyPolicy.md');
|
||||
}}
|
||||
>
|
||||
<ListItemText primary='Privacy Policy' />
|
||||
</ListItem>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import { Divider, List, ListItemSecondaryAction, Switch } from '@material-ui/core';
|
||||
import TimePicker from '@material-ui/lab/TimePicker';
|
||||
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||
import setDate from 'date-fns/setDate';
|
||||
import setMonth from 'date-fns/setMonth';
|
||||
import setYear from 'date-fns/setYear';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||
import setYear from 'date-fns/setYear';
|
||||
import setMonth from 'date-fns/setMonth';
|
||||
import setDate from 'date-fns/setDate';
|
||||
import TimePicker from '@material-ui/lab/TimePicker';
|
||||
import { Divider, List, ListItemSecondaryAction, Switch } from '@material-ui/core';
|
||||
|
||||
import { TokenForm } from '../../../components/TokenForm';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, TextField, TimePickerContainer, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { ListItem, ListItemText, Paper, SectionTitle, TextField, TimePickerContainer } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function Sync(props: Required<ISectionProps>): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -24,9 +24,7 @@ export function Sync(props: Required<ISectionProps>): JSX.Element {
|
|||
<SectionTitle ref={props.sections.sync.ref}>{t('Preference.Sync')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem>
|
||||
<TokenForm />
|
||||
|
|
@ -36,8 +34,8 @@ export function Sync(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={`${t('Preference.SyncBeforeShutdown')} (Mac/Linux)`} secondary={t('Preference.SyncBeforeShutdownDescription')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.syncBeforeShutdown}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('syncBeforeShutdown', event.target.checked);
|
||||
|
|
@ -50,8 +48,8 @@ export function Sync(props: Required<ISectionProps>): JSX.Element {
|
|||
<ListItemText primary={`${t('Preference.SyncOnlyWhenNoDraft')}`} secondary={t('Preference.SyncOnlyWhenNoDraftDescription')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.syncOnlyWhenNoDraft}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('syncOnlyWhenNoDraft', event.target.checked);
|
||||
|
|
@ -65,10 +63,10 @@ export function Sync(props: Required<ISectionProps>): JSX.Element {
|
|||
<TimePickerContainer>
|
||||
<TimePicker
|
||||
ampm={false}
|
||||
openTo="hours"
|
||||
openTo='hours'
|
||||
views={['hours', 'minutes', 'seconds']}
|
||||
inputFormat="HH:mm:ss"
|
||||
mask="__:__:__"
|
||||
inputFormat='HH:mm:ss'
|
||||
mask='__:__:__'
|
||||
renderInput={(timeProps) => <TextField {...timeProps} />}
|
||||
value={fromUnixTime(preference.syncDebounceInterval / 1000 + new Date().getTimezoneOffset() * 60)}
|
||||
onChange={async (date) => {
|
||||
|
|
@ -78,8 +76,12 @@ export function Sync(props: Required<ISectionProps>): JSX.Element {
|
|||
await window.service.preference.set('syncDebounceInterval', utcTime);
|
||||
props.requestRestartCountDown();
|
||||
}}
|
||||
onClose={async () => await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: false })}
|
||||
onOpen={async () => await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: true })}
|
||||
onClose={async () => {
|
||||
await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: false });
|
||||
}}
|
||||
onOpen={async () => {
|
||||
await window.service.window.updateWindowMeta(WindowNames.preferences, { preventClosingWindow: true });
|
||||
}}
|
||||
/>
|
||||
</TimePickerContainer>
|
||||
</ListItem>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { List, MenuItem } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import PopUpMenuItem from '@/components/PopUpMenuItem';
|
||||
import { getOpenAtLoginString, useSystemPreferenceObservable } from '@services/systemPreferences/hooks';
|
||||
import { ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
|
||||
export function System(props: ISectionProps): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -18,25 +18,39 @@ export function System(props: ISectionProps): JSX.Element {
|
|||
<SectionTitle ref={props.sections.system.ref}>{t('Preference.System')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{systemPreference === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{systemPreference === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<PopUpMenuItem
|
||||
id="openAtLogin"
|
||||
id='openAtLogin'
|
||||
buttonElement={
|
||||
<ListItem button>
|
||||
<ListItemText primary={t('Preference.OpenAtLogin')} secondary={getOpenAtLoginString(systemPreference.openAtLogin)} />
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
}>
|
||||
<MenuItem dense onClick={async () => await window.service.systemPreference.setSystemPreference('openAtLogin', 'yes')}>
|
||||
}
|
||||
>
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={async () => {
|
||||
await window.service.systemPreference.setSystemPreference('openAtLogin', 'yes');
|
||||
}}
|
||||
>
|
||||
{t('Yes')}
|
||||
</MenuItem>
|
||||
<MenuItem dense onClick={async () => await window.service.systemPreference.setSystemPreference('openAtLogin', 'yes-hidden')}>
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={async () => {
|
||||
await window.service.systemPreference.setSystemPreference('openAtLogin', 'yes-hidden');
|
||||
}}
|
||||
>
|
||||
{t('Preference.OpenAtLoginMinimized')}
|
||||
</MenuItem>
|
||||
<MenuItem dense onClick={async () => await window.service.systemPreference.setSystemPreference('openAtLogin', 'no')}>
|
||||
<MenuItem
|
||||
dense
|
||||
onClick={async () => {
|
||||
await window.service.systemPreference.setSystemPreference('openAtLogin', 'no');
|
||||
}}
|
||||
>
|
||||
{t('No')}
|
||||
</MenuItem>
|
||||
</PopUpMenuItem>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import useDebouncedCallback from 'beautiful-react-hooks/useDebouncedCallback';
|
||||
|
||||
import { List } from '@material-ui/core';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { useUserInfoObservable } from '@services/auth/hooks';
|
||||
import { ListItemText, ListItemVertical, Paper, SectionTitle, TextField } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function TiddlyWiki(props: Partial<ISectionProps>): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -28,9 +28,7 @@ export function TiddlyWiki(props: Partial<ISectionProps>): JSX.Element {
|
|||
<SectionTitle ref={props.sections?.wiki?.ref}>{t('Preference.TiddlyWiki')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{userInfo === undefined ? (
|
||||
<ListItemVertical>{t('Loading')}</ListItemVertical>
|
||||
) : (
|
||||
{userInfo === undefined ? <ListItemVertical>{t('Loading')}</ListItemVertical> : (
|
||||
<ListItemVertical>
|
||||
<ListItemText primary={t('Preference.WikiMetaData')} secondary={t('Preference.WikiMetaDataDescription')} />
|
||||
<TextField
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Divider, List, ListItemSecondaryAction, Switch } from '@material-ui/core';
|
||||
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { ISectionProps } from '../useSections';
|
||||
import { Paper, SectionTitle, ListItem, ListItemText } from '../PreferenceComponents';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { useUpdaterObservable, getUpdaterMessage } from '@services/updater/hooks';
|
||||
import { IUpdaterStatus } from '@services/updater/interface';
|
||||
import { latestStableUpdateUrl } from '@/constants/urls';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { getUpdaterMessage, useUpdaterObservable } from '@services/updater/hooks';
|
||||
import { IUpdaterStatus } from '@services/updater/interface';
|
||||
import { ListItem, ListItemText, Paper, SectionTitle } from '../PreferenceComponents';
|
||||
import type { ISectionProps } from '../useSections';
|
||||
|
||||
export function Updates(props: Required<ISectionProps>): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -20,33 +20,34 @@ export function Updates(props: Required<ISectionProps>): JSX.Element {
|
|||
<SectionTitle ref={props.sections.updates.ref}>{t('Preference.Network')}</SectionTitle>
|
||||
<Paper elevation={0}>
|
||||
<List dense disablePadding>
|
||||
{preference === undefined || updaterMetaData === undefined ? (
|
||||
<ListItem>{t('Loading')}</ListItem>
|
||||
) : (
|
||||
{preference === undefined || updaterMetaData === undefined ? <ListItem>{t('Loading')}</ListItem> : (
|
||||
<>
|
||||
<ListItem
|
||||
button
|
||||
onClick={
|
||||
updaterMetaData.status === IUpdaterStatus.updateAvailable
|
||||
? async () => await window.service.native.open(updaterMetaData.info?.latestReleasePageUrl ?? latestStableUpdateUrl)
|
||||
: async () => await window.service.updater.checkForUpdates()
|
||||
}
|
||||
disabled={updaterMetaData.status === IUpdaterStatus.checkingForUpdate || updaterMetaData.status === IUpdaterStatus.downloadProgress}>
|
||||
onClick={updaterMetaData.status === IUpdaterStatus.updateAvailable
|
||||
? async () => {
|
||||
await window.service.native.open(updaterMetaData.info?.latestReleasePageUrl ?? latestStableUpdateUrl);
|
||||
}
|
||||
: async () => {
|
||||
await window.service.updater.checkForUpdates();
|
||||
}}
|
||||
disabled={updaterMetaData.status === IUpdaterStatus.checkingForUpdate || updaterMetaData.status === IUpdaterStatus.downloadProgress}
|
||||
>
|
||||
{updaterMetaData.status !== undefined && (
|
||||
<ListItemText
|
||||
primary={t(`Updater.${updaterMetaData.status}`)}
|
||||
secondary={getUpdaterMessage(updaterMetaData.status, updaterMetaData.info, t)}
|
||||
/>
|
||||
)}
|
||||
<ChevronRightIcon color="action" />
|
||||
<ChevronRightIcon color='action' />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText primary={t('Preference.ReceivePreReleaseUpdates')} />
|
||||
<ListItemSecondaryAction>
|
||||
<Switch
|
||||
edge="end"
|
||||
color="primary"
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={preference.allowPrerelease}
|
||||
onChange={async (event) => {
|
||||
await window.service.preference.set('allowPrerelease', event.target.checked);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import { useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SvgIconTypeMap } from '@material-ui/core';
|
||||
import { OverridableComponent } from '@material-ui/core/OverridableComponent';
|
||||
import BuildIcon from '@material-ui/icons/Build';
|
||||
import CloudDownloadIcon from '@material-ui/icons/CloudDownload';
|
||||
import CodeIcon from '@material-ui/icons/Code';
|
||||
import GitHubIcon from '@material-ui/icons/GitHub';
|
||||
import LanguageIcon from '@material-ui/icons/Language';
|
||||
import MenuBookIcon from '@material-ui/icons/MenuBook';
|
||||
import MoreHorizIcon from '@material-ui/icons/MoreHoriz';
|
||||
import NotificationsIcon from '@material-ui/icons/Notifications';
|
||||
import PowerIcon from '@material-ui/icons/Power';
|
||||
|
|
@ -13,10 +17,6 @@ import SecurityIcon from '@material-ui/icons/Security';
|
|||
import StorefrontIcon from '@material-ui/icons/Storefront';
|
||||
import SystemUpdateAltIcon from '@material-ui/icons/SystemUpdateAlt';
|
||||
import WidgetsIcon from '@material-ui/icons/Widgets';
|
||||
import GitHubIcon from '@material-ui/icons/GitHub';
|
||||
import MenuBookIcon from '@material-ui/icons/MenuBook';
|
||||
import { OverridableComponent } from '@material-ui/core/OverridableComponent';
|
||||
import { SvgIconTypeMap } from '@material-ui/core';
|
||||
|
||||
import { PreferenceSections } from '@services/preferences/interface';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import ButtonRaw from '@material-ui/core/Button';
|
||||
import Checkbox from '@material-ui/core/Checkbox';
|
||||
|
|
@ -10,8 +10,8 @@ import ListItem from '@material-ui/core/ListItem';
|
|||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
|
||||
import { hunspellLanguagesMap, HunspellLanguages } from '../../constants/hunspellLanguages';
|
||||
import { usePreferenceObservable } from '@services/preferences/hooks';
|
||||
import { HunspellLanguages, hunspellLanguagesMap } from '../../constants/hunspellLanguages';
|
||||
|
||||
const Root = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -53,7 +53,7 @@ export default function SpellcheckLanguages(): JSX.Element {
|
|||
}
|
||||
return (
|
||||
<Root>
|
||||
<div id="test" data-usage="For spectron automating testing" />
|
||||
<div id='test' data-usage='For spectron automating testing' />
|
||||
<Helmet>
|
||||
<title>{t('Preference.SpellCheckLanguages')}</title>
|
||||
</Helmet>
|
||||
|
|
@ -72,10 +72,11 @@ export default function SpellcheckLanguages(): JSX.Element {
|
|||
} else {
|
||||
void window.service.preference.set('spellcheckLanguages', [...preference.spellcheckLanguages, code]);
|
||||
}
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<ListItemIcon>
|
||||
<Checkbox
|
||||
edge="start"
|
||||
edge='start'
|
||||
checked={preference.spellcheckLanguages.includes(code)}
|
||||
disabled={preference.spellcheckLanguages.length < 2 && preference.spellcheckLanguages.includes(code)}
|
||||
/>
|
||||
|
|
@ -85,10 +86,16 @@ export default function SpellcheckLanguages(): JSX.Element {
|
|||
))}
|
||||
</Top>
|
||||
<Bottom>
|
||||
<Button color="primary" disabled>
|
||||
<Button color='primary' disabled>
|
||||
This Page is Auto Saved
|
||||
</Button>
|
||||
<Button onClick={async () => await window.remote.closeCurrentWindow()}>Close</Button>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
await window.remote.closeCurrentWindow();
|
||||
}}
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</Bottom>
|
||||
</Root>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,29 +1,36 @@
|
|||
import React from 'react';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import React from 'react';
|
||||
|
||||
import Main from './Main';
|
||||
import AboutPage from './About';
|
||||
import { AddWorkspace as DialogAddWorkspace } from './AddWorkspace';
|
||||
import EditWorkspace from './EditWorkspace';
|
||||
import Main from './Main';
|
||||
import DialogNotifications from './Notifications';
|
||||
import DialogPreferences from './Preferences';
|
||||
import SpellcheckLanguages from './SpellcheckLanguages';
|
||||
|
||||
export function Pages(): JSX.Element {
|
||||
switch (window.meta.windowName) {
|
||||
case WindowNames.about:
|
||||
case WindowNames.about: {
|
||||
return <AboutPage />;
|
||||
case WindowNames.addWorkspace:
|
||||
}
|
||||
case WindowNames.addWorkspace: {
|
||||
return <DialogAddWorkspace />;
|
||||
case WindowNames.editWorkspace:
|
||||
}
|
||||
case WindowNames.editWorkspace: {
|
||||
return <EditWorkspace />;
|
||||
case WindowNames.notifications:
|
||||
}
|
||||
case WindowNames.notifications: {
|
||||
return <DialogNotifications />;
|
||||
case WindowNames.preferences:
|
||||
}
|
||||
case WindowNames.preferences: {
|
||||
return <DialogPreferences />;
|
||||
case WindowNames.spellcheck:
|
||||
}
|
||||
case WindowNames.spellcheck: {
|
||||
return <SpellcheckLanguages />;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
return <Main />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// on production build, if we try to redirect to http://localhost:3012 , we will reach chrome-error://chromewebdata/ , but we can easily get back
|
||||
// this happens when we are redirected by OAuth login
|
||||
import { context, window as windowService } from './services';
|
||||
import { windowName } from './browserViewMetaData';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { windowName } from './browserViewMetaData';
|
||||
import { context, window as windowService } from './services';
|
||||
|
||||
const CHECK_LOADED_INTERVAL = 500;
|
||||
let CHROME_ERROR_PATH: string | undefined;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { MetaDataChannel } from '@/constants/channels';
|
||||
import { WindowNames, WindowMeta } from '@services/windows/WindowProperties';
|
||||
import { WindowMeta, WindowNames } from '@services/windows/WindowProperties';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
const metaDataArguments = process.argv
|
||||
.filter((item) => item.startsWith(MetaDataChannel.browserViewMetaData))
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { preloadBindings } from '@services/libs/i18n/preloadBindings';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
const i18n = {
|
||||
i18nextElectronBackend: preloadBindings(ipcRenderer),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import { WikiChannel } from '@/constants/channels';
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
export const logMethods = {
|
||||
/**
|
||||
|
|
@ -7,7 +7,7 @@ export const logMethods = {
|
|||
* @param messageSetter can be wikiCreationMessageSetter from a useEffect
|
||||
* @returns
|
||||
*/
|
||||
registerWikiCreationMessage: (messageSetter: (message: string) => void): (() => void) => {
|
||||
registerWikiCreationMessage: (messageSetter: (message: string) => void): () => void => {
|
||||
const handleNextMessage = (_event: Electron.IpcRendererEvent, message: string): void => {
|
||||
messageSetter(message);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { contextBridge, ipcRenderer, MenuItemConstructorOptions, webFrame } from 'electron';
|
||||
import { ViewChannel, WindowChannel } from '@/constants/channels';
|
||||
import { rendererMenuItemProxy } from '@services/menu/contextMenu/rendererMenuItemProxy';
|
||||
import { IOnContextMenuInfo } from '@services/menu/interface';
|
||||
import { contextBridge, ipcRenderer, MenuItemConstructorOptions, webFrame } from 'electron';
|
||||
|
||||
import * as service from './services';
|
||||
import { windowName } from './browserViewMetaData';
|
||||
import * as service from './services';
|
||||
|
||||
export const remoteMethods = {
|
||||
buildContextMenuAndPopup: async (menus: MenuItemConstructorOptions[], parameters: IOnContextMenuInfo): Promise<() => void> => {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@
|
|||
import { createProxy } from 'electron-ipc-cat/client';
|
||||
import { AsyncifyProxy } from 'electron-ipc-cat/common';
|
||||
|
||||
import { IAuthenticationService, AuthenticationServiceIPCDescriptor } from '@services/auth/interface';
|
||||
import { IContextService, ContextServiceIPCDescriptor } from '@services/context/interface';
|
||||
import { IGitService, GitServiceIPCDescriptor } from '@services/git/interface';
|
||||
import { AuthenticationServiceIPCDescriptor, IAuthenticationService } from '@services/auth/interface';
|
||||
import { ContextServiceIPCDescriptor, IContextService } from '@services/context/interface';
|
||||
import { GitServiceIPCDescriptor, IGitService } from '@services/git/interface';
|
||||
import { IMenuService, MenuServiceIPCDescriptor } from '@services/menu/interface';
|
||||
import { INativeService, NativeServiceIPCDescriptor } from '@services/native/interface';
|
||||
import { INotificationService, NotificationServiceIPCDescriptor } from '@services/notifications/interface';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'reflect-metadata';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { IServicesWithoutObservables, IServicesWithOnlyObservables } from 'electron-ipc-cat/common';
|
||||
import { IServicesWithOnlyObservables, IServicesWithoutObservables } from 'electron-ipc-cat/common';
|
||||
|
||||
import './common/test';
|
||||
import './common/i18n';
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
import { Channels, WorkspaceChannel } from '@/constants/channels';
|
||||
import { webFrame } from 'electron';
|
||||
import { WorkspaceChannel, Channels } from '@/constants/channels';
|
||||
import './wikiOperation';
|
||||
import { preference, workspace, workspaceView, menu } from './common/services';
|
||||
import { IPossibleWindowMeta, WindowMeta, WindowNames } from '@services/windows/WindowProperties';
|
||||
import { browserViewMetaData, windowName } from './common/browserViewMetaData';
|
||||
import { menu, preference, workspace, workspaceView } from './common/services';
|
||||
|
||||
let handled = false;
|
||||
const handleLoaded = (event: string): void => {
|
||||
|
|
@ -64,11 +64,15 @@ async function executeJavaScriptInBrowserView(): Promise<void> {
|
|||
|
||||
if (windowName === WindowNames.view) {
|
||||
// try to load as soon as dom is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => handleLoaded('document.on("DOMContentLoaded")'));
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
handleLoaded('document.on("DOMContentLoaded")');
|
||||
});
|
||||
// if user navigates between the same website
|
||||
// DOMContentLoaded might not be triggered so double check with 'onload'
|
||||
// https://github.com/atomery/webcatalog/issues/797
|
||||
window.addEventListener('load', () => handleLoaded('window.on("onload")'));
|
||||
window.addEventListener('load', () => {
|
||||
handleLoaded('window.on("onload")');
|
||||
});
|
||||
window.addEventListener('message', async (event?: MessageEvent<{ type?: Channels; workspaceID?: string } | undefined>) => {
|
||||
// set workspace to active when its notification is clicked
|
||||
if (event?.data?.type === WorkspaceChannel.focusWorkspace) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
*
|
||||
* You can use wrapped method in `services/wiki/index.ts` 's `wikiOperation()` instead. Available operations are registered in `src/services/wiki/wikiOperations.ts`
|
||||
*/
|
||||
import { ipcRenderer, webFrame } from 'electron';
|
||||
import { WikiChannel } from '@/constants/channels';
|
||||
import { ipcRenderer, webFrame } from 'electron';
|
||||
|
||||
export const wikiOperations = {
|
||||
[WikiChannel.setState]: async (stateKey: string, content: string) => {
|
||||
|
|
@ -26,13 +26,12 @@ export const wikiOperations = {
|
|||
* @param script js statement to be executed, nothing will be returned
|
||||
*/
|
||||
async function executeTWJavaScriptWhenIdle(script: string, options?: { onlyWhenVisible?: boolean }): Promise<void> {
|
||||
const executeHandlerCode =
|
||||
options?.onlyWhenVisible === true
|
||||
? `
|
||||
const executeHandlerCode = options?.onlyWhenVisible === true
|
||||
? `
|
||||
if (document.visibilityState === 'visible') {
|
||||
handler();
|
||||
}`
|
||||
: `handler();`;
|
||||
: `handler();`;
|
||||
await webFrame.executeJavaScript(`
|
||||
new Promise((resolve, reject) => {
|
||||
const handler = () => {
|
||||
|
|
@ -98,8 +97,9 @@ ipcRenderer.on(WikiChannel.syncProgress, async (event, message: string) => {
|
|||
{ onlyWhenVisible: true },
|
||||
);
|
||||
});
|
||||
ipcRenderer.on(WikiChannel.setState, (_event: Electron.IpcRendererEvent, ...rest: Parameters<typeof wikiOperations[WikiChannel.setState]>) =>
|
||||
wikiOperations[WikiChannel.setState](...rest),
|
||||
ipcRenderer.on(
|
||||
WikiChannel.setState,
|
||||
(_event: Electron.IpcRendererEvent, ...rest: Parameters<typeof wikiOperations[WikiChannel.setState]>) => wikiOperations[WikiChannel.setState](...rest),
|
||||
);
|
||||
ipcRenderer.on(WikiChannel.generalNotification, async (event, message: string) => {
|
||||
await executeTWJavaScriptWhenIdle(`
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
||||
/* eslint-disable promise/always-return */
|
||||
import React from 'react';
|
||||
import i18n from 'i18next';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import StyledEngineProvider from '@material-ui/core/StyledEngineProvider';
|
||||
import DateFnsUtils from '@material-ui/lab/AdapterDateFns';
|
||||
import LocalizationProvider from '@material-ui/lab/LocalizationProvider';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
import 'typeface-roboto/index.css';
|
||||
|
||||
import { useThemeObservable } from '@services/theme/hooks';
|
||||
import { darkTheme, lightTheme } from '@services/theme/defaultTheme';
|
||||
import { useThemeObservable } from '@services/theme/hooks';
|
||||
import { initI18N } from './i18n';
|
||||
import 'electron-ipc-cat/fixContextIsolation';
|
||||
import { Pages } from './pages';
|
||||
import { RootStyle } from './components/RootStyle';
|
||||
import { Pages } from './pages';
|
||||
|
||||
function App(): JSX.Element {
|
||||
const theme = useThemeObservable();
|
||||
|
|
@ -41,7 +41,7 @@ function App(): JSX.Element {
|
|||
}
|
||||
|
||||
void window.remote.setVisualZoomLevelLimits(1, 1);
|
||||
const container = document.getElementById('app');
|
||||
const container = document.querySelector('#app');
|
||||
const root = createRoot(container!);
|
||||
root.render(<App />);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import useObservable from 'beautiful-react-hooks/useObservable';
|
||||
import { IUserInfos } from './interface';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { IGitUserInfos } from '@services/git/interface';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import useObservable from 'beautiful-react-hooks/useObservable';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IUserInfos } from './interface';
|
||||
|
||||
export function useUserInfoObservable(): IUserInfos | undefined {
|
||||
const [userInfo, userInfoSetter] = useState<IUserInfos | undefined>();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
/* eslint-disable @typescript-eslint/require-await */
|
||||
/* eslint-disable unicorn/no-null */
|
||||
import { debounce } from 'lodash';
|
||||
import { injectable } from 'inversify';
|
||||
import settings from 'electron-settings';
|
||||
import { IAuthingUserInfo, SupportedStorageServices } from '@services/types';
|
||||
import { IAuthenticationService, IUserInfos, ServiceBranchTypes, ServiceEmailTypes, ServiceTokenTypes, ServiceUserNameTypes } from './interface';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { IGitUserInfos } from '@services/git/interface';
|
||||
import { IAuthingUserInfo, SupportedStorageServices } from '@services/types';
|
||||
import settings from 'electron-settings';
|
||||
import { injectable } from 'inversify';
|
||||
import { debounce } from 'lodash';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { IAuthenticationService, IUserInfos, ServiceBranchTypes, ServiceEmailTypes, ServiceTokenTypes, ServiceUserNameTypes } from './interface';
|
||||
|
||||
const defaultUserInfos = {
|
||||
userName: '',
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/* eslint-disable unicorn/no-null */
|
||||
import { ProxyPropertyType } from 'electron-ipc-cat/common';
|
||||
import { AuthenticationChannel } from '@/constants/channels';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { IGitUserInfos } from '@services/git/interface';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { ProxyPropertyType } from 'electron-ipc-cat/common';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
export type ServiceTokenTypes = `${SupportedStorageServices}-token`;
|
||||
export const getServiceTokenTypes = (serviceType: SupportedStorageServices): ServiceTokenTypes => `${serviceType}-token`;
|
||||
|
|
@ -25,13 +25,15 @@ export const getServiceBranchTypes = (serviceType: SupportedStorageServices): Se
|
|||
/** Git push: Git commit message branch, you may use different branch for different storage service */
|
||||
type BranchRecord = Record<ServiceBranchTypes, string>;
|
||||
|
||||
export type IUserInfos = {
|
||||
/** Default UserName in TiddlyWiki, each wiki can have different username, but fallback to this if not specific on */
|
||||
userName: string;
|
||||
} & Partial<TokenRecord> &
|
||||
Partial<UserNameRecord> &
|
||||
Partial<EmailRecord> &
|
||||
Partial<BranchRecord>;
|
||||
export type IUserInfos =
|
||||
& {
|
||||
/** Default UserName in TiddlyWiki, each wiki can have different username, but fallback to this if not specific on */
|
||||
userName: string;
|
||||
}
|
||||
& Partial<TokenRecord>
|
||||
& Partial<UserNameRecord>
|
||||
& Partial<EmailRecord>
|
||||
& Partial<BranchRecord>;
|
||||
|
||||
/**
|
||||
* Handle login to Github GitLab Coding.net
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
/* eslint-disable @typescript-eslint/require-await */
|
||||
import { app, net } from 'electron';
|
||||
import process from 'process';
|
||||
import os from 'os';
|
||||
import { isElectronDevelopment } from '@/constants/isElectronDevelopment';
|
||||
import { app, net } from 'electron';
|
||||
import { injectable } from 'inversify';
|
||||
import os from 'os';
|
||||
import process from 'process';
|
||||
|
||||
import { IContextService, IContext, IPaths, IConstants } from './interface';
|
||||
import * as paths from '@/constants/paths';
|
||||
import * as appPaths from '@/constants/appPaths';
|
||||
import { tiddlywikiLanguagesMap, supportedLanguagesMap } from '@/constants/languages';
|
||||
import { supportedLanguagesMap, tiddlywikiLanguagesMap } from '@/constants/languages';
|
||||
import * as paths from '@/constants/paths';
|
||||
import { IConstants, IContext, IContextService, IPaths } from './interface';
|
||||
|
||||
@injectable()
|
||||
export class ContextService implements IContextService {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ProxyPropertyType } from 'electron-ipc-cat/common';
|
||||
import { ContextChannel } from '@/constants/channels';
|
||||
import { ProxyPropertyType } from 'electron-ipc-cat/common';
|
||||
|
||||
export interface IPaths {
|
||||
CHROME_ERROR_PATH: string;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
/* eslint-disable @typescript-eslint/no-misused-promises */
|
||||
import 'source-map-support/register';
|
||||
import { expose } from 'threads/worker';
|
||||
import { Observable } from 'rxjs';
|
||||
import { clone, commitAndSync, GitStep, ILoggerContext, initGit, getModifiedFileList, getRemoteUrl, SyncParameterMissingError } from 'git-sync-js';
|
||||
import type { ICommitAndSyncConfigs, IGitLogMessage, IGitUserInfos } from './interface';
|
||||
import { defaultGitInfo } from './defaultGitInfo';
|
||||
import { WikiChannel } from '@/constants/channels';
|
||||
import type { IWorkspace } from '@services/workspaces/interface';
|
||||
import { clone, commitAndSync, getModifiedFileList, getRemoteUrl, GitStep, ILoggerContext, initGit, SyncParameterMissingError } from 'git-sync-js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { expose } from 'threads/worker';
|
||||
import { defaultGitInfo } from './defaultGitInfo';
|
||||
import type { ICommitAndSyncConfigs, IGitLogMessage, IGitUserInfos } from './interface';
|
||||
|
||||
function initWikiGit(wikiFolderPath: string, syncImmediately?: boolean, remoteUrl?: string, userInfo?: IGitUserInfos): Observable<IGitLogMessage> {
|
||||
return new Observable<IGitLogMessage>((observer) => {
|
||||
|
|
@ -25,10 +25,12 @@ function initWikiGit(wikiFolderPath: string, syncImmediately?: boolean, remoteUr
|
|||
userInfo,
|
||||
defaultGitInfo,
|
||||
logger: {
|
||||
debug: (message: string, context: ILoggerContext): unknown =>
|
||||
observer.next({ message, level: 'debug', meta: { callerFunction: 'initWikiGit', ...context } }),
|
||||
warn: (message: string, context: ILoggerContext): unknown =>
|
||||
observer.next({ message, level: 'warn', meta: { callerFunction: 'initWikiGit', ...context } }),
|
||||
debug: (message: string, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'debug', meta: { callerFunction: 'initWikiGit', ...context } });
|
||||
},
|
||||
warn: (message: string, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'warn', meta: { callerFunction: 'initWikiGit', ...context } });
|
||||
},
|
||||
info: (message: GitStep, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'info', meta: { handler: WikiChannel.createProgress, callerFunction: 'initWikiGit', ...context } });
|
||||
},
|
||||
|
|
@ -41,10 +43,12 @@ function initWikiGit(wikiFolderPath: string, syncImmediately?: boolean, remoteUr
|
|||
userInfo,
|
||||
defaultGitInfo,
|
||||
logger: {
|
||||
debug: (message: string, context: ILoggerContext): unknown =>
|
||||
observer.next({ message, level: 'debug', meta: { callerFunction: 'initWikiGit', ...context } }),
|
||||
warn: (message: string, context: ILoggerContext): unknown =>
|
||||
observer.next({ message, level: 'warn', meta: { callerFunction: 'initWikiGit', ...context } }),
|
||||
debug: (message: string, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'debug', meta: { callerFunction: 'initWikiGit', ...context } });
|
||||
},
|
||||
warn: (message: string, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'warn', meta: { callerFunction: 'initWikiGit', ...context } });
|
||||
},
|
||||
info: (message: GitStep, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'info', meta: { handler: WikiChannel.createProgress, callerFunction: 'initWikiGit', ...context } });
|
||||
},
|
||||
|
|
@ -52,14 +56,17 @@ function initWikiGit(wikiFolderPath: string, syncImmediately?: boolean, remoteUr
|
|||
});
|
||||
}
|
||||
void task.then(
|
||||
() => observer.complete(),
|
||||
(error) => observer.error(error),
|
||||
() => {
|
||||
observer.complete();
|
||||
},
|
||||
(error) => {
|
||||
observer.error(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} wikiFolderPath
|
||||
* @param {string} remoteUrl
|
||||
* @param {{ login: string, email: string, accessToken: string }} userInfo
|
||||
|
|
@ -71,18 +78,24 @@ function commitAndSyncWiki(workspace: IWorkspace, configs: ICommitAndSyncConfigs
|
|||
...configs,
|
||||
defaultGitInfo,
|
||||
logger: {
|
||||
debug: (message: string, context: ILoggerContext): unknown =>
|
||||
observer.next({ message, level: 'debug', meta: { callerFunction: 'commitAndSync', ...context } }),
|
||||
warn: (message: string, context: ILoggerContext): unknown =>
|
||||
observer.next({ message, level: 'warn', meta: { callerFunction: 'commitAndSync', ...context } }),
|
||||
debug: (message: string, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'debug', meta: { callerFunction: 'commitAndSync', ...context } });
|
||||
},
|
||||
warn: (message: string, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'warn', meta: { callerFunction: 'commitAndSync', ...context } });
|
||||
},
|
||||
info: (message: GitStep, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'info', meta: { handler: WikiChannel.syncProgress, id: workspace.id, callerFunction: 'commitAndSync', ...context } });
|
||||
},
|
||||
},
|
||||
filesToIgnore: ['.DS_Store'],
|
||||
}).then(
|
||||
() => observer.complete(),
|
||||
(error) => observer.error(error),
|
||||
() => {
|
||||
observer.complete();
|
||||
},
|
||||
(error) => {
|
||||
observer.error(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -95,15 +108,23 @@ function cloneWiki(repoFolderPath: string, remoteUrl: string, userInfo: IGitUser
|
|||
userInfo,
|
||||
defaultGitInfo,
|
||||
logger: {
|
||||
debug: (message: string, context: ILoggerContext): unknown => observer.next({ message, level: 'debug', meta: { callerFunction: 'clone', ...context } }),
|
||||
warn: (message: string, context: ILoggerContext): unknown => observer.next({ message, level: 'warn', meta: { callerFunction: 'clone', ...context } }),
|
||||
debug: (message: string, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'debug', meta: { callerFunction: 'clone', ...context } });
|
||||
},
|
||||
warn: (message: string, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'warn', meta: { callerFunction: 'clone', ...context } });
|
||||
},
|
||||
info: (message: GitStep, context: ILoggerContext): void => {
|
||||
observer.next({ message, level: 'info', meta: { handler: WikiChannel.createProgress, callerFunction: 'clone', ...context } });
|
||||
},
|
||||
},
|
||||
}).then(
|
||||
() => observer.complete(),
|
||||
(error) => observer.error(error),
|
||||
() => {
|
||||
observer.complete();
|
||||
},
|
||||
(error) => {
|
||||
observer.error(error);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,60 @@
|
|||
import { ipcMain, dialog, net, shell } from 'electron';
|
||||
import { injectable, inject } from 'inversify';
|
||||
import { compact } from 'lodash';
|
||||
import { dialog, ipcMain, net, shell } from 'electron';
|
||||
import {
|
||||
AssumeSyncError,
|
||||
CantSyncGitNotInitializedError,
|
||||
CantSyncInSpecialGitStateAutoFixFailed,
|
||||
getRemoteName,
|
||||
getRemoteUrl,
|
||||
GitPullPushError,
|
||||
GitStep,
|
||||
ModifiedFileList,
|
||||
SyncParameterMissingError,
|
||||
SyncScriptIsInDeadLoopError,
|
||||
getRemoteName,
|
||||
getRemoteUrl,
|
||||
} from 'git-sync-js';
|
||||
import { spawn, Worker, ModuleThread } from 'threads';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { compact } from 'lodash';
|
||||
import { ModuleThread, spawn, Worker } from 'threads';
|
||||
|
||||
import { WikiChannel } from '@/constants/channels';
|
||||
import type { IAuthenticationService, ServiceBranchTypes } from '@services/auth/interface';
|
||||
import { i18n } from '@services/libs/i18n';
|
||||
import { logger } from '@services/libs/log';
|
||||
import type { INativeService } from '@services/native/interface';
|
||||
import type { IPreferenceService } from '@services/preferences/interface';
|
||||
import serviceIdentifier from '@services/serviceIdentifier';
|
||||
import type { IViewService } from '@services/view/interface';
|
||||
import type { IPreferenceService } from '@services/preferences/interface';
|
||||
import type { IWindowService } from '@services/windows/interface';
|
||||
import type { INativeService } from '@services/native/interface';
|
||||
import type { IAuthenticationService, ServiceBranchTypes } from '@services/auth/interface';
|
||||
import type { IWikiService } from '@services/wiki/interface';
|
||||
import { logger } from '@services/libs/log';
|
||||
import { i18n } from '@services/libs/i18n';
|
||||
import { ICommitAndSyncConfigs, IGitLogMessage, IGitService, IGitUserInfos } from './interface';
|
||||
import { WikiChannel } from '@/constants/channels';
|
||||
import { GitWorker } from './gitWorker';
|
||||
import type { IWindowService } from '@services/windows/interface';
|
||||
import { Observer } from 'rxjs';
|
||||
import { GitWorker } from './gitWorker';
|
||||
import { ICommitAndSyncConfigs, IGitLogMessage, IGitService, IGitUserInfos } from './interface';
|
||||
|
||||
import { LOCAL_GIT_DIRECTORY } from '@/constants/appPaths';
|
||||
import { githubDesktopUrl } from '@/constants/urls';
|
||||
import { lazyInject } from '@services/container';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
// @ts-expect-error it don't want .ts
|
||||
// eslint-disable-next-line import/no-webpack-loader-syntax
|
||||
import workerURL from 'threads-plugin/dist/loader?name=gitWorker!./gitWorker.ts';
|
||||
import { LOCAL_GIT_DIRECTORY } from '@/constants/appPaths';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { lazyInject } from '@services/container';
|
||||
import { githubDesktopUrl } from '@/constants/urls';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
import { stepWithChanges } from './stepWithChanges';
|
||||
|
||||
@injectable()
|
||||
export class Git implements IGitService {
|
||||
@lazyInject(serviceIdentifier.Authentication) private readonly authService!: IAuthenticationService;
|
||||
@lazyInject(serviceIdentifier.Wiki) private readonly wikiService!: IWikiService;
|
||||
@lazyInject(serviceIdentifier.Window) private readonly windowService!: IWindowService;
|
||||
@lazyInject(serviceIdentifier.View) private readonly viewService!: IViewService;
|
||||
@lazyInject(serviceIdentifier.NativeService) private readonly nativeService!: INativeService;
|
||||
@lazyInject(serviceIdentifier.Authentication)
|
||||
private readonly authService!: IAuthenticationService;
|
||||
|
||||
@lazyInject(serviceIdentifier.Wiki)
|
||||
private readonly wikiService!: IWikiService;
|
||||
|
||||
@lazyInject(serviceIdentifier.Window)
|
||||
private readonly windowService!: IWindowService;
|
||||
|
||||
@lazyInject(serviceIdentifier.View)
|
||||
private readonly viewService!: IViewService;
|
||||
|
||||
@lazyInject(serviceIdentifier.NativeService)
|
||||
private readonly nativeService!: INativeService;
|
||||
|
||||
private gitWorker?: ModuleThread<GitWorker>;
|
||||
|
||||
|
|
@ -73,7 +82,6 @@ export class Git implements IGitService {
|
|||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} githubRepoName similar to "linonetwo/wiki", string after "https://com/"
|
||||
*/
|
||||
public async updateGitInfoTiddler(workspace: IWorkspace, githubRepoName: string): Promise<void> {
|
||||
|
|
@ -91,7 +99,9 @@ export class Git implements IGitService {
|
|||
browserView.webContents.send(WikiChannel.addTiddler, '$:/GitHub/Repo', githubRepoName, {
|
||||
type: 'text/vnd.tiddlywiki',
|
||||
});
|
||||
ipcMain.once(WikiChannel.addTiddlerDone, () => resolve());
|
||||
ipcMain.once(WikiChannel.addTiddlerDone, () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -236,7 +246,9 @@ export class Git implements IGitService {
|
|||
this.translateAndLogErrorMessage(error as Error);
|
||||
reject(error as Error);
|
||||
},
|
||||
complete: () => resolve(),
|
||||
complete: () => {
|
||||
resolve();
|
||||
},
|
||||
});
|
||||
|
||||
private createFailedDialog(message: string, wikiFolderPath: string): void {
|
||||
|
|
@ -269,7 +281,7 @@ export class Git implements IGitService {
|
|||
public async initWikiGit(wikiFolderPath: string, isSyncedWiki?: boolean, isMainWiki?: boolean, remoteUrl?: string, userInfo?: IGitUserInfos): Promise<void> {
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
const syncImmediately = !!isSyncedWiki && !!isMainWiki;
|
||||
return await new Promise<void>((resolve, reject) => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
this.gitWorker
|
||||
?.initWikiGit(wikiFolderPath, syncImmediately && net.isOnline(), remoteUrl, userInfo)
|
||||
.subscribe(this.getWorkerMessageObserver(resolve, reject));
|
||||
|
|
@ -292,7 +304,9 @@ export class Git implements IGitService {
|
|||
hasChanges = true;
|
||||
}
|
||||
},
|
||||
complete: () => resolve(hasChanges),
|
||||
complete: () => {
|
||||
resolve(hasChanges);
|
||||
},
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
|
@ -306,7 +320,7 @@ export class Git implements IGitService {
|
|||
if (!net.isOnline()) {
|
||||
return;
|
||||
}
|
||||
return await new Promise<void>((resolve, reject) => {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
this.gitWorker?.cloneWiki(repoFolderPath, remoteUrl, userInfo).subscribe(this.getWorkerMessageObserver(resolve, reject));
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { ProxyPropertyType } from 'electron-ipc-cat/common';
|
||||
import { GitChannel } from '@/constants/channels';
|
||||
import { ModifiedFileList } from 'git-sync-js';
|
||||
import type { IWorkspace } from '@services/workspaces/interface';
|
||||
import { ProxyPropertyType } from 'electron-ipc-cat/common';
|
||||
import { ModifiedFileList } from 'git-sync-js';
|
||||
|
||||
export interface IGitUserInfos extends IGitUserInfosWithoutToken {
|
||||
/** Github Login: token */
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
*/
|
||||
import { registerProxy } from 'electron-ipc-cat/server';
|
||||
|
||||
import serviceIdentifier from '@services/serviceIdentifier';
|
||||
import { container } from '@services/container';
|
||||
import serviceIdentifier from '@services/serviceIdentifier';
|
||||
|
||||
import { Authentication } from '@services/auth';
|
||||
import { ContextService } from '@services/context';
|
||||
|
|
@ -45,10 +45,10 @@ import type { IUpdaterService } from '@services/updater/interface';
|
|||
import { UpdaterServiceIPCDescriptor } from '@services/updater/interface';
|
||||
import type { IViewService } from '@services/view/interface';
|
||||
import { ViewServiceIPCDescriptor } from '@services/view/interface';
|
||||
import type { IWikiGitWorkspaceService } from '@services/wikiGitWorkspace/interface';
|
||||
import { WikiGitWorkspaceServiceIPCDescriptor } from '@services/wikiGitWorkspace/interface';
|
||||
import type { IWikiService } from '@services/wiki/interface';
|
||||
import { WikiServiceIPCDescriptor } from '@services/wiki/interface';
|
||||
import type { IWikiGitWorkspaceService } from '@services/wikiGitWorkspace/interface';
|
||||
import { WikiGitWorkspaceServiceIPCDescriptor } from '@services/wikiGitWorkspace/interface';
|
||||
import type { IWindowService } from '@services/windows/interface';
|
||||
import { WindowServiceIPCDescriptor } from '@services/windows/interface';
|
||||
import type { IWorkspaceService } from '@services/workspaces/interface';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { format, isTomorrow, isToday } from 'date-fns';
|
||||
import { format, isToday, isTomorrow } from 'date-fns';
|
||||
|
||||
export const formatDate = (date: Date): string => {
|
||||
if (isToday(date)) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { ipcMain, BrowserView, BrowserWindow } from 'electron';
|
||||
import { Channels } from '@/constants/channels';
|
||||
import { BrowserView, BrowserWindow, ipcMain } from 'electron';
|
||||
|
||||
/**
|
||||
* Get data from a BrowserView
|
||||
|
|
@ -11,6 +11,8 @@ export default async function getFromRenderer<T>(channel: Channels, viewToGetDat
|
|||
const ipcToken = String(Math.random());
|
||||
viewToGetData.webContents.send(channel, { ipcToken });
|
||||
return await new Promise((resolve) => {
|
||||
ipcMain.once(`${channel}-${ipcToken}`, (_event, data: T) => resolve(data));
|
||||
ipcMain.once(`${channel}-${ipcToken}`, (_event, data: T) => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,15 +21,15 @@ export default async function getViewBounds(
|
|||
return {
|
||||
x,
|
||||
y: y + FIND_IN_PAGE_HEIGHT,
|
||||
height: height !== undefined ? height : contentSize[1] - FIND_IN_PAGE_HEIGHT,
|
||||
width: width !== undefined ? width : contentSize[0] - x,
|
||||
height: height === undefined ? contentSize[1] - FIND_IN_PAGE_HEIGHT : height,
|
||||
width: width === undefined ? contentSize[0] - x : width,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
x,
|
||||
y,
|
||||
height: height !== undefined ? height : contentSize[1],
|
||||
width: width !== undefined ? width : contentSize[0] - x,
|
||||
height: height === undefined ? contentSize[1] : height,
|
||||
width: width === undefined ? contentSize[0] - x : width,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import path from 'path';
|
||||
import { isElectronDevelopment } from '@/constants/isElectronDevelopment';
|
||||
import i18next, { TFuncKey, TOptions } from 'i18next';
|
||||
import Backend from 'i18next-fs-backend';
|
||||
import { isElectronDevelopment } from '@/constants/isElectronDevelopment';
|
||||
import path from 'path';
|
||||
|
||||
import { LOCALIZATION_FOLDER } from '@/constants/paths';
|
||||
import { clearMainBindings, mainBindings } from './i18nMainBindings';
|
||||
import changeToDefaultLanguage from './useDefaultLanguage';
|
||||
import { mainBindings, clearMainBindings } from './i18nMainBindings';
|
||||
|
||||
// Workaround for https://github.com/isaachinman/next-i18next/issues/1781
|
||||
declare module 'i18next' {
|
||||
interface TFunction {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-function-type
|
||||
<TKeys extends TFuncKey = string, TInterpolationMap extends object = { [key: string]: any }>(
|
||||
<TKeys extends TFuncKey = string, TInterpolationMap extends object = Record<string, any>>(
|
||||
key: TKeys,
|
||||
options?: TOptions<TInterpolationMap> | string,
|
||||
): string;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
/* eslint-disable unicorn/prevent-abbreviations */
|
||||
import { IpcRenderer, IpcRendererEvent } from 'electron';
|
||||
import { I18NChannels } from '@/constants/channels';
|
||||
import { IpcRenderer, IpcRendererEvent } from 'electron';
|
||||
import { IReadWriteFileRequest } from './types';
|
||||
|
||||
/** This is the code that will go into the preload.js file
|
||||
* in order to set up the contextBridge api
|
||||
*/
|
||||
export const preloadBindings = function (ipcRenderer: IpcRenderer): {
|
||||
export const preloadBindings = function(ipcRenderer: IpcRenderer): {
|
||||
onLanguageChange: (callback: (language: { lng: string }) => unknown) => void;
|
||||
onReceive: (channel: I18NChannels, callback: (readWriteFileArgs: IReadWriteFileRequest) => void) => void;
|
||||
send: (channel: I18NChannels, readWriteFileArgs: IReadWriteFileRequest) => Promise<void>;
|
||||
|
|
@ -22,7 +22,9 @@ export const preloadBindings = function (ipcRenderer: IpcRenderer): {
|
|||
const validChannels = [I18NChannels.readFileResponse, I18NChannels.writeFileResponse];
|
||||
if (validChannels.includes(channel)) {
|
||||
// Deliberately strip event as it includes "sender"
|
||||
ipcRenderer.on(channel, (_event: IpcRendererEvent, arguments_: IReadWriteFileRequest) => callback(arguments_));
|
||||
ipcRenderer.on(channel, (_event: IpcRendererEvent, arguments_: IReadWriteFileRequest) => {
|
||||
callback(arguments_);
|
||||
});
|
||||
}
|
||||
},
|
||||
onLanguageChange: (callback: (language: { lng: string }) => unknown) => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { IWindowService } from '@services/windows/interface';
|
||||
import type { IViewService } from '@services/view/interface';
|
||||
import type { IMenuService } from '@services/menu/interface';
|
||||
import type { IWikiService } from '@services/wiki/interface';
|
||||
import { I18NChannels } from '@/constants/channels';
|
||||
import { supportedLanguagesMap, tiddlywikiLanguagesMap } from '@/constants/languages';
|
||||
import { container } from '@services/container';
|
||||
import type { IMenuService } from '@services/menu/interface';
|
||||
import serviceIdentifier from '@services/serviceIdentifier';
|
||||
import { tiddlywikiLanguagesMap, supportedLanguagesMap } from '@/constants/languages';
|
||||
import type { IViewService } from '@services/view/interface';
|
||||
import type { IWikiService } from '@services/wiki/interface';
|
||||
import type { IWindowService } from '@services/windows/interface';
|
||||
import { logger } from '../log';
|
||||
import { i18n } from '.';
|
||||
|
||||
|
|
@ -31,7 +31,14 @@ export async function requestChangeLanguage(newLanguage: string): Promise<void>
|
|||
// change tiddlywiki language
|
||||
new Promise<unknown>((resolve, reject) => {
|
||||
const tiddlywikiLanguageName = tiddlywikiLanguagesMap[newLanguage];
|
||||
if (tiddlywikiLanguageName !== undefined) {
|
||||
if (tiddlywikiLanguageName === undefined) {
|
||||
const errorMessage = `When click language menu "${newLanguage}", there is no corresponding tiddlywiki language registered`;
|
||||
logger.error(errorMessage, {
|
||||
supportedLanguagesMap,
|
||||
tiddlywikiLanguagesMap,
|
||||
});
|
||||
reject(new Error(errorMessage));
|
||||
} else {
|
||||
if (viewCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -40,13 +47,6 @@ export async function requestChangeLanguage(newLanguage: string): Promise<void>
|
|||
tasks.push(wikiService.setWikiLanguage(view, workspaceID, tiddlywikiLanguageName));
|
||||
});
|
||||
void Promise.all(tasks).then(resolve, reject);
|
||||
} else {
|
||||
const errorMessage = `When click language menu "${newLanguage}", there is no corresponding tiddlywiki language registered`;
|
||||
logger.error(errorMessage, {
|
||||
supportedLanguagesMap,
|
||||
tiddlywikiLanguagesMap,
|
||||
});
|
||||
reject(new Error(errorMessage));
|
||||
}
|
||||
}),
|
||||
// update menu
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import winston, { format } from 'winston';
|
||||
import { LOG_FOLDER } from '@/constants/appPaths';
|
||||
import winston, { format } from 'winston';
|
||||
import RendererTransport from './rendererTransport';
|
||||
import 'winston-daily-rotate-file';
|
||||
|
||||
|
|
@ -20,39 +20,39 @@ export type ILogLevels = keyof typeof levels;
|
|||
const logger = (
|
||||
process.env.NODE_ENV === 'test'
|
||||
? Object.assign(console, {
|
||||
emerg: console.error.bind(console),
|
||||
alert: console.error.bind(console),
|
||||
crit: console.error.bind(console),
|
||||
warning: console.warn.bind(console),
|
||||
notice: console.log.bind(console),
|
||||
debug: console.log.bind(console),
|
||||
})
|
||||
emerg: console.error.bind(console),
|
||||
alert: console.error.bind(console),
|
||||
crit: console.error.bind(console),
|
||||
warning: console.warn.bind(console),
|
||||
notice: console.log.bind(console),
|
||||
debug: console.log.bind(console),
|
||||
})
|
||||
: winston.createLogger({
|
||||
levels,
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
new winston.transports.DailyRotateFile({
|
||||
filename: 'TidGi-%DATE%.log',
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: false,
|
||||
maxSize: '20mb',
|
||||
maxFiles: '14d',
|
||||
dirname: LOG_FOLDER,
|
||||
level: 'debug',
|
||||
}),
|
||||
new RendererTransport(),
|
||||
],
|
||||
exceptionHandlers: [
|
||||
new winston.transports.DailyRotateFile({
|
||||
filename: 'TidGi-Exception-%DATE%.log',
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: false,
|
||||
maxSize: '20mb',
|
||||
maxFiles: '14d',
|
||||
dirname: LOG_FOLDER,
|
||||
}),
|
||||
],
|
||||
format: format.combine(format.timestamp(), format.json()),
|
||||
})
|
||||
levels,
|
||||
transports: [
|
||||
new winston.transports.Console(),
|
||||
new winston.transports.DailyRotateFile({
|
||||
filename: 'TidGi-%DATE%.log',
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: false,
|
||||
maxSize: '20mb',
|
||||
maxFiles: '14d',
|
||||
dirname: LOG_FOLDER,
|
||||
level: 'debug',
|
||||
}),
|
||||
new RendererTransport(),
|
||||
],
|
||||
exceptionHandlers: [
|
||||
new winston.transports.DailyRotateFile({
|
||||
filename: 'TidGi-Exception-%DATE%.log',
|
||||
datePattern: 'YYYY-MM-DD',
|
||||
zippedArchive: false,
|
||||
maxSize: '20mb',
|
||||
maxFiles: '14d',
|
||||
dirname: LOG_FOLDER,
|
||||
}),
|
||||
],
|
||||
format: format.combine(format.timestamp(), format.json()),
|
||||
})
|
||||
) as winston.Logger;
|
||||
export { logger };
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue