Implement Swipe navigation for floating screen (#16390)

This commit is contained in:
Parvesh Monu 2023-06-27 21:02:54 +05:30 committed by GitHub
parent 6b0a51720e
commit b5a96a254a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 117 additions and 9 deletions

View File

@ -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);
}
};
}

View File

@ -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?))

View File

@ -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

View File

@ -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)

View File

@ -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)})))))

View File

@ -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))))