6bef90b7fb
Albums (manual collections): - New albums/album_images tables with FK cascades; DB functions and Tauri commands for create/rename/delete/delete-many/reorder/list, add/remove images, and paginated get_album_images. - Distinct sidebar "ALBUMS" section with cover thumbnails, create/rename/ delete, and a Manage multi-select mode for bulk album deletion. - Album view reuses the gallery grid (activeView "album" + selectedAlbumId); spans folders; add from the bulk bar or the lightbox, remove from within. Gallery multi-select + bulk actions: - gallerySelectedIds selection model with a top-left corner checkbox that reveals on corner hover; click-to-toggle in selection mode, double-click to open. - Floating BulkActionBar: tag (inline autocomplete popover), rating, favorite, add-to-album, and a delete with an explicit "from disk" confirmation. Batch commands bulk_update_details/bulk_add_tags/ bulk_remove_tag. Also: - Duplicate Finder delete now requires confirmation with clear "from disk" wording (was single-click fire-and-forget). - CPU/CUDA build-variant badge in Settings (get_build_variant). - Rating/favorite no longer re-sorts derived collections (similar/region/ semantic/tag/album results); single and bulk paths replace in place there. - Album-aware indexed-images/media-updated handlers so thumbnails paint and newly-indexed files don't leak into an album view. - CHANGELOG updated.
113 lines
4.3 KiB
TypeScript
113 lines
4.3 KiB
TypeScript
import { useEffect } from "react";
|
|
import { useGalleryStore } from "./store";
|
|
import { Sidebar } from "./components/Sidebar";
|
|
import { BackgroundTasks } from "./components/BackgroundTasks";
|
|
import { Toolbar } from "./components/Toolbar";
|
|
import { Gallery } from "./components/Gallery";
|
|
import { Lightbox } from "./components/Lightbox";
|
|
import { TagCloud } from "./components/TagCloud";
|
|
import { DuplicateFinder } from "./components/DuplicateFinder";
|
|
import { Timeline } from "./components/Timeline";
|
|
import { TitleBar } from "./components/TitleBar";
|
|
import { SettingsModal } from "./components/SettingsModal";
|
|
import { FolderPickerModal } from "./components/FolderPickerModal";
|
|
import { UpdateToast } from "./components/UpdateToast";
|
|
import { WhatsNewToast } from "./components/WhatsNewToast";
|
|
import { WhatsNewModal } from "./components/WhatsNewModal";
|
|
import { OnboardingOverlay } from "./components/onboarding/OnboardingOverlay";
|
|
import { DemoPanel } from "./components/DemoPanel";
|
|
import { initializeNotifications } from "./notifications";
|
|
|
|
export default function App() {
|
|
const loadFolders = useGalleryStore((state) => state.loadFolders);
|
|
const loadBackgroundJobProgress = useGalleryStore((state) => state.loadBackgroundJobProgress);
|
|
const loadImages = useGalleryStore((state) => state.loadImages);
|
|
const loadCaptionModelStatus = useGalleryStore((state) => state.loadCaptionModelStatus);
|
|
const loadDuplicateScanCache = useGalleryStore((state) => state.loadDuplicateScanCache);
|
|
const loadAlbums = useGalleryStore((state) => state.loadAlbums);
|
|
const loadMutedFolderIds = useGalleryStore((state) => state.loadMutedFolderIds);
|
|
const loadNotificationsPaused = useGalleryStore((state) => state.loadNotificationsPaused);
|
|
const subscribeToProgress = useGalleryStore((state) => state.subscribeToProgress);
|
|
const loadAppVersion = useGalleryStore((state) => state.loadAppVersion);
|
|
const checkForUpdates = useGalleryStore((state) => state.checkForUpdates);
|
|
const loadFfmpegStatus = useGalleryStore((state) => state.loadFfmpegStatus);
|
|
const loadOnboardingCompleted = useGalleryStore((state) => state.loadOnboardingCompleted);
|
|
const initWhatsNew = useGalleryStore((state) => state.initWhatsNew);
|
|
const activeView = useGalleryStore((state) => state.activeView);
|
|
|
|
useEffect(() => {
|
|
void initializeNotifications();
|
|
void loadMutedFolderIds();
|
|
void loadNotificationsPaused();
|
|
void loadFfmpegStatus();
|
|
void loadOnboardingCompleted();
|
|
// Load the app version first so the What's New toast/modal (which read
|
|
// appVersion from the store) have it before the greeting can appear.
|
|
void loadAppVersion().then(() => initWhatsNew());
|
|
// Quiet launch check — dev builds have no signed artifacts to update to.
|
|
if (import.meta.env.PROD) {
|
|
void checkForUpdates({ quiet: true });
|
|
}
|
|
loadFolders().then(() => {
|
|
void loadBackgroundJobProgress();
|
|
void loadCaptionModelStatus();
|
|
void loadDuplicateScanCache();
|
|
void loadAlbums();
|
|
return loadImages(true);
|
|
});
|
|
let unlisten: (() => void) | undefined;
|
|
subscribeToProgress().then((fn) => {
|
|
unlisten = fn;
|
|
});
|
|
return () => {
|
|
unlisten?.();
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div className="flex h-screen flex-col bg-gray-950 text-white overflow-hidden select-none">
|
|
{/* Custom title bar — sits at the very top */}
|
|
<TitleBar />
|
|
|
|
{/* Main app content below the title bar */}
|
|
<div className="flex flex-1 min-h-0">
|
|
<Sidebar />
|
|
<main className="flex-1 flex flex-col min-w-0">
|
|
{activeView === "timeline" ? (
|
|
<>
|
|
<Toolbar />
|
|
<BackgroundTasks />
|
|
<Timeline />
|
|
</>
|
|
) : activeView === "explore" ? (
|
|
<>
|
|
<BackgroundTasks />
|
|
<TagCloud />
|
|
</>
|
|
) : activeView === "duplicates" ? (
|
|
<>
|
|
<BackgroundTasks />
|
|
<DuplicateFinder />
|
|
</>
|
|
) : (
|
|
<>
|
|
<Toolbar />
|
|
<BackgroundTasks />
|
|
<Gallery />
|
|
</>
|
|
)}
|
|
</main>
|
|
</div>
|
|
|
|
<Lightbox />
|
|
<SettingsModal />
|
|
<FolderPickerModal />
|
|
<UpdateToast />
|
|
<WhatsNewToast />
|
|
<WhatsNewModal />
|
|
<OnboardingOverlay />
|
|
{import.meta.env.DEV && <DemoPanel />}
|
|
</div>
|
|
);
|
|
}
|