feat: Add open folder functionality for photos in file manager
This commit introduces a new feature that allows users to open the folder containing a selected photo in their system's file manager. The API has been updated with a new endpoint to handle folder opening requests, supporting various operating systems. The frontend has been enhanced to include a button for this action, providing user feedback and error handling. Documentation and tests have been updated to reflect these changes, ensuring reliability and usability.
This commit is contained in:
@@ -88,6 +88,13 @@ export const photosApi = {
|
||||
})
|
||||
return data
|
||||
},
|
||||
|
||||
openFolder: async (photoId: number): Promise<{ message: string; folder: string }> => {
|
||||
const { data } = await apiClient.post<{ message: string; folder: string }>(
|
||||
`/api/v1/photos/${photoId}/open-folder`
|
||||
)
|
||||
return data
|
||||
},
|
||||
}
|
||||
|
||||
export interface PhotoSearchResult {
|
||||
|
||||
@@ -63,12 +63,12 @@ export default function Search() {
|
||||
loadTags()
|
||||
}, [])
|
||||
|
||||
const performSearch = async (pageNum: number = page) => {
|
||||
const performSearch = async (pageNum: number = page, folderPathOverride?: string) => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const params: any = {
|
||||
search_type: searchType,
|
||||
folder_path: folderPath || undefined,
|
||||
folder_path: folderPathOverride || folderPath || undefined,
|
||||
page: pageNum,
|
||||
page_size: pageSize,
|
||||
}
|
||||
@@ -105,7 +105,8 @@ export default function Search() {
|
||||
// Auto-run search for no_faces and no_tags
|
||||
if (searchType === 'no_faces' || searchType === 'no_tags') {
|
||||
if (res.items.length === 0) {
|
||||
const folderMsg = folderPath ? ` in folder '${folderPath}'` : ''
|
||||
const actualFolderPath = folderPathOverride || folderPath
|
||||
const folderMsg = actualFolderPath ? ` in folder '${actualFolderPath}'` : ''
|
||||
alert(`No photos found${folderMsg}.`)
|
||||
}
|
||||
}
|
||||
@@ -232,10 +233,17 @@ export default function Search() {
|
||||
window.open(photoUrl, '_blank')
|
||||
}
|
||||
|
||||
const openFolder = (path: string) => {
|
||||
// Extract folder path from file path
|
||||
const folder = path.substring(0, path.lastIndexOf('/'))
|
||||
alert(`Open folder: ${folder}\n\nNote: Folder opening not implemented in web version.`)
|
||||
const openFolder = async (photoId: number) => {
|
||||
try {
|
||||
const result = await photosApi.openFolder(photoId)
|
||||
// Success - folder opened in file manager
|
||||
// Optionally show a brief message
|
||||
console.log('Folder opened:', result.folder)
|
||||
} catch (error: any) {
|
||||
console.error('Error opening folder:', error)
|
||||
const errorMessage = error?.response?.data?.detail || error?.message || 'Failed to open folder'
|
||||
alert(`Error opening folder: ${errorMessage}`)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -272,7 +280,14 @@ export default function Search() {
|
||||
{filtersExpanded && (
|
||||
<div className="mt-4 space-y-3">
|
||||
<div>
|
||||
<label className="block text-sm text-gray-700 mb-1">Folder location:</label>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||||
Folder location:
|
||||
{folderPath && (
|
||||
<span className="ml-2 text-xs text-blue-600 font-normal">
|
||||
(filtering by: {folderPath})
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
@@ -502,9 +517,13 @@ export default function Search() {
|
||||
)}
|
||||
<td className="p-2">
|
||||
<button
|
||||
onClick={() => openFolder(photo.path)}
|
||||
className="cursor-pointer hover:text-blue-600"
|
||||
title="Open file location"
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
openFolder(photo.id)
|
||||
}}
|
||||
className="cursor-pointer hover:text-blue-600 text-lg"
|
||||
title="Open folder in file manager"
|
||||
>
|
||||
📁
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user