Implement Swipe navigation for floating screen (#16390)
This commit is contained in:
parent
6b0a51720e
commit
b5a96a254a
|
@ -1,4 +1,4 @@
|
|||
import { useDerivedValue, withTiming, withSequence, withDelay, Easing } from 'react-native-reanimated';
|
||||
import { useDerivedValue, withTiming, withSequence, withDelay, Easing, runOnJS } from 'react-native-reanimated';
|
||||
import * as constants from './constants';
|
||||
|
||||
// Derived Values
|
||||
|
@ -106,3 +106,43 @@ export function screenBorderRadius(screenState) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function screenGestureOnUpdate(screenLeft) {
|
||||
return function (event) {
|
||||
'worklet';
|
||||
const absoluteX = event.absoluteX;
|
||||
if (absoluteX !== null) {
|
||||
screenLeft.value = event.absoluteX;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function screenGestureOnEnd(data) {
|
||||
return function (event) {
|
||||
'worklet';
|
||||
|
||||
const { screenLeft, screenState, screenWidth, leftVelocity, rightVelocity, screenClosedCallback } = data;
|
||||
const absoluteX = event.absoluteX ?? 0;
|
||||
const velocityX = event.velocityX ?? 0;
|
||||
const closeScreen = velocityX > rightVelocity || (velocityX > leftVelocity && absoluteX >= screenWidth / 2);
|
||||
|
||||
// Velocity (points/sec) = Distance/time
|
||||
var animationVelocity = (screenWidth * 1000) / constants.SHELL_ANIMATION_TIME;
|
||||
|
||||
if (Math.abs(velocityX) > animationVelocity) {
|
||||
animationVelocity = velocityX; // Faster fling
|
||||
}
|
||||
|
||||
const newDistance = closeScreen ? screenWidth - absoluteX : absoluteX;
|
||||
const animationTime = (newDistance * 1000) / animationVelocity;
|
||||
|
||||
screenLeft.value = withTiming(closeScreen ? screenWidth : 0, {
|
||||
duration: animationTime,
|
||||
easing: Easing.bezier(0, 0, 0.58, 1),
|
||||
});
|
||||
|
||||
if (closeScreen) {
|
||||
runOnJS(screenClosedCallback)(animationTime);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -37,6 +37,10 @@
|
|||
|
||||
(defn min-distance [gesture dist] (.minDistance ^js gesture dist))
|
||||
|
||||
(defn fail-offset-x [gesture offset] (.failOffsetX ^js gesture offset))
|
||||
|
||||
(defn hit-slop [gesture settings] (.hitSlop ^js gesture settings))
|
||||
|
||||
(defn number-of-taps [gesture count] (.numberOfTaps ^js gesture count))
|
||||
|
||||
(defn enabled [gesture enabled?] (.enabled ^js gesture enabled?))
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
(ns status-im2.contexts.shell.jump-to.components.floating-screens.view
|
||||
(:require [utils.re-frame :as rf]
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im2.contexts.chat.messages.view :as chat]
|
||||
[status-im2.contexts.shell.jump-to.state :as state]
|
||||
[status-im2.contexts.shell.jump-to.utils :as utils]
|
||||
[status-im2.contexts.chat.messages.view :as chat]
|
||||
[status-im2.contexts.shell.jump-to.animation :as animation]
|
||||
[status-im2.contexts.shell.jump-to.gesture :as shell.gesture]
|
||||
[status-im2.contexts.shell.jump-to.constants :as shell.constants]
|
||||
[status-im2.contexts.shell.jump-to.components.floating-screens.style :as style]
|
||||
[status-im2.contexts.communities.overview.view :as communities.overview]))
|
||||
[status-im2.contexts.communities.overview.view :as communities.overview]
|
||||
[status-im2.contexts.shell.jump-to.components.floating-screens.style :as style]))
|
||||
|
||||
(def screens-map
|
||||
{shell.constants/community-screen communities.overview/overview
|
||||
|
@ -23,10 +25,12 @@
|
|||
[animation id])
|
||||
[reanimated/view
|
||||
{:style (style/screen (get @state/shared-values-atom screen-id))}
|
||||
[rn/view
|
||||
{:style (style/screen-container (utils/dimensions))
|
||||
:key id}
|
||||
[(get screens-map screen-id) id]]])
|
||||
[gesture/gesture-detector
|
||||
{:gesture (shell.gesture/floating-screen-gesture screen-id)}
|
||||
[rn/view
|
||||
{:style (style/screen-container (utils/dimensions))
|
||||
:key id}
|
||||
[(get screens-map screen-id) id]]]])
|
||||
|
||||
;; Currently chat screen and events both depends on current-chat-id, once we remove
|
||||
;; use of current-chat-id from view then we can keep last chat loaded, for fast navigation
|
||||
|
|
|
@ -61,3 +61,8 @@
|
|||
(def ^:const open-screen-with-shell-animation 3)
|
||||
(def ^:const close-screen-without-animation 4)
|
||||
(def ^:const open-screen-without-animation 5)
|
||||
|
||||
;; Floating Screen gesture
|
||||
(def ^:const gesture-width 20)
|
||||
(def ^:const gesture-fling-right-velocity 2000)
|
||||
(def ^:const gesture-fling-left-velocity -1000)
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
(ns status-im2.contexts.shell.jump-to.gesture
|
||||
(:require [utils.re-frame :as rf]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[utils.worklets.shell :as worklets.shell]
|
||||
[status-im2.contexts.shell.jump-to.utils :as utils]
|
||||
[status-im2.contexts.shell.jump-to.state :as state]
|
||||
[status-im2.contexts.shell.jump-to.constants :as constants]))
|
||||
|
||||
(defn screen-closed-callback
|
||||
[screen-id]
|
||||
(fn [animation-time]
|
||||
(js/setTimeout
|
||||
(fn []
|
||||
(reanimated/set-shared-value
|
||||
(get-in @state/shared-values-atom [screen-id :screen-state])
|
||||
constants/close-screen-without-animation)
|
||||
(reset! state/floating-screens-state
|
||||
(assoc @state/floating-screens-state
|
||||
screen-id
|
||||
constants/close-screen-without-animation))
|
||||
(rf/dispatch [:shell/floating-screen-closed screen-id]))
|
||||
(or animation-time constants/shell-animation-time))))
|
||||
|
||||
(defn floating-screen-gesture
|
||||
[screen-id]
|
||||
(let [{:keys [screen-left screen-state]} (get @state/shared-values-atom screen-id)
|
||||
{:keys [width]} (utils/dimensions)]
|
||||
(-> (gesture/gesture-pan)
|
||||
(gesture/min-distance 0)
|
||||
(gesture/max-pointers 1)
|
||||
(gesture/fail-offset-x -1)
|
||||
(gesture/hit-slop (clj->js {:left 0 :width constants/gesture-width}))
|
||||
(gesture/on-update (worklets.shell/floating-screen-gesture-on-update screen-left))
|
||||
(gesture/on-end
|
||||
(worklets.shell/floating-screen-gesture-on-end
|
||||
{:screen-left screen-left
|
||||
:screen-state screen-state
|
||||
:screen-width width
|
||||
:left-velocity constants/gesture-fling-left-velocity
|
||||
:right-velocity constants/gesture-fling-right-velocity
|
||||
:screen-closed-callback (screen-closed-callback screen-id)})))))
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
(ns utils.worklets.shell)
|
||||
(ns utils.worklets.shell
|
||||
(:require [utils.collection]
|
||||
[camel-snake-kebab.core :as csk]))
|
||||
|
||||
(def bottom-tabs-worklets (js/require "../src/js/worklets/shell/bottom_tabs.js"))
|
||||
(def home-stack-worklets (js/require "../src/js/worklets/shell/home_stack.js"))
|
||||
|
@ -87,3 +89,13 @@
|
|||
(defn floating-screen-z-index
|
||||
[screen-state]
|
||||
(.screenZIndex ^js floating-screen-worklets screen-state))
|
||||
|
||||
(defn floating-screen-gesture-on-update
|
||||
[screen-left]
|
||||
(.screenGestureOnUpdate ^js floating-screen-worklets screen-left))
|
||||
|
||||
(defn floating-screen-gesture-on-end
|
||||
[data]
|
||||
(.screenGestureOnEnd
|
||||
^js floating-screen-worklets
|
||||
(clj->js (utils.collection/map-keys csk/->camelCaseString data))))
|
||||
|
|
Loading…
Reference in New Issue