Skip to content

Commit

Permalink
feat(ChatGPT): 🎸 folder move
Browse files Browse the repository at this point in the history
  • Loading branch information
Su Weijie committed Sep 6, 2023
1 parent f44ebad commit 596660e
Show file tree
Hide file tree
Showing 10 changed files with 193 additions and 77 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"devDependencies": {
"@rspack/cli": "0.3.1",
"@tauri-apps/cli": "^1.4.0",
"@testing-library/jest-dom": "^6.1.2",
"@testing-library/jest-dom": "^6.1.3",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
Expand Down
15 changes: 10 additions & 5 deletions packages/ChatGPT/src/components/FolderSelect/FolderSelectItem.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
import { Folder } from '@chatgpt/types/folder';
import { ExpandLess, ExpandMore } from '@mui/icons-material';
import { Collapse, ListItemText, MenuItem } from '@mui/material';
import { useCallback, useContext, useState } from 'react';
import { useCallback, useContext, useMemo, useState } from 'react';
import { FolderSelectContext } from '.';

export interface FolderSelectItemProps {
folder: Folder;
disabled?: boolean;
}

export default function FolderSelectItem({ folder }: FolderSelectItemProps) {
const { selectedId, onSelect } = useContext(FolderSelectContext);
export default function FolderSelectItem({ folder, disabled: parentDisabled }: FolderSelectItemProps) {
const { selectedId, onSelect, disabledFolderIds } = useContext(FolderSelectContext);
const [open, setOpen] = useState(false);
const handleClick = useCallback(() => {
onSelect(folder.id);
setOpen((value) => !value);
}, [folder.id, onSelect]);
const disabled = useMemo(
() => disabledFolderIds.includes(folder.id) || parentDisabled,
[disabledFolderIds, folder.id, parentDisabled],
);
return (
<>
<MenuItem selected={selectedId === folder.id} onClick={handleClick}>
<MenuItem disabled={disabled} selected={selectedId === folder.id} onClick={handleClick}>
<ListItemText primary={folder.name} secondary={folder.path} />
{open ? <ExpandLess /> : <ExpandMore />}
</MenuItem>
{folder.folders.length > 0 && (
<Collapse in={open} timeout="auto" unmountOnExit sx={{ pl: 2 }}>
{folder.folders.map((folder) => (
<FolderSelectItem key={folder.id} folder={folder} />
<FolderSelectItem disabled={disabled} key={folder.id} folder={folder} />
))}
</Collapse>
)}
Expand Down
11 changes: 9 additions & 2 deletions packages/ChatGPT/src/components/FolderSelect/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,33 @@ import React, { ForwardedRef, createContext, useMemo } from 'react';
export type FolderSelectContextType = {
selectedId: number | null;
onSelect: (id: number | null) => void;
disabledFolderIds: number[];
};

export const FolderSelectContext = createContext<FolderSelectContextType>({
selectedId: null,
onSelect: () => {},
disabledFolderIds: [],
});

export interface FolderSelectProps {
value: number | null;
onChange: (id: number | null) => void;
disabledFolderIds?: number[];
}

function FolderSelect({ value, onChange }: FolderSelectProps, ref: ForwardedRef<HTMLUListElement>) {
function FolderSelect(
{ value, onChange, disabledFolderIds = [] }: FolderSelectProps,
ref: ForwardedRef<HTMLUListElement>,
) {
const folders = useAppSelector(SELECT_FOLDERS);
const contextValue = useMemo<FolderSelectContextType>(() => {
return {
selectedId: value,
onSelect: onChange,
disabledFolderIds,
};
}, [onChange, value]);
}, [disabledFolderIds, onChange, value]);
return (
<FolderSelectContext.Provider value={contextValue}>
<MenuList ref={ref}>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
FormLabel,
IconButton,
Tooltip,
} from '@mui/material';
import DriveFileMoveIcon from '@mui/icons-material/DriveFileMove';
import { useCallback, useState } from 'react';
import FolderSelect from '@chatgpt/components/FolderSelect';
import { Controller, useForm } from 'react-hook-form';
import { MoveFolderParams, moveFolder } from '@chatgpt/service/chat';
import { useAppDispatch } from '@chatgpt/app/hooks';
import { fetchConversations } from '@chatgpt/features/Conversations/conversationSlice';
import { Folder } from '@chatgpt/types/folder';

export interface MoveFolderProps {
folder: Folder;
}

export default function MoveFolder({ folder }: MoveFolderProps) {
const dispatch = useAppDispatch();
const [open, setOpen] = useState(false);
const handleOpen = useCallback(() => setOpen(true), []);
const handleClose = useCallback(() => setOpen(false), []);
const { control, handleSubmit } = useForm<Pick<MoveFolderParams, 'parentId'>>({
defaultValues: {
parentId: folder.parentId,
},
});
const onSubmit = useCallback(
async ({ parentId }: Pick<MoveFolderParams, 'parentId'>) => {
await moveFolder({ parentId, id: folder.id });
dispatch(fetchConversations());
handleClose();
},
[folder.id, dispatch, handleClose],
);
return (
<>
<Tooltip title="Move">
<IconButton onClick={handleOpen}>
<DriveFileMoveIcon />
</IconButton>
</Tooltip>
<Dialog
component="form"
onSubmit={handleSubmit(onSubmit)}
open={open}
onClose={handleClose}
fullWidth
sx={{
height: '500px',
'& .MuiDialog-paper': {
backgroundColor: (theme) => theme.palette.background.paper + 'a0',
backdropFilter: 'blur(20px)',
},
}}
>
<DialogTitle>Move Folder</DialogTitle>
<DialogContent>
<FormLabel sx={{ display: 'block' }}>Parent Folder :</FormLabel>
<Controller
control={control}
name="parentId"
render={({ field }) => <FolderSelect {...field} disabledFolderIds={[folder.id]} />}
/>
</DialogContent>
<DialogActions>
<Button variant="text" type="submit">
Move
</Button>
</DialogActions>
</Dialog>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton, Typography } from '@mui/material';
import EditIcon from '@mui/icons-material/Edit';
import { useCallback, useState } from 'react';
import { useAppDispatch } from '@chatgpt/app/hooks';
import FolderEdit, { FolderForm } from '@chatgpt/components/FolderEdit';
import { fetchConversations } from '@chatgpt/features/Conversations/conversationSlice';
import { updateFolder } from '@chatgpt/service/chat';
import { Folder, NewFolder } from '@chatgpt/types/folder';
import FolderEdit, { FolderForm } from '@chatgpt/components/FolderEdit';
import { Delete } from '@mui/icons-material';
import { deleteFolder, updateFolder } from '@chatgpt/service/chat';
import { Button, Dialog, DialogActions, DialogContent, DialogTitle, IconButton } from '@mui/material';
import { useCallback, useState } from 'react';
import EditIcon from '@mui/icons-material/Edit';

export interface FolderHeaderProps {
folder: Folder;
}

export default function FolderHeader({ folder }: FolderHeaderProps) {
export default function UpdateFolder({ folder }: FolderHeaderProps) {
const dispatch = useAppDispatch();
const [open, setOpen] = useState(false);
const handleOpen = useCallback(() => setOpen(true), []);
Expand All @@ -27,43 +27,11 @@ export default function FolderHeader({ folder }: FolderHeaderProps) {
},
[folder.parentId, folder.id, dispatch, handleClose],
);
const handleDelete = useCallback(async () => {
await deleteFolder({ id: folder.id });
dispatch(fetchConversations());
}, [dispatch, folder.id]);

return (
<Box
data-tauri-drag-region
sx={{
width: '100%',
display: 'flex',
p: 1,
justifyContent: 'center',
boxShadow: (theme) => theme.shadows[3].split(',0px')[0],
}}
>
<Box sx={{ flexGrow: 1, display: 'flex', alignItems: 'center', ml: 1 }} data-tauri-drag-region>
<Typography data-tauri-drag-region variant="h6" component="span" paragraph={false}>
{folder.name}
</Typography>
<Typography
sx={{ ml: 1 }}
data-tauri-drag-region
variant="body2"
color="inherit"
component="span"
paragraph={false}
>
{folder.path}
</Typography>
</Box>
<>
<IconButton onClick={handleOpen}>
<EditIcon />
</IconButton>
<IconButton onClick={handleDelete}>
<Delete />
</IconButton>
<Dialog
open={open}
onClose={handleClose}
Expand Down Expand Up @@ -95,6 +63,6 @@ export default function FolderHeader({ folder }: FolderHeaderProps) {
</Button>
</DialogActions>
</Dialog>
</Box>
</>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Box, IconButton, Typography } from '@mui/material';
import { useCallback } from 'react';
import { useAppDispatch } from '@chatgpt/app/hooks';
import { fetchConversations } from '@chatgpt/features/Conversations/conversationSlice';
import { Folder } from '@chatgpt/types/folder';
import { Delete } from '@mui/icons-material';
import { deleteFolder } from '@chatgpt/service/chat';
import UpdateFolder from './UpdateFolder';
import MoveFolder from './MoveFolder';
export interface FolderHeaderProps {
folder: Folder;
}

export default function FolderHeader({ folder }: FolderHeaderProps) {
const dispatch = useAppDispatch();
const handleDelete = useCallback(async () => {
await deleteFolder({ id: folder.id });
dispatch(fetchConversations());
}, [dispatch, folder.id]);

return (
<Box
data-tauri-drag-region
sx={{
width: '100%',
display: 'flex',
p: 1,
justifyContent: 'center',
boxShadow: (theme) => theme.shadows[3].split(',0px')[0],
}}
>
<Box sx={{ flexGrow: 1, display: 'flex', alignItems: 'center', ml: 1 }} data-tauri-drag-region>
<Typography data-tauri-drag-region variant="h6" component="span" paragraph={false}>
{folder.name}
</Typography>
<Typography
sx={{ ml: 1 }}
data-tauri-drag-region
variant="body2"
color="inherit"
component="span"
paragraph={false}
>
{folder.path}
</Typography>
</Box>
<UpdateFolder folder={folder} />
<IconButton onClick={handleDelete}>
<Delete />
</IconButton>
<MoveFolder folder={folder} />
</Box>
);
}
2 changes: 1 addition & 1 deletion packages/ChatGPT/src/features/Home/FolderDetail/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Folder } from '@chatgpt/types/folder';
import { List, Box } from '@mui/material';
import FolderHeader from './components/Header';
import FolderHeader from './components/FolderHeader';
import ContentList from './components/ContentList';
import ContentEmpty from './components/ContentEmpty';

Expand Down
9 changes: 9 additions & 0 deletions packages/ChatGPT/src/service/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,12 @@ export interface MoveConversationParams {
export async function moveConversation(params: MoveConversationParams) {
await appInvoke<MoveConversationParams, unknown>('plugin:chat|move_conversation', params);
}

export interface MoveFolderParams {
id: number;
parentId: number | null;
}

export async function moveFolder(params: MoveFolderParams) {
await appInvoke<MoveFolderParams, unknown>('plugin:chat|move_folder', params);
}
Loading

0 comments on commit 596660e

Please sign in to comment.