- swipe sideways to browse (track follows the finger, FLIP settle), flick up/down to vote Tinder-style with ghost fly-off and the next photo zooming in behind; votes and navigation commit synchronously on release — animations are pure decoration, so closing or keypresses mid-animation can't drop or duplicate an action - two fingers pinch-zoom the photo (springs back, never votes) - voting the last photo fades the modal out over the gallery - gesture engine extracted to useSwipe: per-move motion via direct DOM writes (no React render per touchmove), named threshold constants - touch devices: selection checkmarks always visible, nav arrows yield to swiping, finger-sized vote/rating targets, safe-area padding
This commit is contained in:
@@ -43,6 +43,12 @@ export default function SharePage() {
|
||||
const visibleSelected = visible.filter((p) => selected.has(p.id))
|
||||
const sumBytes = (list) => list.reduce((sum, p) => sum + p.size_bytes, 0)
|
||||
const lightbox = useLightbox(visible)
|
||||
// Voting the last photo ends the run: fade the modal out over the gallery
|
||||
// instead of cutting hard.
|
||||
const [lightboxFading, setLightboxFading] = useState(false)
|
||||
useEffect(() => {
|
||||
if (lightbox.index < 0) setLightboxFading(false)
|
||||
}, [lightbox.index])
|
||||
|
||||
const load = useCallback(
|
||||
() => api(`/api/share/${token}`).then(setView).catch((e) => setError(e.message)),
|
||||
@@ -114,6 +120,8 @@ export default function SharePage() {
|
||||
: `↺ ${photo.filename} cleared`,
|
||||
)
|
||||
if (next) lightbox.show(next.id)
|
||||
// Last photo voted: the run is done — fade back to the gallery.
|
||||
else setLightboxFading(true)
|
||||
}
|
||||
|
||||
const keyActions = [
|
||||
@@ -171,8 +179,8 @@ export default function SharePage() {
|
||||
<h1>{view.album_name}</h1>
|
||||
{view.album_description && <p className="muted">{view.album_description}</p>}
|
||||
<p className="muted">
|
||||
{photos.length} photo{photos.length === 1 ? '' : 's'} · click a photo to view, rate and
|
||||
tag · press <kbd>?</kbd> in the viewer for shortcuts
|
||||
{photos.length} photo{photos.length === 1 ? '' : 's'} · tap a photo to view, rate and
|
||||
tag · swipe ↑ to accept, ↓ to reject · <kbd>?</kbd> shows keyboard shortcuts
|
||||
</p>
|
||||
{photos.length > 0 && (
|
||||
<div className="filter-bar">
|
||||
@@ -242,6 +250,15 @@ export default function SharePage() {
|
||||
onClose={lightbox.close}
|
||||
onNav={lightbox.openAt}
|
||||
actions={keyActions}
|
||||
closing={lightboxFading}
|
||||
onClosed={() => {
|
||||
setLightboxFading(false)
|
||||
lightbox.close()
|
||||
}}
|
||||
gestures={{
|
||||
up: (p) => voteAndAdvance(p, 'accept'),
|
||||
down: (p) => voteAndAdvance(p, 'reject'),
|
||||
}}
|
||||
footer={(p) => (
|
||||
<div className="client-footer">
|
||||
<Thumbs value={p.my_verdict} onChange={(v) => setVerdict(p, v)} />
|
||||
|
||||
Reference in New Issue
Block a user