Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 27 additions & 27 deletions SPEC.md

Large diffs are not rendered by default.

13 changes: 0 additions & 13 deletions e2e/db/works.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,19 +299,6 @@ test("a new work: the photo goes through the upload chain as a work, then the fo
expect(body?.subarray(8, 12).toString()).toBe("WEBP");
}

// #153: the motion switch starts ON, because that is what a work saved
// before it existed reads as. Turned off and on again it must leave
// NOTHING behind — the parameters saved below carry no `glide` key at
// all, which is precisely how ON is stored.
const glideOn = page.getByTestId("work-r360-glide-on");
const glideOff = page.getByTestId("work-r360-glide-off");
await expect(glideOn).toHaveAttribute("aria-pressed", "true");
await glideOff.click();
await expect(glideOff).toHaveAttribute("aria-pressed", "true");
await expect(glideOn).toHaveAttribute("aria-pressed", "false");
await glideOn.click();
await expect(glideOn).toHaveAttribute("aria-pressed", "true");

await page.getByLabel("Inwestor").fill("Archicom S.A.");
await page.getByRole("button", { name: "Zapisz realizację" }).click();
await expect.poll(() => created.length).toBe(1);
Expand Down
4 changes: 0 additions & 4 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -571,10 +571,6 @@
"paramStartFrame": "Start frame",
"useThisFrame": "Use this frame",
"paramFlattening": "Ring flattening",
"paramGlide": "Motion",
"glideOn": "Eased",
"glideOff": "Constant",
"glideHint": "Eased: a click on the ring gathers pace and settles onto its frame, and an orbit thrown with the hand keeps turning and slows to a stop. Constant is one pace from beginning to end. A visitor whose system asks for less motion gets neither — the orbit simply arrives.",
"flatteningCircle": "Circle",
"refused": {
"not_a_zip": "This is not a zip archive.",
Expand Down
4 changes: 0 additions & 4 deletions messages/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -571,10 +571,6 @@
"paramStartFrame": "Klatka startowa",
"useThisFrame": "Użyj tej klatki",
"paramFlattening": "Spłaszczenie pierścienia",
"paramGlide": "Ruch",
"glideOn": "Płynny",
"glideOff": "Jednostajny",
"glideHint": "Płynny: kliknięcie w pierścień nabiera tempa i wyhamowuje na klatce, a obrót rzucony ręką kręci się dalej i zwalnia do zatrzymania. Jednostajny to jedno tempo od początku do końca. Gość, którego system prosi o mniej ruchu, nie dostanie żadnego z nich — obrót po prostu staje na miejscu.",
"flatteningCircle": "Okrąg",
"refused": {
"not_a_zip": "To nie jest archiwum zip.",
Expand Down
17 changes: 0 additions & 17 deletions src/app/[locale]/(public)/[handle]/r360-params.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
type R360Cue,
type R360Params,
} from "@/lib/r360/frame-set-shared";
import { glides } from "@/lib/r360/orbit";

// #103: the owner's five parameters of an R360 set, under the preview in
// the work form (the sixth, the frame count, is the set's, shown read-only
Expand Down Expand Up @@ -95,22 +94,6 @@ export function R360ParamControls({
{t("flatteningCircle")}
</Button>
</RangeParam>
{/* #153: what the visitor's orbit does — a travel that eases in and
out of its frame, a drag that coasts on after the hand. Only OFF
is written into the parameters: absent is on, so a work saved
before the switch existed glides like the rest. */}
<TwoWayParam
name={t("paramGlide")}
disabled={disabled}
hint={t("glideHint")}
choices={([true, false] as const).map((on) => ({
key: String(on),
label: t(on ? "glideOn" : "glideOff"),
active: glides(params) === on,
pick: () => onChange({ glide: on ? undefined : false }),
testId: `work-r360-glide-${on ? "on" : "off"}`,
}))}
/>
<CueControls
params={params}
frameInView={frameInView}
Expand Down
19 changes: 6 additions & 13 deletions src/components/ui/use-orbit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
frameAfterDrag,
frameAfterKey,
framesAlong,
glides,
travelDuration,
travelStop,
wrapFrame,
Expand Down Expand Up @@ -66,9 +65,9 @@ export interface Orbit {
/**
* Moves along `path` (the frames on the way, the destination last) over
* a time proportional to its length, within bounds; reduced motion
* jumps. A grab or a key on the way ends it where it is. #153: eased
* in and out of its frame on an orbit that glides, at a constant pace
* on one whose owner turned that off.
* jumps. A grab or a key on the way ends it where it is. One pace from
* beginning to end: #153's ease and #175's amounts for it are both gone
* (Dawid, 12.09.2026).
*/
travelAlong: (path: readonly number[]) => void;
cancelTravel: () => void;
Expand All @@ -93,11 +92,6 @@ export function useOrbit(
} = {},
): Orbit {
const { frameCount, framesPerWidth, direction } = params;
// #153: read once, and as a plain boolean — the parameters arrive as a
// fresh object on some renders (a form with no set yet builds its
// defaults inline), and a callback keyed on the object itself would be
// rebuilt with every one of them.
const glide = glides(params);
const [frame, setFrameState] = useState(() =>
wrapFrame(options.initialFrame ?? params.startFrame, frameCount),
);
Expand Down Expand Up @@ -203,9 +197,9 @@ export function useOrbit(
place(path[path.length - 1]);
return;
}
run(path, travelDuration(path.length), glide ? "eased" : "steady");
run(path, travelDuration(path.length), "steady");
},
[cancelTravel, glide, place, run],
[cancelTravel, place, run],
);

// A frame count that changed under the hook (a new archive in the same
Expand Down Expand Up @@ -278,7 +272,6 @@ export function useOrbit(
lift: { x: event.clientX, t: clockOf(event) },
width: from.width,
lifted: event.type === "pointerup",
glide,
reducedMotion: reducedMotion(),
},
{ framesPerWidth, direction },
Expand All @@ -290,7 +283,7 @@ export function useOrbit(
"slowing",
);
},
[direction, frameCount, framesPerWidth, glide, run],
[direction, frameCount, framesPerWidth, run],
);

