mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2025-12-15 15:10:31 -08:00
feat: allow modify workflow metadata from list and editor
This commit is contained in:
parent
fbae83117a
commit
5275be539f
10 changed files with 239 additions and 30 deletions
|
|
@ -464,7 +464,18 @@
|
|||
"AddTagsDescription": "in-wiki tags, press Enter to add more",
|
||||
"DeleteWorkflow": "Delete Workflow",
|
||||
"DeleteWorkflowDescription": "The workflow will be completely deleted from the Wiki workspace it belongs to, should it really be deleted?",
|
||||
"OpenInWorkspaceTiddler": "Open {{title}} in {{workspace}}"
|
||||
"OpenInWorkspaceTiddler": "Open {{title}} in {{workspace}}",
|
||||
"RunWorkflow": "Run Workflow",
|
||||
"BackToHome": "Back To List Page",
|
||||
"ToggleReadonly": "Toggle On Readonly",
|
||||
"ZoomToFit": "Zoom To Fit",
|
||||
"ChangeWorkflowMetadata": "Change this Workflow's Metadata",
|
||||
"ChangeSelectedItemInfo": "Change Selected Item's Info",
|
||||
"ToggleOffReadonly": "Toggle Off Readonly",
|
||||
"ChangeMetadata": "Change Metadata",
|
||||
"ChangeWorkflowMetadataDescription": "Create this automated workflow and save it to the selected workspace wiki to backup.",
|
||||
"ChangeWorkflowMetadataDoneMessage": "Change Done",
|
||||
"ToggleOnReadonly": "Toggle On Readonly"
|
||||
},
|
||||
"Description": "Description",
|
||||
"Tags": "Tags",
|
||||
|
|
|
|||
|
|
@ -465,10 +465,20 @@
|
|||
"AddNewWorkflow": "添加新的工作流",
|
||||
"AddNewWorkflowDescription": "创建新的自动化工作流,并保存到所选的工作区Wiki里备份。",
|
||||
"AddNewWorkflowDoneMessage": "添加成功",
|
||||
"ChangeWorkflowMetadataDoneMessage": "修改成功",
|
||||
"DeleteWorkflow": "删除工作流",
|
||||
"DeleteWorkflowDescription": "将从所属的Wiki工作区里彻底删除该工作流,是否真的要删除?",
|
||||
"BelongsToWorkspace": "所属工作区",
|
||||
"AddTagsDescription": "Wiki内的标签,回车可以添加更多",
|
||||
"OpenInWorkspaceTiddler": "在 {{workspace}} 中打开 {{title}}"
|
||||
"OpenInWorkspaceTiddler": "在 {{workspace}} 中打开 {{title}}",
|
||||
"RunWorkflow": "运行工作流",
|
||||
"BackToHome": "返回列表页",
|
||||
"ToggleOnReadonly": "打开只读状态",
|
||||
"ToggleOffReadonly": "关闭只读状态",
|
||||
"ZoomToFit": "缩放到合适大小",
|
||||
"ChangeWorkflowMetadata": "修改本流程图的元信息",
|
||||
"ChangeMetadata": "修改元信息",
|
||||
"ChangeWorkflowMetadataDescription": "修改所选的自动化工作流,并保存到所选的工作区Wiki里备份。",
|
||||
"ChangeSelectedItemInfo": "修改选中的物体的信息"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,14 @@ import { useTranslation } from 'react-i18next';
|
|||
import styled from 'styled-components';
|
||||
import type { IFBPLibrary, INoFloUIComponent } from 'the-graph';
|
||||
import { NoFloIcon } from './NoFloIcon';
|
||||
import { searchBarWidth } from './styleConstant';
|
||||
|
||||
const SearchBarWrapper = styled.div`
|
||||
position: absolute;
|
||||
left: ${sidebarWidth}px;
|
||||
top: 1em;
|
||||
z-index: 2;
|
||||
width: 300px;
|
||||
width: ${searchBarWidth}px;
|
||||
|
||||
opacity: 0.3;
|
||||
&:hover {
|
||||
|
|
|
|||
124
src/pages/Workflow/GraphEditor/components/Toolbar.tsx
Normal file
124
src/pages/Workflow/GraphEditor/components/Toolbar.tsx
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import { sidebarWidth } from '@/constants/style';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import HomeIcon from '@mui/icons-material/Home';
|
||||
import InfoIcon from '@mui/icons-material/Info';
|
||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||
import VisibilityOnIcon from '@mui/icons-material/Visibility';
|
||||
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
|
||||
import ZoomOutMapIcon from '@mui/icons-material/ZoomOutMap';
|
||||
import { IconButton, Toolbar, Tooltip } from '@mui/material';
|
||||
import { PageType } from '@services/pages/interface';
|
||||
import { WindowNames } from '@services/windows/WindowProperties';
|
||||
import { useWorkspacesListObservable } from '@services/workspaces/hooks';
|
||||
import React, { Dispatch, MutableRefObject, SetStateAction, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
import { ITheGraphEditor } from 'the-graph';
|
||||
import { useLocation } from 'wouter';
|
||||
import { IWorkflowContext } from '../../useContext';
|
||||
import { AddItemDialog } from '../../WorkflowManage/AddItemDialog';
|
||||
import { addWorkflowToWiki, useAvailableFilterTags } from '../../WorkflowManage/useWorkflowDataSource';
|
||||
import { IWorkflowListItem } from '../../WorkflowManage/WorkflowList';
|
||||
import { searchBarWidth } from './styleConstant';
|
||||
|
||||
const ToolbarContainer = styled(Toolbar)`
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
left: ${sidebarWidth + searchBarWidth}px;
|
||||
min-height: unset;
|
||||
/** same as the search bar */
|
||||
height: 56px;
|
||||
`;
|
||||
|
||||
interface IGraphTopToolbarProps {
|
||||
editorReference: MutableRefObject<ITheGraphEditor | undefined>;
|
||||
readonly: boolean;
|
||||
setReadonly: Dispatch<SetStateAction<boolean>>;
|
||||
workflowContext: IWorkflowContext;
|
||||
}
|
||||
export const GraphTopToolbar = (props: IGraphTopToolbarProps) => {
|
||||
const { editorReference, readonly, setReadonly, workflowContext } = props;
|
||||
const { t } = useTranslation();
|
||||
const [, setLocation] = useLocation();
|
||||
|
||||
const runWorkflow = useCallback(() => {
|
||||
console.log('Running workflow...');
|
||||
}, []);
|
||||
|
||||
const backToHome = useCallback(() => {
|
||||
// don't need to save here, because we already debouncedSave after all operations
|
||||
setLocation(`/${WindowNames.main}/${PageType.workflow}/`);
|
||||
}, [setLocation]);
|
||||
|
||||
const toggleReadonly = useCallback(() => {
|
||||
setReadonly((readonly: boolean) => !readonly);
|
||||
}, [setReadonly]);
|
||||
|
||||
const zoomToFit = useCallback(() => {
|
||||
editorReference?.current?.triggerFit();
|
||||
}, [editorReference]);
|
||||
|
||||
const [changeGraphInfoDialogOpen, setChangeGraphInfoDialogOpen] = useState(false);
|
||||
const workspacesList = useWorkspacesListObservable();
|
||||
const [availableFilterTags] = useAvailableFilterTags(workspacesList);
|
||||
const changeGraphInfo = useCallback(() => {
|
||||
setChangeGraphInfoDialogOpen(true);
|
||||
}, []);
|
||||
const closeChangeGraphInfoDialog = useCallback(() => {
|
||||
setChangeGraphInfoDialogOpen(false);
|
||||
}, []);
|
||||
const handleDialogAddWorkflow = useCallback(async (newItem: IWorkflowListItem, oldItem?: IWorkflowListItem) => {
|
||||
await addWorkflowToWiki(newItem, oldItem);
|
||||
workflowContext.setOpenedWorkflowItem(newItem);
|
||||
closeChangeGraphInfoDialog();
|
||||
}, [closeChangeGraphInfoDialog, workflowContext]);
|
||||
|
||||
const changeSelectedItemInfo = useCallback(() => {
|
||||
console.log('Changing selected item info...');
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ToolbarContainer>
|
||||
<Tooltip title={t('Workflow.RunWorkflow')}>
|
||||
<IconButton onClick={runWorkflow}>
|
||||
<PlayArrowIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t('Workflow.BackToHome')}>
|
||||
<IconButton onClick={backToHome}>
|
||||
<HomeIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={readonly ? t('Workflow.ToggleOnReadonly') : t('Workflow.ToggleOffReadonly')}>
|
||||
<IconButton onClick={toggleReadonly}>
|
||||
{readonly ? <VisibilityOnIcon /> : <VisibilityOffIcon />}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t('Workflow.ZoomToFit')}>
|
||||
<IconButton onClick={zoomToFit}>
|
||||
<ZoomOutMapIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t('Workflow.ChangeWorkflowMetadata')}>
|
||||
<IconButton onClick={changeGraphInfo}>
|
||||
<InfoIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={t('Workflow.ChangeSelectedItemInfo')}>
|
||||
<IconButton onClick={changeSelectedItemInfo}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</ToolbarContainer>
|
||||
<AddItemDialog
|
||||
open={changeGraphInfoDialogOpen}
|
||||
onClose={closeChangeGraphInfoDialog}
|
||||
onAdd={handleDialogAddWorkflow}
|
||||
availableFilterTags={availableFilterTags}
|
||||
workspacesList={workspacesList}
|
||||
item={workflowContext.openedWorkflowItem}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1 @@
|
|||
export const searchBarWidth = 300;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { sidebarWidth } from '@/constants/style';
|
||||
import { useThemeObservable } from '@services/theme/hooks';
|
||||
import { useRef } from 'react';
|
||||
import { useContext, useRef, useState } from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import styled from 'styled-components';
|
||||
|
|
@ -13,7 +13,9 @@ import '@fortawesome/fontawesome-free/js/all.js';
|
|||
import '@fortawesome/fontawesome-free/css/all.css';
|
||||
import '@fortawesome/fontawesome-free/css/v4-font-face.css';
|
||||
|
||||
import { WorkflowContext } from '../useContext';
|
||||
import { SearchComponents } from './components/SearchComponents';
|
||||
import { GraphTopToolbar } from './components/Toolbar';
|
||||
import { useLibrary } from './library';
|
||||
import { useMenu } from './menu';
|
||||
import { useMouseEvents } from './mouseEvents';
|
||||
|
|
@ -52,7 +54,9 @@ export function GraphEditor() {
|
|||
const theme = useThemeObservable();
|
||||
|
||||
const [graph, setGraph] = useSaveLoadGraph();
|
||||
const workflowContext = useContext(WorkflowContext);
|
||||
const library = useLibrary();
|
||||
const [readonly, setReadonly] = useState(false);
|
||||
|
||||
// methods
|
||||
// const { subscribeGraph, unsubscribeGraph } = useSubscribeGraph({ readonly });
|
||||
|
|
@ -79,7 +83,6 @@ export function GraphEditor() {
|
|||
<TheGraph.App
|
||||
graph={graph}
|
||||
library={library}
|
||||
readonly={false}
|
||||
height={window.innerHeight}
|
||||
width={window.innerWidth}
|
||||
offsetX={sidebarWidth}
|
||||
|
|
@ -88,6 +91,7 @@ export function GraphEditor() {
|
|||
onNodeSelection={onNodeSelection}
|
||||
onEdgeSelection={onEdgeSelection}
|
||||
getEditorRef={editorReference}
|
||||
readonly={readonly}
|
||||
/>
|
||||
</TheGraphContainer>
|
||||
<ThumbnailContainer>
|
||||
|
|
@ -106,6 +110,7 @@ export function GraphEditor() {
|
|||
/>
|
||||
</ThumbnailContainer>
|
||||
<SearchComponents library={library} addNode={addNode} />
|
||||
<GraphTopToolbar editorReference={editorReference} readonly={readonly} setReadonly={setReadonly} workflowContext={workflowContext} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import type { IWorkflowListItem } from './WorkflowList';
|
|||
|
||||
interface AddItemDialogProps {
|
||||
availableFilterTags: string[];
|
||||
onAdd: (newItem: IWorkflowListItem) => Promise<void>;
|
||||
item?: IWorkflowListItem;
|
||||
onAdd: (newItem: IWorkflowListItem, oldItem?: IWorkflowListItem) => Promise<void>;
|
||||
onClose: () => void;
|
||||
open: boolean;
|
||||
workspacesList: IWorkspaceWithMetadata[] | undefined;
|
||||
|
|
@ -20,23 +21,37 @@ export const AddItemDialog: React.FC<AddItemDialogProps> = ({
|
|||
onAdd,
|
||||
availableFilterTags,
|
||||
workspacesList,
|
||||
item,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [title, setTitle] = useState('');
|
||||
const [description, setDescription] = useState('');
|
||||
const isModifyMode = !!item;
|
||||
const [title, setTitle] = useState(item?.title ?? '');
|
||||
const [description, setDescription] = useState(item?.description ?? '');
|
||||
const [hasError, setHasError] = useState(false);
|
||||
const [doneMessageSnackBarOpen, setDoneMessageSnackBarOpen] = useState(false);
|
||||
const [workspaceToSaveTo, setWorkspaceToSaveTo] = useState<IWorkspaceWithMetadata | undefined | null>(workspacesList?.[0]);
|
||||
const [workspaceToSaveTo, setWorkspaceToSaveTo] = useState<IWorkspaceWithMetadata | undefined | null>(
|
||||
item?.workspaceID ? workspacesList?.find(workspace => workspace.id === item.workspaceID) : workspacesList?.[0],
|
||||
);
|
||||
useEffect(() => {
|
||||
// when list was undefined and change to have value, auto set default value once.
|
||||
if (workspaceToSaveTo === undefined && workspacesList?.[0]) {
|
||||
setWorkspaceToSaveTo(workspacesList?.[0]);
|
||||
}
|
||||
}, [workspaceToSaveTo, workspacesList]);
|
||||
const [tags, setTags] = useState<string[]>([]);
|
||||
const [tags, setTags] = useState<string[]>(item?.tags ?? []);
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
setTitle(item.title ?? '');
|
||||
setDescription(item.description ?? '');
|
||||
setTags(item.tags ?? []);
|
||||
setWorkspaceToSaveTo(workspacesList?.find(workspace => workspace.id === item.workspaceID));
|
||||
}
|
||||
}, [item, workspacesList]);
|
||||
const closeAndCleanup = useCallback(() => {
|
||||
setTitle('');
|
||||
setDescription('');
|
||||
setTags([]);
|
||||
// no need to reset workspace dropdown, because later workflow might save to same workspace
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
const workspaceIDs = useMemo(() => workspacesList?.map(workspace => workspace.id) ?? [], [workspacesList]);
|
||||
|
|
@ -52,16 +67,18 @@ export const AddItemDialog: React.FC<AddItemDialogProps> = ({
|
|||
return;
|
||||
}
|
||||
const newItem: IWorkflowListItem = {
|
||||
...item,
|
||||
id: `${workspaceID}:${title}`,
|
||||
title,
|
||||
tags,
|
||||
workspaceID,
|
||||
graphJSONString: '{}',
|
||||
description,
|
||||
};
|
||||
await onAdd(newItem);
|
||||
await onAdd(newItem, item);
|
||||
setDoneMessageSnackBarOpen(true);
|
||||
closeAndCleanup();
|
||||
}, [workspaceToSaveTo?.id, workspacesList, workspaceIDs, title, tags, onAdd, closeAndCleanup]);
|
||||
}, [workspaceToSaveTo?.id, workspacesList, workspaceIDs, title, tags, description, item, onAdd, closeAndCleanup]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -71,13 +88,13 @@ export const AddItemDialog: React.FC<AddItemDialogProps> = ({
|
|||
setDoneMessageSnackBarOpen(false);
|
||||
}}
|
||||
autoHideDuration={3000}
|
||||
message={t('Workflow.AddNewWorkflowDoneMessage')}
|
||||
message={isModifyMode ? t('Workflow.ChangeWorkflowMetadataDoneMessage') : t('Workflow.AddNewWorkflowDoneMessage')}
|
||||
anchorOrigin={{ horizontal: 'center', vertical: 'top' }}
|
||||
/>
|
||||
<Dialog open={open} onClose={closeAndCleanup}>
|
||||
<DialogTitle>{t('Workflow.AddNewWorkflow')}</DialogTitle>
|
||||
<DialogTitle>{isModifyMode ? t('Workflow.ChangeWorkflowMetadata') : t('Workflow.AddNewWorkflow')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>{t('Workflow.AddNewWorkflowDescription')}</DialogContentText>
|
||||
<DialogContentText>{isModifyMode ? t('Workflow.ChangeWorkflowMetadataDescription') : t('Workflow.AddNewWorkflowDescription')}</DialogContentText>
|
||||
<FormControl fullWidth>
|
||||
<TextField
|
||||
required
|
||||
|
|
|
|||
|
|
@ -38,12 +38,13 @@ const ItemMenuCardActions = styled(CardActions)`
|
|||
`;
|
||||
|
||||
interface IWorkflowListItemProps {
|
||||
handleOpenChangeMetadataDialog: (item: IWorkflowListItem) => void;
|
||||
item: IWorkflowListItem;
|
||||
onDeleteWorkflow: (item: IWorkflowListItem) => void;
|
||||
}
|
||||
export function WorkflowListItem(props: IWorkflowListItemProps) {
|
||||
const { t } = useTranslation();
|
||||
const { onDeleteWorkflow, item } = props;
|
||||
const { onDeleteWorkflow, item, handleOpenChangeMetadataDialog: handleOpenChangeMetadataDialogRaw } = props;
|
||||
const [anchorElement, setAnchorElement] = useState<null | HTMLElement>(null);
|
||||
|
||||
const handleOpenItemMenu = useCallback((event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
|
|
@ -57,6 +58,10 @@ export function WorkflowListItem(props: IWorkflowListItemProps) {
|
|||
setAnchorElement(null);
|
||||
onDeleteWorkflow(item);
|
||||
}, [item, onDeleteWorkflow]);
|
||||
const handleOpenChangeMetadataDialog = useCallback(() => {
|
||||
setAnchorElement(null);
|
||||
handleOpenChangeMetadataDialogRaw(item);
|
||||
}, [item, handleOpenChangeMetadataDialogRaw]);
|
||||
|
||||
const [, setLocation] = useLocation();
|
||||
const handleOpenInWiki = useCallback(async () => {
|
||||
|
|
@ -110,6 +115,11 @@ export function WorkflowListItem(props: IWorkflowListItemProps) {
|
|||
TransitionComponent={Fade}
|
||||
>
|
||||
<MenuItem onClick={handleDelete}>{t('Delete')}</MenuItem>
|
||||
<MenuItem
|
||||
onClick={handleOpenChangeMetadataDialog}
|
||||
>
|
||||
{t('Workflow.ChangeMetadata')}
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleOpenInWiki}>
|
||||
{t('Workflow.OpenInWorkspaceTiddler', { title: item.title, workspace: item.metadata?.workspace?.name ?? t('AddWorkspace.MainWorkspace') })}
|
||||
</MenuItem>
|
||||
|
|
@ -136,11 +146,12 @@ export interface IWorkflowListItem {
|
|||
}
|
||||
|
||||
interface IWorkflowListProps {
|
||||
handleOpenChangeMetadataDialog: (item: IWorkflowListItem) => void;
|
||||
onDeleteWorkflow: (item: IWorkflowListItem) => void;
|
||||
workflows: IWorkflowListItem[];
|
||||
}
|
||||
|
||||
export const WorkflowList: React.FC<IWorkflowListProps> = ({ workflows, onDeleteWorkflow }) => {
|
||||
export const WorkflowList: React.FC<IWorkflowListProps> = ({ workflows, onDeleteWorkflow, handleOpenChangeMetadataDialog }) => {
|
||||
const [itemToDelete, setDeleteItem] = useState<IWorkflowListItem | undefined>();
|
||||
const handleDeleteConfirmed = useCallback(() => {
|
||||
if (itemToDelete) {
|
||||
|
|
@ -161,7 +172,7 @@ export const WorkflowList: React.FC<IWorkflowListProps> = ({ workflows, onDelete
|
|||
<Grid container spacing={{ xs: 2, md: 3 }} columns={{ xs: 4, sm: 8, md: 12 }}>
|
||||
{workflows.map((workflow) => (
|
||||
<Grid item xs={2} sm={4} md={4} key={workflow.id}>
|
||||
<WorkflowListItem item={workflow} onDeleteWorkflow={handleDeleteWithConfirmation} />
|
||||
<WorkflowListItem item={workflow} onDeleteWorkflow={handleDeleteWithConfirmation} handleOpenChangeMetadataDialog={handleOpenChangeMetadataDialog} />
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ const SearchRegionContainer = styled(Box)`
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 1em;
|
||||
padding-right: 1em;
|
||||
width: 100%;
|
||||
`;
|
||||
const SearchBar = styled(TextField)`
|
||||
|
|
@ -42,15 +43,18 @@ export const WorkflowManage: React.FC = () => {
|
|||
const [availableFilterTags, setTagsByWorkspace] = useAvailableFilterTags(workspacesList);
|
||||
const [workflows, onAddWorkflow, onDeleteWorkflow] = useWorkflows(workspacesList, setTagsByWorkspace);
|
||||
|
||||
const handleOpenDialog = useCallback(() => {
|
||||
const [itemSelectedForDialog, setItemSelectedForDialog] = useState<IWorkflowListItem | undefined>();
|
||||
const handleOpenDialog = useCallback((item?: IWorkflowListItem) => {
|
||||
setItemSelectedForDialog(item);
|
||||
setDialogOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleCloseDialog = useCallback(() => {
|
||||
setDialogOpen(false);
|
||||
// eslint-disable-next-line unicorn/no-useless-undefined
|
||||
setItemSelectedForDialog(undefined);
|
||||
}, []);
|
||||
const handleDialogAddWorkflow = useCallback(async (newItem: IWorkflowListItem) => {
|
||||
await onAddWorkflow(newItem);
|
||||
const handleDialogAddWorkflow = useCallback(async (newItem: IWorkflowListItem, oldItem?: IWorkflowListItem) => {
|
||||
await onAddWorkflow(newItem, oldItem);
|
||||
handleCloseDialog();
|
||||
}, [handleCloseDialog, onAddWorkflow]);
|
||||
|
||||
|
|
@ -87,9 +91,15 @@ export const WorkflowManage: React.FC = () => {
|
|||
))}
|
||||
</Stack>
|
||||
</SearchRegionContainer>
|
||||
<WorkflowList workflows={filteredWorkflows} onDeleteWorkflow={onDeleteWorkflow} />
|
||||
<WorkflowList workflows={filteredWorkflows} onDeleteWorkflow={onDeleteWorkflow} handleOpenChangeMetadataDialog={handleOpenDialog} />
|
||||
</SimpleBar>
|
||||
<AddNewItemFloatingButton color='primary' aria-label='add' onClick={handleOpenDialog}>
|
||||
<AddNewItemFloatingButton
|
||||
color='primary'
|
||||
aria-label='add'
|
||||
onClick={() => {
|
||||
handleOpenDialog();
|
||||
}}
|
||||
>
|
||||
<AddIcon />
|
||||
</AddNewItemFloatingButton>
|
||||
<AddItemDialog
|
||||
|
|
@ -98,6 +108,7 @@ export const WorkflowManage: React.FC = () => {
|
|||
onAdd={handleDialogAddWorkflow}
|
||||
availableFilterTags={availableFilterTags}
|
||||
workspacesList={workspacesList}
|
||||
item={itemSelectedForDialog}
|
||||
/>
|
||||
</WorkflowManageContainer>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -112,12 +112,12 @@ export function useWorkflows(workspacesList: IWorkspaceWithMetadata[] | undefine
|
|||
const initialWorkflows = useWorkflowFromWiki(workspacesList);
|
||||
// loading workflows using filter expression is expensive, so we only do this on initial load. Later just update&use local state value
|
||||
useEffect(() => {
|
||||
setWorkflows(initialWorkflows);
|
||||
setWorkflows(initialWorkflows.sort(sortWorkflow));
|
||||
}, [initialWorkflows]);
|
||||
const onAddWorkflow = useCallback(async (newItem: IWorkflowListItem) => {
|
||||
await addWorkflowToWiki(newItem);
|
||||
const onAddWorkflow = useCallback(async (newItem: IWorkflowListItem, oldItem?: IWorkflowListItem) => {
|
||||
await addWorkflowToWiki(newItem, oldItem);
|
||||
// can overwrite a old workflow with same title
|
||||
setWorkflows((workflows) => [...workflows.filter(item => item.title !== newItem.title), newItem]);
|
||||
setWorkflows((workflows) => [...workflows.filter(item => item.title !== newItem.title), newItem].sort(sortWorkflow));
|
||||
// update tag list in the search region tags filter
|
||||
setTagsByWorkspace((previousTagsByWorkspace) => {
|
||||
// add newly appeared tags to local state
|
||||
|
|
@ -138,13 +138,14 @@ export function useWorkflows(workspacesList: IWorkspaceWithMetadata[] | undefine
|
|||
item.title,
|
||||
);
|
||||
// delete workflow from local state
|
||||
setWorkflows((workflows) => workflows.filter(workflow => workflow.id !== item.id));
|
||||
setWorkflows((workflows) => workflows.filter(workflow => workflow.id !== item.id).sort(sortWorkflow));
|
||||
}, [setWorkflows]);
|
||||
|
||||
return [workflows, onAddWorkflow, onDeleteWorkflow] as const;
|
||||
}
|
||||
|
||||
export async function addWorkflowToWiki(newItem: IWorkflowListItem) {
|
||||
export async function addWorkflowToWiki(newItem: IWorkflowListItem, oldItem?: IWorkflowListItem) {
|
||||
// FIXME: this won't resolve if user haven't click on wiki once, the browser view might not initialized
|
||||
await window.service.wiki.wikiOperation(
|
||||
WikiChannel.addTiddler,
|
||||
newItem.workspaceID,
|
||||
|
|
@ -159,4 +160,21 @@ export async function addWorkflowToWiki(newItem: IWorkflowListItem) {
|
|||
} satisfies Omit<IWorkflowTiddler, 'text' | 'title'>,
|
||||
{ withDate: true },
|
||||
);
|
||||
// we sort workflows using workflow.metadata.tiddler.modified, so we need to update it (side effect)
|
||||
if (newItem.metadata?.tiddler) {
|
||||
// @ts-expect-error Cannot assign to 'modified' because it is a read-only property.ts(2540)
|
||||
newItem.metadata.tiddler.modified = new Date();
|
||||
}
|
||||
// when change title, wiki requires delete old tiddler manually
|
||||
if (oldItem !== undefined && oldItem.title !== newItem.title) {
|
||||
window.service.wiki.wikiOperation(
|
||||
WikiChannel.deleteTiddler,
|
||||
oldItem.workspaceID,
|
||||
oldItem.title,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function sortWorkflow(workflow1: IWorkflowListItem, workflow2: IWorkflowListItem) {
|
||||
return (workflow2.metadata?.tiddler?.modified ?? new Date()).getTime() - (workflow1.metadata?.tiddler?.modified ?? new Date()).getTime();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue