mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2026-01-21 12:02:57 -08:00
refactor: split edit workspace UI code
This commit is contained in:
parent
baa7eb3a33
commit
fb41fd3688
6 changed files with 668 additions and 542 deletions
84
src/windows/EditWorkspace/AppearanceOptions.tsx
Normal file
84
src/windows/EditWorkspace/AppearanceOptions.tsx
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { AccordionDetails, Divider, Tooltip } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import defaultIcon from '../../images/default-icon.png';
|
||||
|
||||
import { wikiPictureExtensions } from '@/constants/fileNames';
|
||||
import { IWorkspace } from '@services/workspaces/interface';
|
||||
import { Avatar, AvatarFlex, AvatarLeft, AvatarPicture, AvatarRight, OptionsAccordion, OptionsAccordionSummary, PictureButton, TextField } from './styles';
|
||||
|
||||
interface AppearanceOptionsProps {
|
||||
workspace: IWorkspace;
|
||||
workspaceSetter: (newValue: IWorkspace) => void;
|
||||
}
|
||||
|
||||
const getValidIconPath = (iconPath?: string | null): string => {
|
||||
if (typeof iconPath === 'string') {
|
||||
return `file:///${iconPath}`;
|
||||
}
|
||||
return defaultIcon;
|
||||
};
|
||||
|
||||
export function AppearanceOptions(props: AppearanceOptionsProps): React.JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const { workspace, workspaceSetter } = props;
|
||||
const { name, picturePath } = workspace;
|
||||
|
||||
return (
|
||||
<OptionsAccordion defaultExpanded>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
{t('EditWorkspace.AppearanceOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
<AccordionDetails>
|
||||
<TextField
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.Name')}
|
||||
helperText={t('EditWorkspace.NameDescription')}
|
||||
placeholder='Optional'
|
||||
value={name}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, name: event.target.value });
|
||||
}}
|
||||
/>
|
||||
<Divider />
|
||||
<AvatarFlex>
|
||||
<AvatarLeft>
|
||||
<Avatar transparentBackground={false}>
|
||||
<AvatarPicture alt='Icon' src={getValidIconPath(picturePath)} />
|
||||
</Avatar>
|
||||
</AvatarLeft>
|
||||
<AvatarRight>
|
||||
<Tooltip title={wikiPictureExtensions.join(', ')} placement='top'>
|
||||
<PictureButton
|
||||
variant='outlined'
|
||||
size='small'
|
||||
onClick={async () => {
|
||||
const filePaths = await window.service.native.pickFile([{ name: 'Images', extensions: wikiPictureExtensions }]);
|
||||
if (filePaths.length > 0) {
|
||||
workspaceSetter({ ...workspace, picturePath: filePaths[0] });
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('EditWorkspace.SelectLocal')}
|
||||
</PictureButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title={t('EditWorkspace.NoRevert') ?? ''} placement='bottom'>
|
||||
<PictureButton
|
||||
onClick={() => {
|
||||
workspaceSetter({ ...workspace, picturePath: null });
|
||||
}}
|
||||
disabled={!picturePath}
|
||||
>
|
||||
{t('EditWorkspace.ResetDefaultIcon')}
|
||||
</PictureButton>
|
||||
</Tooltip>
|
||||
</AvatarRight>
|
||||
</AvatarFlex>
|
||||
</AccordionDetails>
|
||||
</OptionsAccordion>
|
||||
);
|
||||
}
|
||||
134
src/windows/EditWorkspace/MiscOptions.tsx
Normal file
134
src/windows/EditWorkspace/MiscOptions.tsx
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { AccordionDetails, Divider, List, ListItem, ListItemText, Switch, Tooltip } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { isWikiWorkspace, IWorkspace } from '@services/workspaces/interface';
|
||||
import { OptionsAccordion, OptionsAccordionSummary, TextField } from './styles';
|
||||
|
||||
interface MiscOptionsProps {
|
||||
workspace: IWorkspace;
|
||||
workspaceSetter: (newValue: IWorkspace, requestSaveAndRestart?: boolean) => void;
|
||||
rememberLastPageVisited: boolean | undefined;
|
||||
}
|
||||
|
||||
export function MiscOptions(props: MiscOptionsProps): React.JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const { workspace, workspaceSetter, rememberLastPageVisited } = props;
|
||||
|
||||
const isWiki = isWikiWorkspace(workspace);
|
||||
const {
|
||||
disableAudio = false,
|
||||
disableNotifications = false,
|
||||
enableFileSystemWatch = false,
|
||||
hibernateWhenUnused = false,
|
||||
homeUrl = '',
|
||||
isSubWiki = false,
|
||||
lastUrl = null,
|
||||
} = isWiki ? workspace : {
|
||||
disableAudio: false,
|
||||
disableNotifications: false,
|
||||
enableFileSystemWatch: false,
|
||||
hibernateWhenUnused: false,
|
||||
homeUrl: '',
|
||||
isSubWiki: false,
|
||||
lastUrl: null,
|
||||
};
|
||||
|
||||
return (
|
||||
<OptionsAccordion>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />} data-testid='preference-section-miscOptions'>
|
||||
{t('EditWorkspace.MiscOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
<AccordionDetails>
|
||||
{!isSubWiki && (
|
||||
<List>
|
||||
<Divider />
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={hibernateWhenUnused}
|
||||
data-testid='hibernate-when-unused-switch'
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, hibernateWhenUnused: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.HibernateTitle')} secondary={t('EditWorkspace.HibernateDescription')} />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={disableNotifications}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, disableNotifications: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.DisableNotificationTitle')} secondary={t('EditWorkspace.DisableNotification')} />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={disableAudio}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, disableAudio: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.DisableAudioTitle')} secondary={t('EditWorkspace.DisableAudio')} />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={enableFileSystemWatch}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, enableFileSystemWatch: event.target.checked }, true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('EditWorkspace.EnableFileSystemWatchTitle')}
|
||||
secondary={t('EditWorkspace.EnableFileSystemWatchDescription')}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
{!isSubWiki && rememberLastPageVisited && (
|
||||
<TextField
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.LastVisitState')}
|
||||
helperText={t('Preference.RememberLastVisitState')}
|
||||
placeholder={homeUrl}
|
||||
value={lastUrl}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({
|
||||
...workspace,
|
||||
lastUrl: (event.target.value || homeUrl) ?? '',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AccordionDetails>
|
||||
</OptionsAccordion>
|
||||
);
|
||||
}
|
||||
203
src/windows/EditWorkspace/SaveAndSyncOptions.tsx
Normal file
203
src/windows/EditWorkspace/SaveAndSyncOptions.tsx
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { AccordionDetails, Button, Divider, List, ListItem, ListItemText, Switch, Tooltip } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { TokenForm } from '@/components/TokenForm';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { isWikiWorkspace, IWorkspace } from '@services/workspaces/interface';
|
||||
import { SyncedWikiDescription } from '../AddWorkspace/Description';
|
||||
import { GitRepoUrlForm } from '../AddWorkspace/GitRepoUrlForm';
|
||||
import { OptionsAccordion, OptionsAccordionSummary, TextField } from './styles';
|
||||
|
||||
interface SaveAndSyncOptionsProps {
|
||||
workspace: IWorkspace;
|
||||
workspaceSetter: (newValue: IWorkspace, requestSaveAndRestart?: boolean) => void;
|
||||
rememberLastPageVisited: boolean | undefined;
|
||||
}
|
||||
|
||||
export function SaveAndSyncOptions(props: SaveAndSyncOptionsProps): React.JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const { workspace, workspaceSetter, rememberLastPageVisited: _rememberLastPageVisited } = props;
|
||||
|
||||
const isWiki = isWikiWorkspace(workspace);
|
||||
const {
|
||||
gitUrl = null,
|
||||
homeUrl: _homeUrl = '',
|
||||
isSubWiki = false,
|
||||
lastUrl: _lastUrl = null,
|
||||
storageService = SupportedStorageServices.github,
|
||||
syncOnInterval = false,
|
||||
syncOnStartup = false,
|
||||
backupOnInterval = false,
|
||||
userName = '',
|
||||
wikiFolderLocation = '',
|
||||
} = isWiki ? workspace : {
|
||||
gitUrl: null,
|
||||
homeUrl: '',
|
||||
isSubWiki: false,
|
||||
lastUrl: null,
|
||||
storageService: SupportedStorageServices.github,
|
||||
syncOnInterval: false,
|
||||
syncOnStartup: false,
|
||||
backupOnInterval: false,
|
||||
userName: '',
|
||||
wikiFolderLocation: '',
|
||||
};
|
||||
|
||||
const fallbackUserName = '';
|
||||
const isCreateSyncedWorkspace = storageService !== SupportedStorageServices.local;
|
||||
|
||||
return (
|
||||
<OptionsAccordion>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />} data-testid='preference-section-saveAndSyncOptions'>
|
||||
{t('EditWorkspace.SaveAndSyncOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
<AccordionDetails>
|
||||
<TextField
|
||||
fullWidth
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.Path')}
|
||||
helperText={t('EditWorkspace.PathDescription')}
|
||||
placeholder='Optional'
|
||||
disabled
|
||||
value={wikiFolderLocation}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, wikiFolderLocation: event.target.value });
|
||||
}}
|
||||
/>
|
||||
<Tooltip title={t('EditWorkspace.MoveWorkspaceTooltip') ?? ''} placement='top'>
|
||||
<Button
|
||||
variant='outlined'
|
||||
size='small'
|
||||
onClick={async () => {
|
||||
const directories = await window.service.native.pickDirectory();
|
||||
if (directories.length > 0) {
|
||||
const newLocation = directories[0];
|
||||
try {
|
||||
await window.service.wikiGitWorkspace.moveWorkspaceLocation(workspace.id, newLocation);
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
void window.service.native.log('error', `Failed to move workspace: ${errorMessage}`, { error, workspaceID: workspace.id, newLocation });
|
||||
void window.service.notification.show({
|
||||
title: t('EditWorkspace.MoveWorkspaceFailed'),
|
||||
body: t('EditWorkspace.MoveWorkspaceFailedMessage', { name: workspace.name, error: errorMessage }),
|
||||
});
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('EditWorkspace.MoveWorkspace')}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{isSubWiki && workspace && isWikiWorkspace(workspace) && workspace.mainWikiToLink && (
|
||||
<TextField
|
||||
fullWidth
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.MainWorkspacePath')}
|
||||
helperText={t('EditWorkspace.PathDescription')}
|
||||
value={workspace.mainWikiToLink}
|
||||
disabled
|
||||
/>
|
||||
)}
|
||||
{!isSubWiki && (
|
||||
<TextField
|
||||
helperText={t('AddWorkspace.WorkspaceUserNameDetail')}
|
||||
fullWidth
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, userName: event.target.value }, true);
|
||||
}}
|
||||
label={t('AddWorkspace.WorkspaceUserName')}
|
||||
placeholder={fallbackUserName}
|
||||
value={userName}
|
||||
/>
|
||||
)}
|
||||
<Divider />
|
||||
<SyncedWikiDescription
|
||||
isCreateSyncedWorkspace={isCreateSyncedWorkspace}
|
||||
isCreateSyncedWorkspaceSetter={(isSynced: boolean) => {
|
||||
workspaceSetter({ ...workspace, storageService: isSynced ? SupportedStorageServices.github : SupportedStorageServices.local });
|
||||
}}
|
||||
/>
|
||||
{isCreateSyncedWorkspace && (
|
||||
<TokenForm
|
||||
storageProvider={storageService}
|
||||
storageProviderSetter={(nextStorageService) => {
|
||||
workspaceSetter({ ...workspace, storageService: nextStorageService });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{storageService !== SupportedStorageServices.local && (
|
||||
<GitRepoUrlForm
|
||||
storageProvider={storageService}
|
||||
gitRepoUrl={gitUrl ?? ''}
|
||||
gitRepoUrlSetter={(nextGitUrl: string) => {
|
||||
workspaceSetter({ ...workspace, gitUrl: nextGitUrl });
|
||||
}}
|
||||
isCreateMainWorkspace={!isSubWiki}
|
||||
/>
|
||||
)}
|
||||
{storageService !== SupportedStorageServices.local && (
|
||||
<>
|
||||
<List>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={syncOnInterval}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, syncOnInterval: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.SyncOnInterval')} secondary={t('EditWorkspace.SyncOnIntervalDescription')} />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={syncOnStartup}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, syncOnStartup: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.SyncOnStartup')} secondary={t('EditWorkspace.SyncOnStartupDescription')} />
|
||||
</ListItem>
|
||||
</List>
|
||||
</>
|
||||
)}
|
||||
{storageService === SupportedStorageServices.local && (
|
||||
<>
|
||||
<List>
|
||||
<Divider />
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={backupOnInterval}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, backupOnInterval: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.BackupOnInterval')} secondary={t('EditWorkspace.BackupOnIntervalDescription')} />
|
||||
</ListItem>
|
||||
</List>
|
||||
</>
|
||||
)}
|
||||
</AccordionDetails>
|
||||
</OptionsAccordion>
|
||||
);
|
||||
}
|
||||
117
src/windows/EditWorkspace/SubWorkspaceRouting.tsx
Normal file
117
src/windows/EditWorkspace/SubWorkspaceRouting.tsx
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import { AccordionDetails, Autocomplete, AutocompleteRenderInputParams, List, ListItem, ListItemText, Switch, Tooltip, Typography } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { IWikiWorkspace } from '@services/workspaces/interface';
|
||||
import { OptionsAccordion, OptionsAccordionSummary, TextField } from './styles';
|
||||
|
||||
interface SubWorkspaceRoutingProps {
|
||||
workspace: IWikiWorkspace;
|
||||
workspaceSetter: (newValue: IWikiWorkspace, requestSaveAndRestart?: boolean) => void;
|
||||
availableTags: string[];
|
||||
isSubWiki: boolean;
|
||||
}
|
||||
|
||||
export function SubWorkspaceRouting(props: SubWorkspaceRoutingProps): React.JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const { workspace, workspaceSetter, availableTags, isSubWiki } = props;
|
||||
|
||||
const {
|
||||
tagNames,
|
||||
includeTagTree,
|
||||
fileSystemPathFilterEnable,
|
||||
fileSystemPathFilter,
|
||||
} = workspace;
|
||||
|
||||
return (
|
||||
<OptionsAccordion defaultExpanded={isSubWiki}>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />} data-testid='preference-section-subWorkspaceOptions'>
|
||||
{t('AddWorkspace.SubWorkspaceOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
<AccordionDetails>
|
||||
<Typography variant='body2' color='textSecondary' sx={{ mb: 2 }}>
|
||||
{isSubWiki ? t('AddWorkspace.SubWorkspaceOptionsDescriptionForSub') : t('AddWorkspace.SubWorkspaceOptionsDescriptionForMain')}
|
||||
</Typography>
|
||||
<Autocomplete
|
||||
multiple
|
||||
freeSolo
|
||||
options={availableTags}
|
||||
value={tagNames}
|
||||
onChange={(_event: React.SyntheticEvent, newValue: string[]) => {
|
||||
void _event;
|
||||
workspaceSetter({ ...workspace, tagNames: newValue }, true);
|
||||
}}
|
||||
slotProps={{
|
||||
chip: {
|
||||
variant: 'outlined',
|
||||
},
|
||||
}}
|
||||
renderInput={(parameters: AutocompleteRenderInputParams) => (
|
||||
<TextField
|
||||
{...parameters}
|
||||
label={t('AddWorkspace.TagName')}
|
||||
helperText={isSubWiki ? t('AddWorkspace.TagNameHelp') : t('AddWorkspace.TagNameHelpForMain')}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<List>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={includeTagTree}
|
||||
data-testid='include-tag-tree-switch'
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, includeTagTree: event.target.checked }, true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('AddWorkspace.IncludeTagTree')}
|
||||
secondary={isSubWiki ? t('AddWorkspace.IncludeTagTreeHelp') : t('AddWorkspace.IncludeTagTreeHelpForMain')}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={fileSystemPathFilterEnable}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, fileSystemPathFilterEnable: event.target.checked }, true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('AddWorkspace.UseFilter')}
|
||||
secondary={t('AddWorkspace.UseFilterHelp')}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
{fileSystemPathFilterEnable && (
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={10}
|
||||
value={fileSystemPathFilter ?? ''}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, fileSystemPathFilter: event.target.value || null }, true);
|
||||
}}
|
||||
label={t('AddWorkspace.FilterExpression')}
|
||||
helperText={t('AddWorkspace.FilterExpressionHelp')}
|
||||
sx={{ mb: 2 }}
|
||||
/>
|
||||
)}
|
||||
</AccordionDetails>
|
||||
</OptionsAccordion>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,144 +1,23 @@
|
|||
import { Helmet } from '@dr.pogodin/react-helmet';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionDetails,
|
||||
AccordionSummary,
|
||||
Autocomplete,
|
||||
AutocompleteRenderInputParams,
|
||||
Button as ButtonRaw,
|
||||
Divider,
|
||||
Paper,
|
||||
Switch,
|
||||
TextField as TextFieldRaw,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { css, styled } from '@mui/material/styles';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { Divider } from '@mui/material';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import defaultIcon from '../../images/default-icon.png';
|
||||
|
||||
import { usePromiseValue } from '@/helpers/useServiceValue';
|
||||
import { WindowMeta, WindowNames } from '@services/windows/WindowProperties';
|
||||
import { useWorkspaceObservable } from '@services/workspaces/hooks';
|
||||
import { useForm } from './useForm';
|
||||
|
||||
import { List, ListItem, ListItemText } from '@/components/ListItem';
|
||||
import { RestartSnackbarType, useRestartSnackbar } from '@/components/RestartSnackbar';
|
||||
import { TokenForm } from '@/components/TokenForm';
|
||||
import { wikiPictureExtensions } from '@/constants/fileNames';
|
||||
import { SupportedStorageServices } from '@services/types';
|
||||
import { isWikiWorkspace, nonConfigFields } from '@services/workspaces/interface';
|
||||
import { isEqual, omit } from 'lodash';
|
||||
import { SyncedWikiDescription } from '../AddWorkspace/Description';
|
||||
import { GitRepoUrlForm } from '../AddWorkspace/GitRepoUrlForm';
|
||||
import { useAvailableTags } from '../AddWorkspace/useAvailableTags';
|
||||
import { AppearanceOptions } from './AppearanceOptions';
|
||||
import { MiscOptions } from './MiscOptions';
|
||||
import { SaveAndSyncOptions } from './SaveAndSyncOptions';
|
||||
import { ServerOptions } from './server';
|
||||
|
||||
const OptionsAccordion = styled((props: React.ComponentProps<typeof Accordion>) => <Accordion {...props} />)`
|
||||
box-shadow: unset;
|
||||
background-color: unset;
|
||||
`;
|
||||
const OptionsAccordionSummary = styled((props: React.ComponentProps<typeof AccordionSummary>) => <AccordionSummary {...props} />)`
|
||||
padding: 0;
|
||||
flex-direction: row-reverse;
|
||||
`;
|
||||
const Root = styled((props: React.ComponentProps<typeof Paper>) => <Paper {...props} />)`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
/** for SaveCancelButtonsContainer 's height */
|
||||
margin-bottom: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: ${({ theme }) => theme.palette.background.paper};
|
||||
`;
|
||||
const FlexGrow = styled('div')`
|
||||
flex: 1;
|
||||
`;
|
||||
const Button = styled((props: React.ComponentProps<typeof ButtonRaw>) => <ButtonRaw {...props} />)`
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
`;
|
||||
const TextField = styled((props: React.ComponentProps<typeof TextFieldRaw>) => (
|
||||
<TextFieldRaw fullWidth margin='dense' size='small' variant='filled' slotProps={{ inputLabel: { shrink: true } }} {...props} />
|
||||
))`
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
const AvatarFlex = styled('div')`
|
||||
display: flex;
|
||||
`;
|
||||
const AvatarLeft = styled('div')`
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 0;
|
||||
padding-right: 10px;
|
||||
`;
|
||||
const AvatarRight = styled('div')`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: flex-start;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 10px;
|
||||
padding-right: 0;
|
||||
`;
|
||||
/**
|
||||
* border: theme.palette.type === 'dark' ? 'none': '1px solid rgba(0, 0, 0, 0.12)';
|
||||
*/
|
||||
const Avatar = styled('div')<{ transparentBackground?: boolean }>`
|
||||
height: 85px;
|
||||
width: 85px;
|
||||
border-radius: 4px;
|
||||
color: #333;
|
||||
font-size: 32px;
|
||||
line-height: 64px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
user-select: none;
|
||||
|
||||
overflow: hidden;
|
||||
${({ transparentBackground }) => {
|
||||
if (transparentBackground === true) {
|
||||
return css`
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
`;
|
||||
}
|
||||
}}
|
||||
`;
|
||||
const SaveCancelButtonsContainer = styled('div')`
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
|
||||
height: auto;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background-color: ${({ theme }) => theme.palette.background.paper};
|
||||
opacity: 0.9;
|
||||
backdrop-filter: blur(10px);
|
||||
`;
|
||||
const AvatarPicture = styled('img')`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
const PictureButton = styled((props: React.ComponentProps<typeof ButtonRaw>) => <ButtonRaw variant='outlined' size='small' {...props} />)``;
|
||||
const _Caption = styled((props: { children?: ReactNode } & React.ComponentProps<typeof Typography>) => <Typography variant='caption' {...props} />)`
|
||||
display: block;
|
||||
`;
|
||||
|
||||
const getValidIconPath = (iconPath?: string | null): string => {
|
||||
if (typeof iconPath === 'string') {
|
||||
return `file:///${iconPath}`;
|
||||
}
|
||||
return defaultIcon;
|
||||
};
|
||||
import { Button, FlexGrow, Root, SaveCancelButtonsContainer } from './styles';
|
||||
import { SubWorkspaceRouting } from './SubWorkspaceRouting';
|
||||
|
||||
const workspaceID = (window.meta() as WindowMeta[WindowNames.editWorkspace]).workspaceID!;
|
||||
|
||||
|
|
@ -148,34 +27,11 @@ export default function EditWorkspace(): React.JSX.Element {
|
|||
const [requestRestartCountDown, RestartSnackbar] = useRestartSnackbar({ waitBeforeCountDown: 0, workspace: originalWorkspace, restartType: RestartSnackbarType.Wiki });
|
||||
const [workspace, workspaceSetter, onSave] = useForm(originalWorkspace, requestRestartCountDown);
|
||||
const isWiki = workspace && isWikiWorkspace(workspace);
|
||||
const {
|
||||
name,
|
||||
order,
|
||||
picturePath,
|
||||
} = workspace ?? {};
|
||||
const { order } = workspace ?? {};
|
||||
const { name } = workspace ?? {};
|
||||
|
||||
// Wiki-specific properties with fallbacks
|
||||
const backupOnInterval = isWiki ? workspace.backupOnInterval : false;
|
||||
const disableAudio = isWiki ? workspace.disableAudio : false;
|
||||
const disableNotifications = isWiki ? workspace.disableNotifications : false;
|
||||
const enableFileSystemWatch = isWiki ? workspace.enableFileSystemWatch : false;
|
||||
const gitUrl = isWiki ? workspace.gitUrl : null;
|
||||
const hibernateWhenUnused = isWiki ? workspace.hibernateWhenUnused : false;
|
||||
const homeUrl = isWiki ? workspace.homeUrl : '';
|
||||
const isSubWiki = isWiki ? workspace.isSubWiki : false;
|
||||
const mainWikiToLink = isWiki ? workspace.mainWikiToLink : null;
|
||||
const storageService = isWiki ? workspace.storageService : SupportedStorageServices.github;
|
||||
const syncOnInterval = isWiki ? workspace.syncOnInterval : false;
|
||||
const syncOnStartup = isWiki ? workspace.syncOnStartup : false;
|
||||
const tagNames = isWiki ? workspace.tagNames : [];
|
||||
const includeTagTree = isWiki ? workspace.includeTagTree : false;
|
||||
const fileSystemPathFilterEnable = isWiki ? workspace.fileSystemPathFilterEnable : false;
|
||||
const fileSystemPathFilter = isWiki ? workspace.fileSystemPathFilter : null;
|
||||
const transparentBackground = isWiki ? workspace.transparentBackground : false;
|
||||
const userName = isWiki ? workspace.userName : '';
|
||||
const lastUrl = isWiki ? workspace.lastUrl : null;
|
||||
const wikiFolderLocation = isWiki ? workspace.wikiFolderLocation : '';
|
||||
const fallbackUserName = usePromiseValue<string>(async () => (await window.service.auth.get('userName'))!, '');
|
||||
|
||||
// Fetch all tags from main wiki for autocomplete suggestions
|
||||
const availableTags = useAvailableTags(mainWikiToLink ?? undefined, isSubWiki);
|
||||
|
|
@ -197,7 +53,6 @@ export default function EditWorkspace(): React.JSX.Element {
|
|||
if (workspace === undefined) {
|
||||
return <Root>{t('Loading')}</Root>;
|
||||
}
|
||||
const isCreateSyncedWorkspace = storageService !== SupportedStorageServices.local;
|
||||
return (
|
||||
<Root>
|
||||
{RestartSnackbar}
|
||||
|
|
@ -214,395 +69,17 @@ export default function EditWorkspace(): React.JSX.Element {
|
|||
<Divider />
|
||||
</>
|
||||
)}
|
||||
<OptionsAccordion defaultExpanded>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />}>
|
||||
{t('EditWorkspace.AppearanceOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
<AccordionDetails>
|
||||
<TextField
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.Name')}
|
||||
helperText={t('EditWorkspace.NameDescription')}
|
||||
placeholder='Optional'
|
||||
value={name}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, name: event.target.value });
|
||||
}}
|
||||
/>
|
||||
<Divider />
|
||||
<AvatarFlex>
|
||||
<AvatarLeft>
|
||||
<Avatar transparentBackground={transparentBackground}>
|
||||
<AvatarPicture alt='Icon' src={getValidIconPath(picturePath)} />
|
||||
</Avatar>
|
||||
</AvatarLeft>
|
||||
<AvatarRight>
|
||||
<Tooltip title={wikiPictureExtensions.join(', ')} placement='top'>
|
||||
<PictureButton
|
||||
variant='outlined'
|
||||
size='small'
|
||||
onClick={async () => {
|
||||
const filePaths = await window.service.native.pickFile([{ name: 'Images', extensions: wikiPictureExtensions }]);
|
||||
if (filePaths.length > 0) {
|
||||
workspaceSetter({ ...workspace, picturePath: filePaths[0] });
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('EditWorkspace.SelectLocal')}
|
||||
</PictureButton>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip title={t('EditWorkspace.NoRevert') ?? ''} placement='bottom'>
|
||||
<PictureButton
|
||||
onClick={() => {
|
||||
workspaceSetter({ ...workspace, picturePath: null });
|
||||
}}
|
||||
disabled={!picturePath}
|
||||
>
|
||||
{t('EditWorkspace.ResetDefaultIcon')}
|
||||
</PictureButton>
|
||||
</Tooltip>
|
||||
</AvatarRight>
|
||||
</AvatarFlex>
|
||||
</AccordionDetails>
|
||||
</OptionsAccordion>
|
||||
<OptionsAccordion>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />} data-testid='preference-section-saveAndSyncOptions'>
|
||||
{t('EditWorkspace.SaveAndSyncOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
<AccordionDetails>
|
||||
<TextField
|
||||
fullWidth
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.Path')}
|
||||
helperText={t('EditWorkspace.PathDescription')}
|
||||
placeholder='Optional'
|
||||
disabled
|
||||
value={wikiFolderLocation}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, wikiFolderLocation: event.target.value });
|
||||
}}
|
||||
/>
|
||||
<Tooltip title={t('EditWorkspace.MoveWorkspaceTooltip') ?? ''} placement='top'>
|
||||
<PictureButton
|
||||
variant='outlined'
|
||||
size='small'
|
||||
onClick={async () => {
|
||||
const directories = await window.service.native.pickDirectory();
|
||||
if (directories.length > 0) {
|
||||
const newLocation = directories[0];
|
||||
try {
|
||||
await window.service.wikiGitWorkspace.moveWorkspaceLocation(workspaceID, newLocation);
|
||||
} catch (error) {
|
||||
const errorMessage = (error as Error).message;
|
||||
void window.service.native.log('error', `Failed to move workspace: ${errorMessage}`, { error, workspaceID, newLocation });
|
||||
// Show error notification
|
||||
void window.service.notification.show({
|
||||
title: t('EditWorkspace.MoveWorkspaceFailed'),
|
||||
body: t('EditWorkspace.MoveWorkspaceFailedMessage', { name: workspace.name, error: errorMessage }),
|
||||
});
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
{t('EditWorkspace.MoveWorkspace')}
|
||||
</PictureButton>
|
||||
</Tooltip>
|
||||
{isSubWiki && mainWikiToLink && (
|
||||
<TextField
|
||||
fullWidth
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.MainWorkspacePath')}
|
||||
helperText={t('EditWorkspace.PathDescription')}
|
||||
value={mainWikiToLink}
|
||||
disabled
|
||||
/>
|
||||
)}
|
||||
{!isSubWiki && (
|
||||
<TextField
|
||||
helperText={t('AddWorkspace.WorkspaceUserNameDetail')}
|
||||
fullWidth
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, userName: event.target.value }, true);
|
||||
}}
|
||||
label={t('AddWorkspace.WorkspaceUserName')}
|
||||
placeholder={fallbackUserName}
|
||||
value={userName}
|
||||
/>
|
||||
)}
|
||||
<Divider />
|
||||
<SyncedWikiDescription
|
||||
isCreateSyncedWorkspace={isCreateSyncedWorkspace}
|
||||
isCreateSyncedWorkspaceSetter={(isSynced: boolean) => {
|
||||
workspaceSetter({ ...workspace, storageService: isSynced ? SupportedStorageServices.github : SupportedStorageServices.local });
|
||||
}}
|
||||
/>
|
||||
{isCreateSyncedWorkspace && (
|
||||
<TokenForm
|
||||
storageProvider={storageService}
|
||||
storageProviderSetter={(nextStorageService) => {
|
||||
workspaceSetter({ ...workspace, storageService: nextStorageService });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{storageService !== SupportedStorageServices.local && (
|
||||
<GitRepoUrlForm
|
||||
storageProvider={storageService}
|
||||
gitRepoUrl={gitUrl ?? ''}
|
||||
gitRepoUrlSetter={(nextGitUrl: string) => {
|
||||
workspaceSetter({ ...workspace, gitUrl: nextGitUrl });
|
||||
}}
|
||||
isCreateMainWorkspace={!isSubWiki}
|
||||
/>
|
||||
)}
|
||||
{storageService !== SupportedStorageServices.local && (
|
||||
<>
|
||||
<List>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={syncOnInterval}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, syncOnInterval: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.SyncOnInterval')} secondary={t('EditWorkspace.SyncOnIntervalDescription')} />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={syncOnStartup}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, syncOnStartup: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.SyncOnStartup')} secondary={t('EditWorkspace.SyncOnStartupDescription')} />
|
||||
</ListItem>
|
||||
</List>
|
||||
</>
|
||||
)}
|
||||
{storageService === SupportedStorageServices.local && (
|
||||
<>
|
||||
<List>
|
||||
<Divider />
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={backupOnInterval}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, backupOnInterval: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.BackupOnInterval')} secondary={t('EditWorkspace.BackupOnIntervalDescription')} />
|
||||
</ListItem>
|
||||
</List>
|
||||
</>
|
||||
)}
|
||||
</AccordionDetails>
|
||||
</OptionsAccordion>
|
||||
{showSubWorkspaceRouting && (
|
||||
<OptionsAccordion defaultExpanded={isSubWiki}>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />} data-testid='preference-section-subWorkspaceOptions'>
|
||||
{t('AddWorkspace.SubWorkspaceOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
<AccordionDetails>
|
||||
<Typography variant='body2' color='textSecondary' sx={{ mb: 2 }}>
|
||||
{isSubWiki ? t('AddWorkspace.SubWorkspaceOptionsDescriptionForSub') : t('AddWorkspace.SubWorkspaceOptionsDescriptionForMain')}
|
||||
</Typography>
|
||||
<Autocomplete
|
||||
multiple
|
||||
freeSolo
|
||||
options={availableTags}
|
||||
value={tagNames}
|
||||
onChange={(_event: React.SyntheticEvent, newValue: string[]) => {
|
||||
void _event;
|
||||
workspaceSetter({ ...workspace, tagNames: newValue }, true);
|
||||
}}
|
||||
slotProps={{
|
||||
chip: {
|
||||
variant: 'outlined',
|
||||
},
|
||||
}}
|
||||
renderInput={(parameters: AutocompleteRenderInputParams) => (
|
||||
<TextField
|
||||
{...parameters}
|
||||
label={t('AddWorkspace.TagName')}
|
||||
helperText={isSubWiki ? t('AddWorkspace.TagNameHelp') : t('AddWorkspace.TagNameHelpForMain')}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<List>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={includeTagTree}
|
||||
data-testid='include-tag-tree-switch'
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, includeTagTree: event.target.checked }, true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('AddWorkspace.IncludeTagTree')}
|
||||
secondary={isSubWiki ? t('AddWorkspace.IncludeTagTreeHelp') : t('AddWorkspace.IncludeTagTreeHelpForMain')}
|
||||
/>
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={fileSystemPathFilterEnable}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, fileSystemPathFilterEnable: event.target.checked }, true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('AddWorkspace.UseFilter')}
|
||||
secondary={t('AddWorkspace.UseFilterHelp')}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
{fileSystemPathFilterEnable && (
|
||||
<TextField
|
||||
fullWidth
|
||||
multiline
|
||||
minRows={2}
|
||||
maxRows={10}
|
||||
value={fileSystemPathFilter ?? ''}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, fileSystemPathFilter: event.target.value || null }, true);
|
||||
}}
|
||||
label={t('AddWorkspace.FilterExpression')}
|
||||
helperText={t('AddWorkspace.FilterExpressionHelp')}
|
||||
sx={{ mb: 2 }}
|
||||
/>
|
||||
)}
|
||||
</AccordionDetails>
|
||||
</OptionsAccordion>
|
||||
<AppearanceOptions workspace={workspace} workspaceSetter={workspaceSetter} />
|
||||
<SaveAndSyncOptions workspace={workspace} workspaceSetter={workspaceSetter} rememberLastPageVisited={rememberLastPageVisited} />
|
||||
{showSubWorkspaceRouting && isWiki && (
|
||||
<SubWorkspaceRouting
|
||||
workspace={workspace}
|
||||
workspaceSetter={workspaceSetter}
|
||||
availableTags={availableTags}
|
||||
isSubWiki={isSubWiki}
|
||||
/>
|
||||
)}
|
||||
<OptionsAccordion>
|
||||
<Tooltip title={t('EditWorkspace.ClickToExpand')}>
|
||||
<OptionsAccordionSummary expandIcon={<ExpandMoreIcon />} data-testid='preference-section-miscOptions'>
|
||||
{t('EditWorkspace.MiscOptions')}
|
||||
</OptionsAccordionSummary>
|
||||
</Tooltip>
|
||||
<AccordionDetails>
|
||||
{!isSubWiki && (
|
||||
<List>
|
||||
<Divider />
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={hibernateWhenUnused}
|
||||
data-testid='hibernate-when-unused-switch'
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, hibernateWhenUnused: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.HibernateTitle')} secondary={t('EditWorkspace.HibernateDescription')} />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={disableNotifications}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, disableNotifications: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.DisableNotificationTitle')} secondary={t('EditWorkspace.DisableNotification')} />
|
||||
</ListItem>
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={disableAudio}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, disableAudio: event.target.checked });
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText primary={t('EditWorkspace.DisableAudioTitle')} secondary={t('EditWorkspace.DisableAudio')} />
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem
|
||||
disableGutters
|
||||
secondaryAction={
|
||||
<Switch
|
||||
edge='end'
|
||||
color='primary'
|
||||
checked={enableFileSystemWatch}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({ ...workspace, enableFileSystemWatch: event.target.checked }, true);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ListItemText
|
||||
primary={t('EditWorkspace.EnableFileSystemWatchTitle')}
|
||||
secondary={t('EditWorkspace.EnableFileSystemWatchDescription')}
|
||||
/>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
{!isSubWiki && rememberLastPageVisited && (
|
||||
<TextField
|
||||
id='outlined-full-width'
|
||||
label={t('EditWorkspace.LastVisitState')}
|
||||
helperText={t('Preference.RememberLastVisitState')}
|
||||
placeholder={homeUrl}
|
||||
value={lastUrl}
|
||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
workspaceSetter({
|
||||
...workspace,
|
||||
lastUrl: (event.target.value || homeUrl) ?? '',
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</AccordionDetails>
|
||||
</OptionsAccordion>
|
||||
<MiscOptions workspace={workspace} workspaceSetter={workspaceSetter} rememberLastPageVisited={rememberLastPageVisited} />
|
||||
</FlexGrow>
|
||||
{!isEqual(omit(workspace, nonConfigFields), omit(originalWorkspace, nonConfigFields)) && (
|
||||
<SaveCancelButtonsContainer>
|
||||
|
|
|
|||
111
src/windows/EditWorkspace/styles.tsx
Normal file
111
src/windows/EditWorkspace/styles.tsx
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
import { Accordion, AccordionSummary, Paper } from '@mui/material';
|
||||
import { Button as ButtonRaw, TextField as TextFieldRaw, Typography } from '@mui/material';
|
||||
import { css, styled } from '@mui/material/styles';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
export const OptionsAccordion = styled((props: React.ComponentProps<typeof Accordion>) => <Accordion {...props} />)`
|
||||
box-shadow: unset;
|
||||
background-color: unset;
|
||||
`;
|
||||
|
||||
export const OptionsAccordionSummary = styled((props: React.ComponentProps<typeof AccordionSummary>) => <AccordionSummary {...props} />)`
|
||||
padding: 0;
|
||||
flex-direction: row-reverse;
|
||||
`;
|
||||
|
||||
export const Root = styled((props: React.ComponentProps<typeof Paper>) => <Paper {...props} />)`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
/** for SaveCancelButtonsContainer 's height */
|
||||
margin-bottom: 40px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: ${({ theme }) => theme.palette.background.paper};
|
||||
`;
|
||||
|
||||
export const FlexGrow = styled('div')`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
export const Button = styled((props: React.ComponentProps<typeof ButtonRaw>) => <ButtonRaw {...props} />)`
|
||||
float: right;
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
export const TextField = styled((props: React.ComponentProps<typeof TextFieldRaw>) => (
|
||||
<TextFieldRaw fullWidth margin='dense' size='small' variant='filled' slotProps={{ inputLabel: { shrink: true } }} {...props} />
|
||||
))`
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
|
||||
export const AvatarFlex = styled('div')`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const AvatarLeft = styled('div')`
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 0;
|
||||
padding-right: 10px;
|
||||
`;
|
||||
|
||||
export const AvatarRight = styled('div')`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
align-items: flex-start;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 10px;
|
||||
padding-right: 0;
|
||||
`;
|
||||
|
||||
export const Avatar = styled('div')<{ transparentBackground?: boolean }>`
|
||||
height: 85px;
|
||||
width: 85px;
|
||||
border-radius: 4px;
|
||||
color: #333;
|
||||
font-size: 32px;
|
||||
line-height: 64px;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
user-select: none;
|
||||
|
||||
overflow: hidden;
|
||||
${({ transparentBackground }) => {
|
||||
if (transparentBackground === true) {
|
||||
return css`
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
`;
|
||||
}
|
||||
}}
|
||||
`;
|
||||
|
||||
export const SaveCancelButtonsContainer = styled('div')`
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
|
||||
height: auto;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
background-color: ${({ theme }) => theme.palette.background.paper};
|
||||
opacity: 0.9;
|
||||
backdrop-filter: blur(10px);
|
||||
`;
|
||||
|
||||
export const AvatarPicture = styled('img')`
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const PictureButton = styled((props: React.ComponentProps<typeof ButtonRaw>) => <ButtonRaw variant='outlined' size='small' {...props} />)``;
|
||||
|
||||
export const _Caption = styled((props: { children?: ReactNode } & React.ComponentProps<typeof Typography>) => <Typography variant='caption' {...props} />)`
|
||||
display: block;
|
||||
`;
|
||||
Loading…
Add table
Add a link
Reference in a new issue