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 { 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 { ServerOptions } from './server'; const OptionsAccordion = styled((props: React.ComponentProps) => )` box-shadow: unset; background-color: unset; `; const OptionsAccordionSummary = styled((props: React.ComponentProps) => )` padding: 0; flex-direction: row-reverse; `; const Root = styled((props: React.ComponentProps) => )` 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) => )` float: right; margin-left: 10px; `; const TextField = styled((props: React.ComponentProps) => ( ))` 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) => )``; const _Caption = styled((props: { children?: ReactNode } & React.ComponentProps) => )` display: block; `; const getValidIconPath = (iconPath?: string | null): string => { if (typeof iconPath === 'string') { return `file:///${iconPath}`; } return defaultIcon; }; const workspaceID = (window.meta() as WindowMeta[WindowNames.editWorkspace]).workspaceID!; export default function EditWorkspace(): React.JSX.Element { const { t } = useTranslation(); const originalWorkspace = useWorkspaceObservable(workspaceID); 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 ?? {}; // 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 tagName = isWiki ? workspace.tagName : 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(async () => (await window.service.auth.get('userName'))!, ''); // Fetch all tags from main wiki for autocomplete suggestions const availableTags = useAvailableTags(mainWikiToLink ?? undefined, isSubWiki); const rememberLastPageVisited = usePromiseValue(async () => await window.service.preference.get('rememberLastPageVisited')); if (workspaceID === undefined) { return Error {workspaceID ?? '-'} not exists; } if (workspace === undefined) { return {t('Loading')}; } const isCreateSyncedWorkspace = storageService !== SupportedStorageServices.local; return ( {RestartSnackbar} {t('WorkspaceSelector.EditWorkspace')} {String(order ?? 1)} {name} {!isSubWiki && ( <> )} }> {t('EditWorkspace.AppearanceOptions')} ) => { workspaceSetter({ ...workspace, name: event.target.value }); }} /> { const filePaths = await window.service.native.pickFile([{ name: 'Images', extensions: wikiPictureExtensions }]); if (filePaths.length > 0) { workspaceSetter({ ...workspace, picturePath: filePaths[0] }); } }} > {t('EditWorkspace.SelectLocal')} { workspaceSetter({ ...workspace, picturePath: null }); }} disabled={!picturePath} > {t('EditWorkspace.ResetDefaultIcon')} } data-testid='preference-section-saveAndSyncOptions'> {t('EditWorkspace.SaveAndSyncOptions')} ) => { workspaceSetter({ ...workspace, wikiFolderLocation: event.target.value }); }} /> { 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')} {isSubWiki && mainWikiToLink && ( )} ) => { workspaceSetter({ ...workspace, userName: event.target.value }, true); }} label={t('AddWorkspace.WorkspaceUserName')} placeholder={fallbackUserName} value={userName} /> {isSubWiki && ( { void _event; workspaceSetter({ ...workspace, tagName: value }, true); }} renderInput={(parameters: AutocompleteRenderInputParams) => ( )} /> )} { workspaceSetter({ ...workspace, storageService: isSynced ? SupportedStorageServices.github : SupportedStorageServices.local }); }} /> {isCreateSyncedWorkspace && ( { workspaceSetter({ ...workspace, storageService: nextStorageService }); }} /> )} {storageService !== SupportedStorageServices.local && ( { workspaceSetter({ ...workspace, gitUrl: nextGitUrl }); }} isCreateMainWorkspace={!isSubWiki} /> )} {storageService !== SupportedStorageServices.local && ( <> ) => { workspaceSetter({ ...workspace, syncOnInterval: event.target.checked }); }} /> } > ) => { workspaceSetter({ ...workspace, syncOnStartup: event.target.checked }); }} /> } > )} {storageService === SupportedStorageServices.local && ( <> ) => { workspaceSetter({ ...workspace, backupOnInterval: event.target.checked }); }} /> } > )} } data-testid='preference-section-miscOptions'> {t('EditWorkspace.MiscOptions')} {!isSubWiki && ( ) => { workspaceSetter({ ...workspace, hibernateWhenUnused: event.target.checked }); }} /> } > ) => { workspaceSetter({ ...workspace, disableNotifications: event.target.checked }); }} /> } > ) => { workspaceSetter({ ...workspace, disableAudio: event.target.checked }); }} /> } > ) => { workspaceSetter({ ...workspace, enableFileSystemWatch: event.target.checked }, true); }} /> } > )} {!isSubWiki && rememberLastPageVisited && ( ) => { workspaceSetter({ ...workspace, lastUrl: (event.target.value || homeUrl) ?? '', }); }} /> )} {!isEqual(omit(workspace, nonConfigFields), omit(originalWorkspace, nonConfigFields)) && ( )} ); }