Feature: Batch Editing
Relevant source files
The following files were used as context for generating this wiki page:The Batch Editing feature provides users with the ability to modify the metadata for multiple photos simultaneously. This enhances workflow efficiency by allowing bulk changes to properties such as date, location, and tags. The feature is accessible from the main photo library view after a user enters "Selection Mode" and selects one or more photos.
The implementation consists of a primary dialog component (BatchEditDialog) that is triggered from the Library page. This dialog presents a form with the editable fields. Upon saving, it interfaces with the application's IndexedDB service (db.ts) to persist the changes across all selected photo records. The system provides user feedback through toast notifications and UI sounds.
Architecture and Data Flow
The batch editing process involves several components working together, from UI interaction in the Library page to data persistence in the PhotoDatabase.
Sources: src/pages/Library.tsx, src/components/BatchEditDialog.tsx, src/lib/db.ts
Component Interaction Flow
The following sequence diagram illustrates the interaction between the user, the Library page, the BatchEditDialog, and the PhotoDatabase when performing a batch edit.
Core Components
Library Page (Library.tsx)
The Library.tsx page is the entry point for the batch editing feature. It manages the application state required to initiate a batch edit session.
- State Management: It maintains
selectionMode(a boolean to toggle selection UI) andselectedIds(an array of strings containing the unique IDs of the selected photos). - UI Trigger: When
selectionModeis active, it displays a header with an "Edit Metadata" button. Clicking this button, provided at least one photo is selected, sets thebatchEditOpenstate totrue, which renders theBatchEditDialog. - Callback Handling: It passes an
onCompletehandler to the dialog. This handler,handleBatchEditComplete, is responsible for resetting the selection state (selectedIdsandselectionMode) after a successful batch edit.
// src/pages/Library.tsx:42-52
<Button
size="sm"
onClick={handleBatchEdit}
disabled={selectedIds.length === 0}
className="bg-app-accent hover:bg-app-accent-hover"
>
<Edit className="w-4 h-4 mr-2" />
Edit Metadata
</Button>
// src/pages/Library.tsx:83-88
<BatchEditDialog
open={batchEditOpen}
onOpenChange={setBatchEditOpen}
selectedPhotoIds={selectedIds}
onComplete={handleBatchEditComplete}
/>
Sources: src/pages/Library.tsx:12-14, 29-35, 83-88
Batch Edit Dialog (BatchEditDialog.tsx)
This is the central UI component for the feature. It is a modal dialog built using shadcn/ui components.
Properties
| Prop | Type | Description |
|---|---|---|
open | boolean | Controls the visibility of the dialog. |
onOpenChange | (open: boolean) => void | Callback function to handle changes to the dialog's open state. |
selectedPhotoIds | string[] | An array of IDs for the photos to be edited. |
onComplete | () => void | Callback function executed after a successful save operation. |
Sources: src/components/BatchEditDialog.tsx:16-21
Internal State and Logic
The dialog manages the state for the form inputs and the saving process.
- Form State:
date,location, andtagsstates hold the user's input. - Saving State: A
savingboolean state is used to disable buttons and show a "Saving..." message during the update process.
The handleSave function contains the core logic for the update. The flow is as follows:
Database Interaction
All batch edit changes are persisted in the pixona_photos_db IndexedDB database, which is managed by the PhotoDatabase class.
Sources: src/lib/db.ts
Photo Metadata Schema
The photos object store holds records conforming to the PhotoMetadata interface. The batch edit dialog can update a subset of these fields.
The following ER diagram shows the structure of a photo record.
Sources: src/lib/db.ts:12-38
updatePhotos Method
The BatchEditDialog calls the db.updatePhotos(photoIds, updates) method to perform the bulk update.
This method iterates through the provided photoIds, retrieves each corresponding photo object, merges it with the updates object, and writes the result back to the photos object store within a single IndexedDB transaction.
// src/lib/db.ts:205-226
async updatePhotos(photoIds: string[], updates: Partial<PhotoMetadata>): Promise<void> {
if (!this.db) throw new Error('Database not initialized');
const transaction = this.db.transaction([STORES.photos], 'readwrite');
const store = transaction.objectStore(STORES.photos);
for (const id of photoIds) {
const request = store.get(id);
request.onsuccess = () => {
const photo = request.result;
if (photo) {
const updatedPhoto = { ...photo, ...updates };
store.put(updatedPhoto);
}
};
}
return new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
});
}
Sources: src/lib/db.ts:205-226
Data Transformation
Before calling updatePhotos, the handleSave function transforms the raw string inputs from the form into the structure required by the PhotoMetadata interface.
| Form Field | UI Component | Transformation Logic | Target Field |
|---|---|---|---|
| Date | Calendar | The selected Date object is directly assigned. | created |
| Location | Input | The string is used as the name property of a new location object. lat and lng are hardcoded to 0 as a geocoding service is not yet implemented. | location |
| Tags | Input | The comma-separated string is split, with each part trimmed of whitespace and filtered for empty strings, to create an array of strings. | tags |
Sources: src/components/BatchEditDialog.tsx:43-58
Summary
The Batch Editing feature is a well-encapsulated module that significantly improves the user experience for managing photo metadata. It is initiated from the Library view, uses a dedicated BatchEditDialog component for user input, and relies on a robust PhotoDatabase service for data persistence. The data flow is unidirectional and event-driven, with clear separation of concerns between UI presentation, state management, and data access.