Skip to content

Commit

Permalink
Added dialog to select folder and option to add subfolders
Browse files Browse the repository at this point in the history
  • Loading branch information
wladimiiir committed Oct 27, 2024
1 parent a2a1949 commit 089da88
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 12 deletions.
20 changes: 15 additions & 5 deletions src/main/ipcHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { eventManager } from './eventManager';
import { Store } from './store';
import { MODEL_PROVIDERS } from './models/modelProvider';

const scanFolder = async (store: Store, folderPath: string, modelManager: ModelManager) => {
const scanFolder = async (store: Store, folderPath: string, includeSubdirectories: boolean, modelManager: ModelManager) => {
const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif'];
const images: Image[] = store.getImages() || [];

Expand All @@ -16,7 +16,7 @@ const scanFolder = async (store: Store, folderPath: string, modelManager: ModelM
for (const entry of entries) {
const imagePath = path.join(dir, entry.name);

if (entry.isDirectory()) {
if (entry.isDirectory() && includeSubdirectories) {
await scanRecursively(imagePath);
} else if (entry.isFile() && imageExtensions.includes(path.extname(entry.name).toLowerCase())) {
let image = images.find((image) => image.id === imagePath);
Expand Down Expand Up @@ -51,7 +51,7 @@ export const setupIpcHandlers = (webContents: Electron.WebContents, store: Store
return store.getSettings();
});

ipcMain.handle('add-folder', async () => {
ipcMain.handle('select-folder', async () => {
const settings = store.getSettings();

if (!MODEL_PROVIDERS[settings.modelProviderConfig.name].isInitialized(settings)) {
Expand All @@ -61,12 +61,22 @@ export const setupIpcHandlers = (webContents: Electron.WebContents, store: Store
const result = await dialog.showOpenDialog({ properties: ['openDirectory'] });
if (!result.canceled) {
const folderPath = result.filePaths[0];
await scanFolder(store, folderPath, modelManager);
return { success: true, message: 'Folder added successfully' };
return { success: true, path: folderPath };
}
return { success: false };
});

ipcMain.handle('add-folder', async (_event, folderPath: string, includeSubdirectories: boolean) => {
const settings = store.getSettings();

if (!MODEL_PROVIDERS[settings.modelProviderConfig.name].isInitialized(settings)) {
return { success: false, message: 'Model provider not initialized. Go to Settings and enter your the information for the model provider.' };
}

await scanFolder(store, folderPath, includeSubdirectories, modelManager);
return { success: true, message: 'Folder added successfully' };
});

ipcMain.handle('get-images', () => {
return store.getImages();
});
Expand Down
4 changes: 3 additions & 1 deletion src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ const imageUpdatedListeners: Record<string, (event: Electron.IpcRendererEvent, i
const api = {
saveSettings: (settings: Settings): Promise<void> => ipcRenderer.invoke('save-settings', settings),
getSettings: (): Promise<Settings> => ipcRenderer.invoke('get-settings'),
addFolder: (): Promise<{ success: boolean; message: string }> => ipcRenderer.invoke('add-folder'),
selectFolder: (): Promise<{ success: boolean; path?: string }> => ipcRenderer.invoke('select-folder'),
addFolder: (folderPath: string, includeSubdirs: boolean): Promise<{ success: boolean; message?: string }> =>
ipcRenderer.invoke('add-folder', folderPath, includeSubdirs),
getImages: (): Promise<Image[]> => ipcRenderer.invoke('get-images'),
generateImageCaption: (image: Image): Promise<string> => ipcRenderer.invoke('generate-image-caption', image),
addImageUpdatedListener: (callback: (event: Electron.IpcRendererEvent, image: Image) => void): string => {
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const App = () => {

useEffect(() => {
Modal.setAppElement('#app');
navigate('/');
navigate('/home');
}, []);

return (
Expand Down
92 changes: 92 additions & 0 deletions src/renderer/src/components/AddFolderModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { useState } from 'react';
import Modal from 'react-modal';
import { HiFolderAdd } from 'react-icons/hi';

type Props = {
isOpen: boolean;
onRequestClose: () => void;
onSubmit: (folderPath: string, includeSubdirs: boolean) => void;
};

const AddFolderModal = ({ isOpen, onRequestClose, onSubmit }: Props) => {
const [folderPath, setFolderPath] = useState('');
const [includeSubdirectories, setIncludeSubdirectories] = useState(false);

const handleSelectFolder = async () => {
const result = await window.api.selectFolder();
if (result.success && result.path) {
setFolderPath(result.path);
}
};

const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSubmit(folderPath, includeSubdirectories);
setFolderPath('');
setIncludeSubdirectories(false);
};

return (
<Modal
isOpen={isOpen}
onRequestClose={onRequestClose}
className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white rounded-lg p-6 w-96"
overlayClassName="fixed inset-0 bg-black bg-opacity-75"
ariaHideApp={false}
>
<h2 className="text-xl font-bold mb-4">Add Folder</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700">Folder Path</label>
<div className="mt-1 flex rounded-md shadow-sm">
<input
type="text"
value={folderPath}
readOnly
className="flex-1 min-w-0 block w-full px-3 py-2 rounded-l-md border border-gray-300 bg-gray-50"
placeholder="Select a folder..."
/>
<button
type="button"
onClick={handleSelectFolder}
className="inline-flex items-center px-3 py-2 border border-l-0 border-gray-300 rounded-r-md bg-gray-50 hover:bg-gray-100"
>
<HiFolderAdd className="h-5 w-5 text-gray-400" />
</button>
</div>
</div>
<div className="flex items-center">
<input
type="checkbox"
id="includeSubdirs"
checked={includeSubdirectories}
onChange={(e) => setIncludeSubdirectories(e.target.checked)}
className="h-4 w-4 text-teal-600 focus:ring-teal-500 border-gray-300 rounded"
/>
<label htmlFor="includeSubdirs" className="ml-2 block text-sm text-gray-900">
Include subdirectories
</label>
</div>

<div className="flex justify-end space-x-3 pt-4">
<button
type="button"
onClick={onRequestClose}
className="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
>
Cancel
</button>
<button
type="submit"
disabled={!folderPath}
className="px-4 py-2 text-sm font-medium text-white bg-teal-600 border border-transparent rounded-md hover:bg-teal-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
Add
</button>
</div>
</form>
</Modal>
);
};

export default AddFolderModal;
13 changes: 9 additions & 4 deletions src/renderer/src/components/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Image } from '@shared/types';
import ImageDetails from './ImageDetails';
import { useSnackbar } from 'notistack';
import Pager from './Pager';
import AddFolderModal from './AddFolderModal';

const Home = () => {
const { enqueueSnackbar } = useSnackbar();
Expand Down Expand Up @@ -45,10 +46,12 @@ const Home = () => {
// The filtering is now done in the filteredImages variable
};

const handleAddFolder = async (): Promise<void> => {
const result = await window.api.addFolder();
const [isAddFolderModalOpen, setIsAddFolderModalOpen] = useState(false);

const handleAddFolder = async (folderPath: string, includeSubdirectories: boolean): Promise<void> => {
const result = await window.api.addFolder(folderPath, includeSubdirectories);
if (result.success) {
// Refresh the images after adding a new folder
setIsAddFolderModalOpen(false);
const updatedImages = await window.api.getImages();
setImages(updatedImages);
} else if (result.message) {
Expand Down Expand Up @@ -91,7 +94,7 @@ const Home = () => {
</div>
</form>
<button
onClick={handleAddFolder}
onClick={() => setIsAddFolderModalOpen(true)}
className="flex items-center justify-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-teal-600 hover:bg-teal-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
<HiFolderAdd className="mr-2 h-5 w-5" />
Expand Down Expand Up @@ -165,6 +168,8 @@ const Home = () => {
<div className="fixed inset-0 bg-black bg-opacity-75" onClick={() => setSelectedImage(null)} />
{selectedImage && <ImageDetails image={selectedImage} />}
</Modal>

<AddFolderModal isOpen={isAddFolderModalOpen} onRequestClose={() => setIsAddFolderModalOpen(false)} onSubmit={handleAddFolder} />
</div>
);
};
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/src/types/electron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { Image, Settings } from '@shared/types';
export interface ElectronAPI {
saveSettings: (settings: Settings) => Promise<void>;
getSettings: () => Promise<Settings>;
addFolder: () => Promise<{ success: boolean; message?: string }>;
addFolder: (folderPath: string, includeSubdirs: boolean) => Promise<{ success: boolean; message?: string }>;
selectFolder: () => Promise<{ success: boolean; path?: string }>;
getImages: () => Promise<Image[]>;
generateImageCaption: (image: Image) => Promise<string>;
addImageUpdatedListener: (callback: (event: Electron.IpcRendererEvent, image: Image) => void) => string;
Expand Down

0 comments on commit 089da88

Please sign in to comment.