Skip to content

Commit

Permalink
Enable right-click to delete option in Explorer cards (#1341)
Browse files Browse the repository at this point in the history
* Export `fsPromises.unlink` from `node:fs` for `metafiles/deleteFile` thunk

* Added `DeleteFileDialog` modal

* Close `DeleteBranchDialog` on successful deletion

* Allow right-click on `File` in `Explorer` to initiate `DeleteFileDialog` modal
  • Loading branch information
nelsonni committed Nov 6, 2023
1 parent ecbbf38 commit 4d36d96
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/preload/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export {
replaceExt,
validateFileName,
writeFileAsync,
unlink,
} from './io';
export {
clipboard,
Expand Down
1 change: 1 addition & 0 deletions packages/preload/src/io/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export * from './io-readDir';
export * from './io-readFile';
export * from './io-validateFileName';
export * from './io-writeFile';
export {unlink} from 'node:fs/promises';

/**
* **WARNING** If a method is not included here, please see
Expand Down
15 changes: 14 additions & 1 deletion packages/renderer/src/components/Explorer/File.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {extractFilename, isModified, isStaged, isUnmerged, uuid} from '#preload';
import {Description} from '@mui/icons-material';
import {Skeleton} from '@mui/material';
import type {UUID} from '@syn-types/app';
Expand All @@ -8,7 +9,7 @@ import {isFilebasedMetafile, isVersionedMetafile} from '../../store/slices/metaf
import {createCard} from '../../store/thunks/cards';
import {StageButton, UnstageButton} from './GitButtons';
import {StyledTreeItem} from './TreeItem';
import {extractFilename, isModified, isStaged, isUnmerged} from '#preload';
import {modalAdded} from '/@/store/slices/modals';

const File = ({id}: {id: UUID}) => {
const metafile = useAppSelector(state => metafileSelectors.selectById(state, id));
Expand All @@ -27,6 +28,17 @@ const File = ({id}: {id: UUID}) => {
if (isFilebasedMetafile(metafile)) await dispatch(createCard({path: metafile.path}));
};

const handleRightClick = () => {
if (isFilebasedMetafile(metafile))
dispatch(
modalAdded({
id: uuid(),
type: 'DeleteFileDialog',
metafile: metafile.id,
}),
);
};

return (
<>
{isFilebasedMetafile(metafile) ? (
Expand All @@ -53,6 +65,7 @@ const File = ({id}: {id: UUID}) => {
endIcon={<Description />}
color={motif?.color}
onClick={handleClick}
onContextMenu={handleRightClick}
/>
) : (
<Skeleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const DeleteBranchDialog = ({modal}: {modal: DeleteBranchDialogModal}) => {
? `Deleted ${branch.scope}/${branch.ref} branch...`
: `Unable to delete ${branch.scope}/${branch.ref} branch...`,
);
if (result) handleClose();
}
};

Expand Down
78 changes: 78 additions & 0 deletions packages/renderer/src/components/Modal/DeleteFileDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
Box,
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
styled,
} from '@mui/material';
import type {DeleteFileDialog as DeleteFileDialogModal} from '@syn-types/modal';
import {useAppDispatch, useAppSelector} from '../../store/hooks';
import {modalRemoved} from '../../store/slices/modals';
import {deleteFile} from '/@/store/thunks/metafiles';
import metafileSelectors from '/@/store/selectors/metafiles';

const DeleteFileDialog = ({modal}: {modal: DeleteFileDialogModal}) => {
const dispatch = useAppDispatch();
const metafile = useAppSelector(state => metafileSelectors.selectById(state, modal.metafile));

const handleClose = () => dispatch(modalRemoved(modal.id));
const handleClick = async () => {
if (metafile) {
const result = await dispatch(deleteFile(metafile.id));
console.log(
result ? `Deleted ${metafile.name} file...` : `Unable to delete ${metafile.name} file...`,
);
if (result) handleClose();
}
};

return (
<StyledDialog
open
onClose={handleClose}
aria-labelledby="delete-file-dialog-title"
aria-describedby="delete-file-description"
>
<DialogTitle id="delete-file-dialog-title">Delete File</DialogTitle>
<DialogContent sx={{px: 2, pb: 1.5}}>
<DialogContentText id="delete-file-description">
Are you sure you want to permanently delete the file{' '}
<Box
component="span"
fontWeight="fontWeightBold"
>
{metafile?.name}
</Box>{' '}
?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
id="cancel-button"
variant="contained"
onClick={handleClose}
>
Cancel
</Button>
<Button
id="delete-file-button"
variant="contained"
onClick={handleClick}
>
Delete
</Button>
</DialogActions>
</StyledDialog>
);
};

const StyledDialog = styled(Dialog)(() => ({
'& .MuiDialog-paper': {
width: 400,
},
}));

export default DeleteFileDialog;
6 changes: 5 additions & 1 deletion packages/renderer/src/components/Modal/ModalComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {Modal} from '@syn-types/modal';
import GitGraph from '../GitGraph';
import CommitDialog from './CommitDialog';
import DeleteBranchDialog from './DeleteBranchDialog';
import DeleteFileDialog from './DeleteFileDialog';
import MergeDialog from './MergeDialog';
import NewBranchDialog from './NewBranchDialog';
import NewCardDialog from './NewCardDialog';
Expand All @@ -10,6 +11,7 @@ import RevertCommitDialog from './RevertCommitDialog';
import {
isCommitDialogModal,
isDeleteBranchDialog,
isDeleteFileDialog,
isGitGraphModal,
isMergeDialogModal,
isNewBranchDialog,
Expand All @@ -31,8 +33,10 @@ const ModalComponent = (props: Modal) => {
return isNewBranchDialog(props) ? <NewBranchDialog modal={props} /> : null;
case 'DeleteBranchDialog':
return isDeleteBranchDialog(props) ? <DeleteBranchDialog modal={props} /> : null;
case 'DeleteFileDialog':
return isDeleteFileDialog(props) ? <DeleteFileDialog modal={props} /> : null;
case 'NewCardDialog':
// no specific fields required for `NewCardDialog`, so just using Modal props
// no specific fields required for `NewCardDialog`, so no type predicate is needed
return <NewCardDialog modal={props} />;
case 'Notification':
return isNotification(props) ? <Notification {...props} /> : null;
Expand Down
4 changes: 4 additions & 0 deletions packages/renderer/src/store/slices/modals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {createEntityAdapter, createSlice} from '@reduxjs/toolkit';
import type {
CommitDialog,
DeleteBranchDialog,
DeleteFileDialog,
GitGraph,
MergeDialog,
Modal,
Expand Down Expand Up @@ -37,6 +38,9 @@ export const isNewBranchDialog = (modal: Modal | undefined): modal is NewBranchD
export const isDeleteBranchDialog = (modal: Modal | undefined): modal is DeleteBranchDialog =>
isDefined(modal) && modal.type === 'DeleteBranchDialog';

export const isDeleteFileDialog = (modal: Modal | undefined): modal is DeleteFileDialog =>
isDefined(modal) && modal.type === 'DeleteFileDialog';

export const isNotification = (modal: Modal | undefined): modal is Notification =>
isDefined(modal) && modal.type === 'Notification';

Expand Down
28 changes: 28 additions & 0 deletions packages/renderer/src/store/thunks/metafiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
isVersionedMetafile,
isVirtualMetafile,
metafileAdded,
metafileRemoved,
metafileUpdated,
} from '../slices/metafiles';
import {addBranch, fetchBranch, updateBranch} from './branches';
Expand Down Expand Up @@ -49,6 +50,7 @@ import {
uuid,
worktreeStatus,
writeFileAsync,
unlink,
} from '#preload';

/**
Expand Down Expand Up @@ -336,6 +338,32 @@ export const saveFile = createAppAsyncThunk<boolean, {id: UUID; filepath?: strin
},
);

/**
* Delete file from the filesystem and remove Metafile object from the store.
* @param obj - A destructured object for named parameters.
* @param obj.id - The UUID of a {@link Metafile} object that should be deleted.
* @returns {boolean} A boolean indicating true if the filesystem and metafile were successfully
* deleted, false otherwise.
*/
export const deleteFile = createAppAsyncThunk<boolean, UUID>(
'metafiles/deleteFile',
async (id, thunkAPI) => {
const metafile = thunkAPI.getState().metafiles.entities[id];

if (isFilebasedMetafile(metafile)) {
// delete file
await unlink(metafile.path);
// remove metafile
thunkAPI.dispatch(metafileRemoved(metafile.id));
// update parent metafile
const parent = await thunkAPI.dispatch(fetchParentMetafile(metafile)).unwrap();
if (parent) thunkAPI.dispatch(updateDirectoryMetafile(parent));
return true;
}
return false;
},
);

export const stageMetafile = createAppAsyncThunk<void, UUID>(
'metafiles/stageMetafile',
async (id, thunkAPI) => {
Expand Down
1 change: 1 addition & 0 deletions types/@syn-types/app.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export type ModalType =
| 'BranchList'
| 'CloneSelector'
| 'DeleteBranchDialog'
| 'DeleteFileDialog'
| 'GitGraph'
| 'MergeDialog'
| 'MergeSelector'
Expand Down
8 changes: 8 additions & 0 deletions types/@syn-types/modal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export type Modal = {
Partial<MergeDialogProps> &
Partial<NewBranchDialogProps> &
Partial<DeleteBranchDialogProps> &
Partial<DeleteFileDialogProps> &
Partial<NotificationProps> &
Partial<RevertCommitDialogProps>;

Expand Down Expand Up @@ -44,6 +45,13 @@ export type MergeDialogProps = {
readonly mode?: MergeAction;
};

/** A modal for deleting an existinf file within the filesystem and Redux store. */
export type DeleteFileDialog = Prettify<Override<Modal, DeleteFileDialogProps>>;
export type DeleteFileDialogProps = {
/** The UUID for associated Metafile object. */
readonly metafile: UUID;
};

/** A modal for creating a new branch within the filesystem and Redux store. */
export type NewBranchDialog = Prettify<Override<Modal, NewBranchDialogProps>>;
export type NewBranchDialogProps = {
Expand Down

0 comments on commit 4d36d96

Please sign in to comment.