mirror of
https://github.com/Boof2015/astra-mobile.git
synced 2026-08-19 12:14:47 +02:00
fix softlock further
This commit is contained in:
@@ -47,7 +47,6 @@ import {
|
|||||||
} from '@/components/player/nowPlayingTrackTransition';
|
} from '@/components/player/nowPlayingTrackTransition';
|
||||||
import {
|
import {
|
||||||
resolveNowPlayingPanRelease,
|
resolveNowPlayingPanRelease,
|
||||||
resolveNowPlayingDismissSpring,
|
|
||||||
shouldEnableNowPlayingPan,
|
shouldEnableNowPlayingPan,
|
||||||
shouldStartNowPlayingPan,
|
shouldStartNowPlayingPan,
|
||||||
} from '@/components/player/nowPlayingDismiss';
|
} from '@/components/player/nowPlayingDismiss';
|
||||||
@@ -131,6 +130,8 @@ const MENU_ANIMATION_OUT_MS = 100;
|
|||||||
const MENU_ENTER_OFFSET_Y = -8;
|
const MENU_ENTER_OFFSET_Y = -8;
|
||||||
const NOW_PLAYING_SPECTRUM_SMOOTHING = 0.85;
|
const NOW_PLAYING_SPECTRUM_SMOOTHING = 0.85;
|
||||||
const PAN_DISMISS_HANDOFF_BACKSTOP_MS = 120;
|
const PAN_DISMISS_HANDOFF_BACKSTOP_MS = 120;
|
||||||
|
const PAN_TOUCH_END_BACKSTOP_MS = 80;
|
||||||
|
const PAN_GESTURE_RECOVERY_MS = 1200;
|
||||||
|
|
||||||
interface NowPlayingMenuItem {
|
interface NowPlayingMenuItem {
|
||||||
key: string;
|
key: string;
|
||||||
@@ -304,9 +305,7 @@ export function NowPlayingOverlay() {
|
|||||||
shouldEnableNowPlayingPan(playerOpen, queueOpen, lyricsBodySwitching)
|
shouldEnableNowPlayingPan(playerOpen, queueOpen, lyricsBodySwitching)
|
||||||
);
|
);
|
||||||
const panDismissRequested = useSharedValue(false);
|
const panDismissRequested = useSharedValue(false);
|
||||||
const panExitPending = useSharedValue(false);
|
const panRecoveryLease = useSharedValue(0);
|
||||||
const panReleaseVelocity = useSharedValue(0);
|
|
||||||
const panReleaseRemainingDistance = useSharedValue(windowHeight);
|
|
||||||
const screenHeight = useSharedValue(windowHeight);
|
const screenHeight = useSharedValue(windowHeight);
|
||||||
const companionTouchStartX = useSharedValue(companionStartX);
|
const companionTouchStartX = useSharedValue(companionStartX);
|
||||||
const menuProgress = useSharedValue(0);
|
const menuProgress = useSharedValue(0);
|
||||||
@@ -318,7 +317,7 @@ export function NowPlayingOverlay() {
|
|||||||
const suspendPanForChildTransition = () => {
|
const suspendPanForChildTransition = () => {
|
||||||
panEnabled.value = false;
|
panEnabled.value = false;
|
||||||
panDismissRequested.value = false;
|
panDismissRequested.value = false;
|
||||||
panExitPending.value = false;
|
cancelAnimation(panRecoveryLease);
|
||||||
cancelAnimation(translateY);
|
cancelAnimation(translateY);
|
||||||
translateY.value = 0;
|
translateY.value = 0;
|
||||||
};
|
};
|
||||||
@@ -561,30 +560,9 @@ export function NowPlayingOverlay() {
|
|||||||
// after an effect that depends on the value.
|
// after an effect that depends on the value.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (phase === 'closing') {
|
if (phase === 'closing') {
|
||||||
// The gesture and button paths own the exit animation, including its
|
// Gesture/button paths attach their own exit animation. Only animate here
|
||||||
// velocity-matched spring shaping. Only animate here when `closing`
|
// when `closing` arrived from a direct closePlayer() call.
|
||||||
// arrived from a direct closePlayer() with no animation attached.
|
if (!exitAnimated) {
|
||||||
if (exitAnimated && panExitPending.value) {
|
|
||||||
const releaseSpring = resolveNowPlayingDismissSpring(
|
|
||||||
panReleaseVelocity.value,
|
|
||||||
panReleaseRemainingDistance.value
|
|
||||||
);
|
|
||||||
panExitPending.value = false;
|
|
||||||
cancelAnimation(translateY);
|
|
||||||
translateY.value = withSpring(
|
|
||||||
screenHeight.value,
|
|
||||||
{
|
|
||||||
damping: releaseSpring.damping,
|
|
||||||
stiffness: releaseSpring.stiffness,
|
|
||||||
velocity: releaseSpring.velocity,
|
|
||||||
overshootClamping: true,
|
|
||||||
energyThreshold: 1e-4,
|
|
||||||
},
|
|
||||||
(finished) => {
|
|
||||||
if (finished) runOnJS(commitClosed)();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else if (!exitAnimated) {
|
|
||||||
translateY.value = withTiming(windowHeight, { duration: 200 });
|
translateY.value = withTiming(windowHeight, { duration: 200 });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -597,11 +575,6 @@ export function NowPlayingOverlay() {
|
|||||||
exitAnimated,
|
exitAnimated,
|
||||||
queueOpen,
|
queueOpen,
|
||||||
lyricsMode,
|
lyricsMode,
|
||||||
commitClosed,
|
|
||||||
panExitPending,
|
|
||||||
panReleaseRemainingDistance,
|
|
||||||
panReleaseVelocity,
|
|
||||||
screenHeight,
|
|
||||||
translateY,
|
translateY,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -629,6 +602,22 @@ export function NowPlayingOverlay() {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}, [phase, openRequest]);
|
}, [phase, openRequest]);
|
||||||
|
|
||||||
|
const dismissFromPan = useCallback(() => {
|
||||||
|
// One JS transaction: the store enters `closing` before the deterministic
|
||||||
|
// slide is scheduled. Its 220 ms duration always beats the 450 ms fallback
|
||||||
|
// unmount, so a normal release cannot visibly despawn.
|
||||||
|
beginDismiss();
|
||||||
|
cancelAnimation(panRecoveryLease);
|
||||||
|
cancelAnimation(translateY);
|
||||||
|
translateY.value = withTiming(
|
||||||
|
screenHeight.value,
|
||||||
|
motion.snap,
|
||||||
|
(finished) => {
|
||||||
|
if (finished) runOnJS(commitClosed)();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}, [beginDismiss, commitClosed, panRecoveryLease, screenHeight, translateY]);
|
||||||
|
|
||||||
const pan = useMemo(
|
const pan = useMemo(
|
||||||
() => Gesture.Pan()
|
() => Gesture.Pan()
|
||||||
.activeOffsetY(14) // engage only on a downward drag
|
.activeOffsetY(14) // engage only on a downward drag
|
||||||
@@ -649,10 +638,31 @@ export function NowPlayingOverlay() {
|
|||||||
.onTouchesMove((_event, stateManager) => {
|
.onTouchesMove((_event, stateManager) => {
|
||||||
if (!panEnabled.value) stateManager.fail();
|
if (!panEnabled.value) stateManager.fail();
|
||||||
})
|
})
|
||||||
|
.onTouchesUp(() => {
|
||||||
|
if (panDismissRequested.value) return;
|
||||||
|
translateY.value = withDelay(
|
||||||
|
PAN_TOUCH_END_BACKSTOP_MS,
|
||||||
|
withTiming(0, motion.snap)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.onTouchesCancelled(() => {
|
||||||
|
if (panDismissRequested.value) return;
|
||||||
|
translateY.value = withTiming(0, motion.snap);
|
||||||
|
})
|
||||||
.onStart(() => {
|
.onStart(() => {
|
||||||
panDismissRequested.value = false;
|
panDismissRequested.value = false;
|
||||||
panExitPending.value = false;
|
|
||||||
cancelAnimation(translateY);
|
cancelAnimation(translateY);
|
||||||
|
cancelAnimation(panRecoveryLease);
|
||||||
|
panRecoveryLease.value = withDelay(
|
||||||
|
PAN_GESTURE_RECOVERY_MS,
|
||||||
|
withTiming(panRecoveryLease.value + 1, { duration: 0 }, (finished) => {
|
||||||
|
if (!finished || panDismissRequested.value) return;
|
||||||
|
// This lease is independent of RNGH's terminal callbacks. If a
|
||||||
|
// nested native gesture drops the handler, the partial drag still
|
||||||
|
// repairs itself on the UI thread.
|
||||||
|
translateY.value = withTiming(0, motion.snap);
|
||||||
|
})
|
||||||
|
);
|
||||||
})
|
})
|
||||||
.onUpdate((event) => {
|
.onUpdate((event) => {
|
||||||
if (!panEnabled.value) {
|
if (!panEnabled.value) {
|
||||||
@@ -669,40 +679,35 @@ export function NowPlayingOverlay() {
|
|||||||
);
|
);
|
||||||
if (release === 'dismiss') {
|
if (release === 'dismiss') {
|
||||||
panDismissRequested.value = true;
|
panDismissRequested.value = true;
|
||||||
panExitPending.value = true;
|
cancelAnimation(panRecoveryLease);
|
||||||
panReleaseVelocity.value = event.velocityY;
|
|
||||||
panReleaseRemainingDistance.value =
|
|
||||||
screenHeight.value - translateY.value;
|
|
||||||
// If the RN handoff is ever dropped, restore the partial drag instead
|
// If the RN handoff is ever dropped, restore the partial drag instead
|
||||||
// of leaving an open player stranded. The phase effect cancels this
|
// of leaving an open player stranded. dismissFromPan cancels this
|
||||||
// delayed animation only after Zustand has entered `closing`.
|
// delayed animation after synchronously entering `closing`.
|
||||||
translateY.value = withDelay(
|
translateY.value = withDelay(
|
||||||
PAN_DISMISS_HANDOFF_BACKSTOP_MS,
|
PAN_DISMISS_HANDOFF_BACKSTOP_MS,
|
||||||
withTiming(0, motion.snap)
|
withTiming(0, motion.snap)
|
||||||
);
|
);
|
||||||
runOnJS(beginDismiss)();
|
runOnJS(dismissFromPan)();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
panDismissRequested.value = false;
|
panDismissRequested.value = false;
|
||||||
panExitPending.value = false;
|
cancelAnimation(panRecoveryLease);
|
||||||
translateY.value = withTiming(0, motion.snap);
|
translateY.value = withTiming(0, motion.snap);
|
||||||
})
|
})
|
||||||
.onFinalize(() => {
|
.onFinalize(() => {
|
||||||
// Successful dismissals retain the short handoff backstop above. Every
|
// Successful dismissals retain the short handoff backstop above. Every
|
||||||
// other terminal path, including cancellation, re-anchors immediately.
|
// other terminal path, including cancellation, re-anchors immediately.
|
||||||
if (!panDismissRequested.value) {
|
if (!panDismissRequested.value) {
|
||||||
|
cancelAnimation(panRecoveryLease);
|
||||||
translateY.value = withTiming(0, motion.snap);
|
translateY.value = withTiming(0, motion.snap);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
beginDismiss,
|
|
||||||
companionTouchStartX,
|
companionTouchStartX,
|
||||||
|
dismissFromPan,
|
||||||
panDismissRequested,
|
panDismissRequested,
|
||||||
panEnabled,
|
panEnabled,
|
||||||
panExitPending,
|
panRecoveryLease,
|
||||||
panReleaseRemainingDistance,
|
|
||||||
panReleaseVelocity,
|
|
||||||
screenHeight,
|
|
||||||
translateY,
|
translateY,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user