const onKeyDown = useCallback(
Expand Down
13 changes: 4 additions & 9 deletions src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,11 @@ export const works = pgTable(
developer: text("developer"),
// #102 (A13): the frame set the owner's browser derived from a zip on
// their own disk — the prefix `u/<user>/r360/<set id>/` the frames sit
// under (32 hex digits minted at presign) — and the six viewer
// under (32 hex digits minted at presign) — and the five viewer
// parameters: frame count, direction, frames per picture width, start
// frame and ring flattening (#68), and whether the orbit glides
// (#153). Both or neither: a set without parameters cannot be shown,
// parameters without a set describe nothing.
// frame and ring flattening (#68). Both or neither: a set without
// parameters cannot be shown, parameters without a set describe
// nothing.
//
// #120: there is no archive column any more. The zip never leaves the
// owner's machine, so the work names its frames and nothing else.
Expand Down Expand Up @@ -488,11 +488,6 @@ export interface R360ParamsRow {
flattening: number;
/** #107: labelled frames; absent on a work saved before them. */
cues?: { frame: number; label: string }[];
/**
* #153: whether the orbit eases and coasts. Absent is ON — the field
* arrived after works were saved — so only `false` is ever written.
*/
glide?: boolean;
}

// #30: a staged upload is bytes that already exist in the bucket but have no
Expand Down
30 changes: 0 additions & 30 deletions src/lib/r360/cues.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,30 +79,6 @@ describe("cues in the parameters", () => {
});
});

// #153: the switch is stored the other way about from the cue points —
// its absence is the feature being ON, so only an owner turning it off
// is ever written into a work.
describe("the glide in the parameters (#153)", () => {
it("reads a work saved before the switch as one that glides", () => {
const parsed = r360ParamsSchema.safeParse(defaultR360Params(4));
expect(parsed.success).toBe(true);
expect(parsed.data?.glide).toBeUndefined();
});

it("takes the owner turning it off, and refuses anything that is not a yes or a no", () => {
const off = r360ParamsSchema.safeParse({
...defaultR360Params(4),
glide: false,
});
expect(off.success).toBe(true);
expect(off.data?.glide).toBe(false);
expect(
r360ParamsSchema.safeParse({ ...defaultR360Params(4), glide: "off" })
.success,
).toBe(false);
});
});

