library: stand a frame up in the catalogue, wall, strip and studio

The library stage carried no way to correct a frame shot on its side, and
nothing kept the correction. A quarter turn now rides on the catalogue row
(`rot`, beside `star`, carried over by a rescan), the two chips under the
preview step it, and the tile, the strip thumbnail and the stage all read it
through one bake so a frame is never stood on its side in one place and
upright in another. The studio opens a frame at that angle and a turn made
there is filed straight back onto the row, so either end of the turn is
what the frame comes up at next time.
This commit is contained in:
2026-10-06 17:22:56 +07:00
parent 162024b9b3
commit e2e0297f31
5 changed files with 159 additions and 4 deletions
+15 -1
View File
@@ -18,7 +18,7 @@ import { renderPhoto, type RenderAssets } from './engine/exportEngine';
import { download, fileToBytes, readCapturedAt, readGps, readIso, resizedJpeg, scaledJpeg } from './engine/imageOps';
import { developRaw, isRawName } from './engine/rawDevelop';
import { clearRaw, readRaw, storeRaw } from './engine/rawStore';
import { getPhoto, loadEdit, readPhotoFile, saveEdit } from './engine/library';
import { getPhoto, loadEdit, readPhotoFile, saveEdit, setRotation as savePhotoRotation } from './engine/library';
import { offerInstall } from './pwa/install';
import { cameraSupported } from './engine/camera';
import { clearSession, forgetPhoto, loadPhoto, loadState, savePhoto, saveState } from './engine/session';
@@ -2051,6 +2051,11 @@ export function Workspace() {
await loadFile(await readPhotoFile(row));
const saved = await loadEdit(id);
if (saved) applyRecipe(saved.recipe);
// The catalogue's own standing for this frame, which the reader set on
// the library's stage: the studio opens the negative the way they last
// saw it, at the same angle. It arrives after `loadFile`, which puts the
// frame on the neutral look and so at 0.
setRotation(row.rot ?? 0);
setLibPhotoId(id);
} catch {
setError(t('err.decode'));
@@ -2070,6 +2075,15 @@ export function Workspace() {
return () => clearTimeout(timer);
}, [libPhotoId, source, recipe]);
// The same filing for the frame's own standing: the catalogue opened this
// frame at an angle, and a turn made here is that number's to keep — the
// library then shows the frame the way it was left, without a second turn.
// The ROTATE chip steps a quarter at a time, so there is nothing to debounce.
useEffect(() => {
if (!libPhotoId || !source) return;
void savePhotoRotation(libPhotoId, rotation);
}, [libPhotoId, source, rotation]);
// The saved frame's own switch. The reel reads `consent`, so the tick answers
// the click at once; only the store's refusal (a failed call) takes it back.
const togglePhotoConsent = useCallback(async (photo: Photo | undefined) => {