2022-11-08 18:30:17 +01:00
|
|
|
(ns react-native.gesture
|
2023-10-16 22:03:18 +00:00
|
|
|
(:require
|
|
|
|
["react-native-gesture-handler" :refer
|
|
|
|
(Gesture
|
|
|
|
GestureDetector
|
|
|
|
RectButton
|
|
|
|
Swipeable
|
|
|
|
TouchableWithoutFeedback
|
|
|
|
gestureHandlerRootHOC
|
|
|
|
FlatList
|
|
|
|
ScrollView)]
|
|
|
|
[react-native.flat-list :as rn-flat-list]
|
|
|
|
[reagent.core :as reagent]))
|
2022-09-06 11:19:05 +02:00
|
|
|
|
|
|
|
(def gesture-detector (reagent/adapt-react-class GestureDetector))
|
2023-02-23 07:46:42 +04:00
|
|
|
|
2022-11-14 19:16:55 +01:00
|
|
|
(def gesture-handler-root-hoc gestureHandlerRootHOC)
|
2022-09-06 11:19:05 +02:00
|
|
|
|
2023-02-23 07:46:42 +04:00
|
|
|
(defn gesture-tap [] (.Tap ^js Gesture))
|
|
|
|
|
2022-09-06 11:19:05 +02:00
|
|
|
(defn gesture-pan [] (.Pan ^js Gesture))
|
|
|
|
|
2023-06-27 20:45:13 +05:30
|
|
|
(defn gesture-long-press [] (.LongPress ^js Gesture))
|
|
|
|
|
2023-02-23 07:46:42 +04:00
|
|
|
(defn gesture-pinch [] (.Pinch ^js Gesture))
|
|
|
|
|
|
|
|
(defn on-begin [gesture handler] (.onBegin ^js gesture handler))
|
|
|
|
|
|
|
|
(defn on-start [gesture handler] (.onStart ^js gesture handler))
|
|
|
|
|
|
|
|
(defn on-update [gesture handler] (.onUpdate ^js gesture handler))
|
|
|
|
|
|
|
|
(defn on-end [gesture handler] (.onEnd ^js gesture handler))
|
|
|
|
|
2023-03-03 16:33:28 +04:00
|
|
|
(defn on-finalize [gesture handler] (.onFinalize ^js gesture handler))
|
|
|
|
|
Lint & fix some shadowed core Clojure(Script) vars (#16500)
It's well known that shadowing core Clojure vars can lead to unexpected bugs. In
fact, it's a common source of bugs in other languages too. In the status-mobile
repository there are, in total, 562 shadowed vars, ~500 are core vars. Excluding
the "old code" we still have 285 offenders.
In status-mobile I've already seen two bugs caused by shadowed vars, both with
the shadowed var "name". But probably other problems happened in the past, and
others will happen in the future if we don't do something about this. This PR is
also my response to my frustration trying to review PRs and checking for
shadowed vars, humans were not meant for that!
In this commit we are enabling ":shadowed-var" to lint certain (not all) core
vars as errors (not warnings). In future PRs we can gradually unshadow more
vars. For the record, name is shadowed 40 times in the new code and 130 in
total, and type is shadowed 93 times in the new code and 124 in total!
What about non-core vars, should we allow shadowing? There are ~70 non-core
shadowed vars. In my opinion, we should also lint and disallow shadowing
non-core vars, since it may cause the same kind of bugs of shadowing core vars.
But this decision can be left for another moment/issue, after we have fixed the
most prominent problem of shadowing core vars.
Which vars are unshadowed in this PR? I fixed 62 errors and unshadowed
cljs.core/iter, cljs.core/time, cljs.core/count, cljs.core/key,
clojure.core/key.
Resources:
- [clj-kondo linter: shadowed-var](https://github.com/clj-kondo/clj-kondo/blob/master/doc/linters.md#shadowed-var)
2023-07-06 10:28:07 +00:00
|
|
|
(defn max-pointers [gesture amount] (.maxPointers ^js gesture amount))
|
2023-03-15 16:37:01 +04:00
|
|
|
|
2023-06-15 14:25:52 +02:00
|
|
|
(defn min-distance [gesture dist] (.minDistance ^js gesture dist))
|
|
|
|
|
2023-06-27 21:02:54 +05:30
|
|
|
(defn fail-offset-x [gesture offset] (.failOffsetX ^js gesture offset))
|
|
|
|
|
|
|
|
(defn hit-slop [gesture settings] (.hitSlop ^js gesture settings))
|
|
|
|
|
Lint & fix some shadowed core Clojure(Script) vars (#16500)
It's well known that shadowing core Clojure vars can lead to unexpected bugs. In
fact, it's a common source of bugs in other languages too. In the status-mobile
repository there are, in total, 562 shadowed vars, ~500 are core vars. Excluding
the "old code" we still have 285 offenders.
In status-mobile I've already seen two bugs caused by shadowed vars, both with
the shadowed var "name". But probably other problems happened in the past, and
others will happen in the future if we don't do something about this. This PR is
also my response to my frustration trying to review PRs and checking for
shadowed vars, humans were not meant for that!
In this commit we are enabling ":shadowed-var" to lint certain (not all) core
vars as errors (not warnings). In future PRs we can gradually unshadow more
vars. For the record, name is shadowed 40 times in the new code and 130 in
total, and type is shadowed 93 times in the new code and 124 in total!
What about non-core vars, should we allow shadowing? There are ~70 non-core
shadowed vars. In my opinion, we should also lint and disallow shadowing
non-core vars, since it may cause the same kind of bugs of shadowing core vars.
But this decision can be left for another moment/issue, after we have fixed the
most prominent problem of shadowing core vars.
Which vars are unshadowed in this PR? I fixed 62 errors and unshadowed
cljs.core/iter, cljs.core/time, cljs.core/count, cljs.core/key,
clojure.core/key.
Resources:
- [clj-kondo linter: shadowed-var](https://github.com/clj-kondo/clj-kondo/blob/master/doc/linters.md#shadowed-var)
2023-07-06 10:28:07 +00:00
|
|
|
(defn number-of-taps [gesture amount] (.numberOfTaps ^js gesture amount))
|
2022-09-06 11:19:05 +02:00
|
|
|
|
2023-02-23 07:46:42 +04:00
|
|
|
(defn enabled [gesture enabled?] (.enabled ^js gesture enabled?))
|
2022-09-06 11:19:05 +02:00
|
|
|
|
2023-02-23 07:46:42 +04:00
|
|
|
(defn average-touches [gesture average-touches?] (.averageTouches ^js gesture average-touches?))
|
2023-02-02 15:03:59 +04:00
|
|
|
|
2023-06-15 14:25:52 +02:00
|
|
|
(defn with-test-ID [gesture test-ID] (.withTestId ^js gesture (str test-ID)))
|
|
|
|
|
2023-02-23 07:46:42 +04:00
|
|
|
(defn simultaneous
|
|
|
|
([g1 g2] (.Simultaneous ^js Gesture g1 g2))
|
|
|
|
([g1 g2 g3] (.Simultaneous ^js Gesture g1 g2 g3)))
|
2023-02-02 15:03:59 +04:00
|
|
|
|
2023-02-23 07:46:42 +04:00
|
|
|
(defn exclusive [g1 g2] (.Exclusive ^js Gesture g1 g2))
|
Allow users to swipe to delete or swipe to toggle unread notification status (#15106)
Adds support for swiping left/right on some types of notifications. Swiping left
(from left to right) shows a blue button allowing the user to mark the
notification as read/unread. Swiping right (from right to left) shows a red
button, allowing the user to delete the notification for good.
Related PR in status-go https://github.com/status-im/status-go/pull/3201.
Fixes https://github.com/status-im/status-mobile/issues/14901
Fixes https://github.com/status-im/status-mobile/issues/14900
Technical notes
===============
How's the performance? It feels near native performance in a production release
in a mid-range smartphone. So I'd say it's pretty good, but let me know if you
find any issue.
- I refrained from trying to eliminate all code duplication in this PR. Some
notifications will behave differently, especially the ones with call to
action, so I ask you to please take that in consideration when reviewing. See
https://github.com/status-im/status-mobile/issues/15118
- React Native Gesture Handler has a component named
[Swipeable](https://docs.swmansion.com/react-native-gesture-handler/docs/api/components/swipeable/).
I used it instead of writing a monstrosity :japanese_ogre: of code in
Reanimated to achieve the same results.
- RN Gesture Handler touchables are the only ones that work with the Swipeable
component, so I used them and added vars to `react-native.gesture`.
- I had to manually interpolate the translation X of the buttons behind
notifications because notifications are transparent. To make interpolation
work with `Swipeable` it's mandatory to use RN `Animated.View` and not
`Reanimated.View` (see next point).
- `Swipeable` expects us to pass functions that will receive RN
`AnimatedInterpolation` instances and the rendering lifecycle does not work as
usual. Hooks didn't trigger as expected, functional Reagent components didn't
behave as expected, etc. This means `Reanimated.View` and its interpolation
function is out of question. I did try for almost two days, nothing works.
Testing notes
=============
These are some of the manual tests I ran. There are more scenarios to cover
obviously. Assuming no unread notifications before each flow:
Contact request notification
============================
From the perspective of an user A:
1. Receive a contact request from a non-mutual contact B.
2. Verify the unread count is displayed over the bell icon.
3. Verify the unread count is displayed on the `Messages > Contacts` tab, as
well as on the AC `Contact requests` tab.
4. Open the AC and before accepting/declining the contact request, check that
you CAN'T swipe left or right.
5. Accept or decline the contact request.
6. Check the unread indicator disappears in all necessary places.
7. Press on the notification and see if you're redirected to the chat.
8. Go back to the AC and swipe left to mark as `Unread`. Notice that opening the
chat marks the notification as `Read`. Also very important, notice that the
`Messages > Contacts` tab will NOT show the *pending contact requests*
section at the top. This is on purpose, given the notification is unread, but
the user has already accepted/declined the contact request, hence it's not
pending.
9. Swipe left againg to mark as `Read`. Check all unread indicators are updated.
10. Swipe right to delete the notification (it won't be displayed ever again).
Admin notification
==================
1. Generate an admin notification, e.g. a community owner receiving a request
notification to join.
2. Verify the unread count is displayed over the bell icon, as well as the AC
Admin tab.
3. Verify the community unread indicator is correctly displayed.
4. As an admin, open the AC and before accepting/declining the request, check
that you CAN'T swipe left or right.
5. Accept or decline the membership request.
6. Check the unread indicator disappears accordingly.
7. Swipe left to mark as `Read`.
8. Swipe left to mark as `Unread`.
9. Swipe right to delete the notification (it won't be displayed ever again).
Mentions & replies
==================
Similar steps outlined for `Admin` notifications, but there's one important
difference. Mention and reply notifications don't require a call to action from
the user, so the user can swipe left/right **without** first having to do
anything on the notification (such as pressing on it). See issue
https://github.com/status-im/status-mobile/issues/15118
What about other types of notifications?
========================================
Swipe gestures for other notification types will be implemented in a separate
PR.
2023-02-24 21:22:31 -03:00
|
|
|
|
|
|
|
;; RN Gesture Handler touchables are drop-in replacements for the RN ones. In
|
|
|
|
;; some cases, it's the only touchable that works with Swipeable components.
|
|
|
|
(def touchable-without-feedback (reagent/adapt-react-class TouchableWithoutFeedback))
|
|
|
|
|
|
|
|
(def rect-button (reagent/adapt-react-class RectButton))
|
|
|
|
|
|
|
|
(def ^:private swipeable-component
|
|
|
|
(reagent/adapt-react-class Swipeable))
|
|
|
|
|
|
|
|
(defn swipeable
|
|
|
|
[{:keys [render-left-actions render-right-actions] :as props} & children]
|
|
|
|
(into [swipeable-component
|
|
|
|
(cond-> props
|
|
|
|
render-left-actions
|
|
|
|
(assoc :render-left-actions
|
|
|
|
(fn [& args]
|
|
|
|
(reagent/as-element (apply render-left-actions args))))
|
|
|
|
|
|
|
|
render-right-actions
|
|
|
|
(assoc :render-right-actions
|
|
|
|
(fn [& args]
|
|
|
|
(reagent/as-element (apply render-right-actions args)))))]
|
|
|
|
children))
|
2023-03-15 16:37:01 +04:00
|
|
|
|
|
|
|
(def gesture-flat-list (reagent/adapt-react-class FlatList))
|
2023-03-31 16:13:27 +02:00
|
|
|
|
2023-03-15 16:37:01 +04:00
|
|
|
(defn flat-list
|
|
|
|
[props]
|
|
|
|
[gesture-flat-list (rn-flat-list/base-list-props props)])
|
2023-03-31 16:13:27 +02:00
|
|
|
|
|
|
|
(def scroll-view (reagent/adapt-react-class ScrollView))
|
2023-04-25 15:20:52 +04:00
|
|
|
|
|
|
|
;;; Custom gesture section-list
|
|
|
|
(defn- flatten-sections
|
|
|
|
[sections]
|
|
|
|
(mapcat (fn [{:keys [title data]}]
|
|
|
|
(into [{:title title :header? true}] data))
|
|
|
|
sections))
|
|
|
|
|
|
|
|
(defn section-list
|
|
|
|
[{:keys [sections render-section-header-fn render-fn] :as props}]
|
|
|
|
(let [data (flatten-sections sections)]
|
|
|
|
[flat-list
|
|
|
|
(merge props
|
|
|
|
{:data data
|
2023-06-01 10:35:57 +02:00
|
|
|
:render-fn (fn [p1 p2 p3 p4]
|
|
|
|
(if (:header? p1)
|
|
|
|
[render-section-header-fn p1 p2 p3 p4]
|
|
|
|
[render-fn p1 p2 p3 p4]))})]))
|