describe("isDefaultR360Params", () => {
it("is true for the defaults of any count, and false once anything of the owner's is in them", () => {
expect(isDefaultR360Params(defaultR360Params(4))).toBe(true);
Expand All @@ -113,7 +89,6 @@ describe("isDefaultR360Params", () => {
{ startFrame: 3 },
{ flattening: 0.3 },
{ cues: [{ frame: 2, label: "Taras" }] },
{ glide: false },
]) {
expect(
isDefaultR360Params({ ...defaultR360Params(4), ...change }),
Expand All @@ -124,11 +99,6 @@ describe("isDefaultR360Params", () => {
expect(isDefaultR360Params({ ...defaultR360Params(4), cues: [] })).toBe(
true,
);
// #153: the glide left ON is the default, written down or not — it is
// nothing of the owner's to carry over to the next zip.
expect(isDefaultR360Params({ ...defaultR360Params(4), glide: true })).toBe(
true,
);
});
});

Expand Down
12 changes: 4 additions & 8 deletions src/lib/r360/frame-set-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,10 +136,10 @@ export type R360Cue = z.infer<typeof cueSchema>;
* are the cue points (#107) — optional, so a work saved before them reads
* as a work with none.
*
* #153's glide is optional the other way about: absent means the orbit
* glides, and only an owner turning it off is written down. A work saved
* before the switch existed therefore reads as one that glides, which is
* the motion it should have had all along.
* #153 added a motion switch here and #175 was to add amounts for it;
* both are gone (Dawid, 12.09.2026). A work saved while the switch existed
* may still carry `glide` — the object drops unknown keys on the way in,
* so it reads as a work without one, which is what it now is.
*/
export const r360ParamsSchema = z
.object({
Expand All @@ -149,7 +149,6 @@ export const r360ParamsSchema = z
startFrame: z.number().int().min(1).max(R360_MAX_FRAMES),
flattening: z.number().min(0.15).max(1),
cues: z.array(cueSchema).max(R360_CUES_MAX).optional(),
glide: z.boolean().optional(),
})
.refine(
(p) => p.framesPerWidth <= p.frameCount && p.startFrame <= p.frameCount,
Expand Down Expand Up @@ -178,8 +177,6 @@ export function defaultR360Params(frameCount: number): R360Params {
/**
* Whether the parameters are the defaults for their count, untouched and
* without cue points — nothing of the owner's in them to keep (#107).
* A glide turned off counts as something of theirs (#153); a glide left
* on is the default, and is not written down at all.
*/
export function isDefaultR360Params(params: R360Params): boolean {
const defaults = defaultR360Params(params.frameCount);
Expand All @@ -188,7 +185,6 @@ export function isDefaultR360Params(params: R360Params): boolean {
params.framesPerWidth === defaults.framesPerWidth &&
params.startFrame === defaults.startFrame &&
params.flattening === defaults.flattening &&
params.glide !== false &&
!params.cues?.length
);
}
Expand Down
34 changes: 1 addition & 33 deletions src/lib/r360/orbit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
frameAfterDrag,
frameAfterKey,
framesAlong,
glides,
loadingOrder,
nearestLoaded,
pageStep,
Expand Down Expand Up @@ -97,15 +96,6 @@ describe("frameAfterKey", () => {
});
});

describe("glides (#153)", () => {
it("is on unless the owner said otherwise: only false turns it off", () => {
expect(glides({})).toBe(true);
expect(glides({ glide: undefined })).toBe(true);
expect(glides({ glide: true })).toBe(true);
expect(glides({ glide: false })).toBe(false);
});
});

describe("framesAlong", () => {
it("lists the frames a turn passes, the destination last and the frame it starts on not among them", () => {
expect(framesAlong(1, 3, 10)).toEqual([2, 3, 4]);
Expand Down Expand Up @@ -253,7 +243,6 @@ describe("coastOnRelease (#153)", () => {
lift: { x: 40, t: 1000 },
width: 200,
lifted: true,
glide: true,
reducedMotion: false,
};
/** The same throw, the hand resting `rest` ms before it lets go. */
Expand Down Expand Up @@ -301,10 +290,6 @@ describe("coastOnRelease (#153)", () => {
expect(coastOnRelease({ ...thrown, lifted: false }, p)).toBeNull();
});

it("does not throw an orbit whose owner turned the glide off", () => {
expect(coastOnRelease({ ...thrown, glide: false }, p)).toBeNull();
});

// Reduced motion wins over the owner: a jump is not a substitute for a
// coast, so there is nothing to fall back to and the orbit stops dead.
it("does not throw for a visitor whose system asks for less motion", () => {
Expand Down Expand Up @@ -353,23 +338,6 @@ describe("travelStop", () => {
expect(travelStop(path, 0, 0)).toEqual({ frame: 1, arrived: true });
});

// #153: the same path and the same time, the frames spread differently
// along it. A ten-frame path over a second, read against the constant
// pace every travel had before.
it("eased lingers at the start and settles onto its frame early", () => {
const ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// A tenth of the way through, an eased travel has not left its first
// frame; a steady one is already on the second.
expect(travelStop(ten, 100, 1000, "eased").frame).toBe(1);
expect(travelStop(ten, 100, 1000).frame).toBe(2);
// Halfway is halfway either way — the curve is symmetric.
expect(travelStop(ten, 500, 1000, "eased").frame).toBe(6);
expect(travelStop(ten, 500, 1000).frame).toBe(6);
// And it is on its last frame with time left to settle there.
expect(travelStop(ten, 850, 1000, "eased").frame).toBe(10);
expect(travelStop(ten, 850, 1000).frame).toBe(9);
});

it("slowing spends its speed early: three quarters of the path in half the time", () => {
const ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
expect(travelStop(ten, 500, 1000, "slowing").frame).toBe(8);
Expand All @@ -384,7 +352,7 @@ describe("travelStop", () => {
// path — the orbit would walk backwards out of its destination.
it("ends on the destination, on time, whatever curve it took", () => {
const ten = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (const curve of ["steady", "eased", "slowing"] as const) {
for (const curve of ["steady", "slowing"] as const) {
expect(travelStop(ten, 1000, 1000, curve), curve).toEqual({
frame: 10,
arrived: true,
Expand Down
Loading
Loading