Docker Build and Publish / build-and-push (push) Failing after 10s
Navigation gestures Horizontal swipe used to advance the carousel and then, on the last slide, fling you into the next post — one gesture meaning two things. Horizontal is now carousel-only. On touch, vertical swipe moves between posts (down on the first post still dismisses, keeping drag-to-close where it can't mean "previous"). Desktop keeps the arrows outside the modal. URLs Permalinks now mirror Instagram: /<archive>/ profile /<archive>/reels/ tab /<archive>/p/<shortcode>/ post A post URL carries no tab, as on Instagram; the tab is re-derived from the post's source, so opening a reel link lands on the Reels tab with next/prev paging through reels. Sidecar posts keep directory-scoped ids internally but expose only the shortcode. The old ?a=&t=&p= form is still parsed so existing links keep working, and reserved prefixes (api, archives, assets…) can never be mistaken for a profile name. Animations Adds a shared motion vocabulary tuned to feel native: critically damped springs rather than fixed-duration easing, and gestures hand their exit velocity to the animation so a flick continues instead of restarting. Post transitions animate along the axis the input implies — vertical for a swipe, horizontal for the arrows. Modal and story viewer present/dismiss with a scale, tiles and highlight circles get touch-down feedback, and prefers-reduced-motion is honoured throughout. Adds 22 routing tests (58 total). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uBWhwV3wFQ5MBCcMHHem7
45 lines
1.7 KiB
TypeScript
45 lines
1.7 KiB
TypeScript
import type { Transition } from 'motion/react';
|
|
|
|
/**
|
|
* Shared motion vocabulary, tuned to feel like a native iOS app.
|
|
*
|
|
* Two rules do most of the work:
|
|
* - UIKit animates with springs, not fixed-duration easing, so gestures hand
|
|
* their exit velocity to the animation and motion continues rather than
|
|
* restarting.
|
|
* - iOS springs are critically damped. They settle firmly with no visible
|
|
* bounce; overshoot reads as "web animation", not "native".
|
|
*/
|
|
|
|
/** The curve UIKit uses for sheet presentation. */
|
|
export const IOS_EASE = [0.32, 0.72, 0, 1] as const;
|
|
|
|
/** Moving between peers: carousel slides, next/previous post. */
|
|
export const NAVIGATE: Transition = { type: 'spring', stiffness: 420, damping: 40, mass: 1 };
|
|
|
|
/** Presenting or dismissing a surface. Slightly softer than navigation. */
|
|
export const PRESENT: Transition = { type: 'spring', stiffness: 320, damping: 34, mass: 1 };
|
|
|
|
/** Backdrops and cross-fades, where a spring would feel fussy. */
|
|
export const FADE: Transition = { duration: 0.28, ease: IOS_EASE };
|
|
|
|
/** Touch-down feedback. Fast enough to feel like a direct response. */
|
|
export const PRESS: Transition = { type: 'spring', stiffness: 600, damping: 30 };
|
|
|
|
/**
|
|
* Continue a drag into its animation.
|
|
*
|
|
* Handing the gesture's exit velocity to the spring is what separates "the
|
|
* sheet kept moving because I flicked it" from "the sheet started a new
|
|
* animation once I let go".
|
|
*/
|
|
export const withVelocity = (velocity: number, base: Transition = NAVIGATE): Transition => ({
|
|
...base,
|
|
velocity,
|
|
});
|
|
|
|
/** True when the viewer has asked the OS to reduce motion. */
|
|
export const prefersReducedMotion = () =>
|
|
typeof window !== 'undefined' &&
|
|
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|