Compare commits

...
Author SHA1 Message Date
Sean Hagstrom c777771524 tweak: utilise use-layout-effect to safely update atom 2024-05-14 09:45:43 +01:00
Sean Hagstrom e6623ef370 tidy: refactor from use-callback to use-memo 2024-05-14 09:45:43 +01:00
Sean Hagstrom 91a592a65b chore: add use-layout-effect 2024-05-14 09:45:43 +01:00
Sean Hagstrom e377064ca9 tidy: remove extra debug labels 2024-05-14 09:45:43 +01:00
Sean Hagstrom 109647c3be wip: refactor to use custom memo implementation and display demo of different behaviors 2024-05-14 09:45:42 +01:00
Sean Hagstrom 01e1ff2b1b tweak: test sharing a state-sub 2024-05-14 09:45:42 +01:00
Sean Hagstrom b69a9933d3 tidy: remove unused button stuff 2024-05-14 09:45:42 +01:00
Sean Hagstrom b61a8c8a5f tweak: update demo with a variety of helper functions 2024-05-14 09:45:42 +01:00
Sean Hagstrom 00c726b7ba tweak: use debug labels for buttons 2024-05-14 09:45:42 +01:00
Sean Hagstrom 2bf0fa845e tweak: only debug buttons with string children 2024-05-14 09:45:41 +01:00
Sean Hagstrom a1a71489e8 tidy: rename functions to be more descriptive 2024-05-14 09:45:41 +01:00
Sean Hagstrom 8b45ddce6f fix: subscription leak during hot reload by using use-memo 2024-05-14 09:45:41 +01:00
Sean Hagstrom 90251249b0 tweak: log when button component renders 2024-05-14 09:45:41 +01:00
Sean Hagstrom be0e14aea4 tweak: refactor example to use reagent.ratom/track and reagent.ratom/track! functions with message state 2024-05-14 09:45:41 +01:00
Sean Hagstrom 1e3d90fef6 wip: add small experiment for ui callback with state and memoization 2024-05-14 09:45:41 +01:00
Sean Hagstrom fc318b6e92 tweak: forward on-press events 2024-05-14 09:45:40 +01:00
4 changed files with 227 additions and 18 deletions
@@ -35,6 +35,7 @@
size 40
customization-color (if (= type :primary) :blue nil)}}
children]
(when (string? children) (js/console.log "button - " children))
(let [[pressed-state? set-pressed-state] (rn/use-state false)
theme (quo.theme/use-theme)
{:keys [icon-color background-color label-color border-color blur-type
+8
View File
@@ -181,6 +181,14 @@
#(let [ret (handler)] (if (fn? ret) ret js/undefined))
(get-js-deps deps))))
(defn use-layout-effect
([handler]
(use-layout-effect handler nil))
([handler deps]
(react/useLayoutEffect
#(let [ret (handler)] (if (fn? ret) ret js/undefined))
(get-js-deps deps))))
(defn use-mount
[handler]
(use-effect handler []))
+2 -2
View File
@@ -14,11 +14,11 @@
[{:keys [on-press allow-multiple-presses? throttle-duration]} throttle-id]
(if allow-multiple-presses?
on-press
(fn []
(fn [event]
(let [id @throttle-id]
(when (and id (not (get @throttle id)))
(swap! throttle assoc id true)
(on-press)
(on-press event)
(js/setTimeout
#(swap! throttle dissoc id)
(or throttle-duration 500)))))))
@@ -1,34 +1,222 @@
(ns status-im.contexts.profile.contact.contact-request.view
(:require [clojure.string :as string]
[quo.core :as quo]
[re-frame.core :as re-frame]
[react-native.core :as rn]
[react-native.platform :as platform]
[reagent.ratom]
[status-im.constants :as constants]
[status-im.contexts.profile.contact.contact-request.style :as style]
[status-im.contexts.profile.utils :as profile.utils]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
;; ---
(def contact-request-message-store
(reagent.ratom/atom {}))
(re-frame/reg-sub
:contact-request-message
(fn []
[contact-request-message-store])
(fn [[store] [_ id]]
(get store id "")))
(re-frame/reg-fx
:store/set-contact-request-message
(fn [[id message]]
(swap! contact-request-message-store assoc id message)))
(re-frame/reg-event-fx
:ui/set-contact-request-message
(fn [_cofx [id message]]
{:fx [[:store/set-contact-request-message [id message]]]}))
;; ---
(def ^:private lookup-sentinel (js-obj))
(defn memo
"Returns a memoized version of a referentially transparent function. The
memoized version of the function keeps a cache of the mapping from arguments
to results and, when calls with the same arguments are repeated often, has
higher performance at the expense of higher memory use."
[mem f]
(fn [& args]
(js/console.log "lookup")
(let [v (get @mem args lookup-sentinel)]
(if (identical? v lookup-sentinel)
(let [ret (apply f args)]
(js/console.log "swap")
(swap! mem assoc args ret)
ret)
v))))
(defn make-state-ref
[state-ratom]
(let [state-ref (atom nil)]
(js/console.log "init make-state-ref")
(let [reaction-sub
(reagent.ratom/track!
(fn [state-ratom]
(js/console.log "reagent reaction")
(let [state @state-ratom]
(js/console.log
(if (nil? @state-ref)
"state-ref init"
"state-ref update")
(clj->js state))
(reset! state-ref state)
state))
state-ratom)]
@reaction-sub
{:sub reaction-sub
:ref state-ref})))
(defn make-state-handler-factory [state-ref]
(let [storage (rn/use-memo #(atom {}) [state-ref])
factory (rn/use-memo
(fn []
(fn [handler]
((memo storage
(fn [callback]
(print "make state handler")
(fn [event]
(callback @state-ref event))))
handler)))
[state-ref storage])]
{:factory factory
:storage storage}))
(defn make-snapshot-handler-factory [snapshot]
(let [storage (rn/use-memo #(atom {}) [])
capture (rn/use-memo #(atom snapshot) [])
_update (rn/use-layout-effect
(fn []
(swap! capture (fn [_] snapshot)))
snapshot)
factory (rn/use-memo
(fn []
(fn [handler]
((memo storage
(fn [callback]
(fn [event]
(callback @capture event))))
handler)))
[storage capture])]
{:factory factory
:storage storage}))
(defn make-past-present-state-handler-factory [state-ref]
(let [storage (rn/use-memo #(atom {}) [state-ref])
capture (rn/use-memo #(atom @state-ref) [state-ref])
factory (rn/use-memo
(fn []
(fn [handler]
(reset! capture @state-ref)
((memo storage
(fn [callback]
(fn [event]
(callback @capture @state-ref event))))
handler)))
[state-ref storage])]
{:factory factory
:storage storage}))
(defn use-bind-sub [state-sub]
(let [{:keys [ref sub]}
(rn/use-memo (fn []
(make-state-ref state-sub))
[state-sub])
{:keys [factory storage]}
(make-state-handler-factory ref)]
(rn/use-unmount (fn []
(js/console.log "unmount bind-sub")
(prn "unmount storage" storage)
(reagent.ratom/dispose! sub)))
factory))
(defn use-bind-past-present-sub [state-sub]
(let [{:keys [ref sub]}
(rn/use-memo (fn []
(make-state-ref state-sub))
[state-sub])
{:keys [factory storage]}
(make-past-present-state-handler-factory ref)]
(rn/use-unmount (fn []
(js/console.log "unmount bind-sub-and-snapshot")
(prn "unmount storage" storage)
(reagent.ratom/dispose! sub)))
factory))
(defn use-bind-snapshot [snapshot]
(let [{:keys [factory storage]}
(make-snapshot-handler-factory snapshot)]
(rn/use-unmount (fn []
(js/console.log "unmount bind-snapshot")
(prn "unmount storage" storage)))
factory))
;; ---
(defn on-message-submit
([snapshot state _event]
(js/console.log "on-press snapshot" (clj->js snapshot))
(js/console.log "on-press state" (clj->js state))
(let [{:keys [public-key message]} state]
;; (rf/dispatch [:hide-bottom-sheet])
;; (rf/dispatch [:contact.ui/send-contact-request
;; public-key message])
(rf/dispatch [:toasts/upsert
{:id :send-contact-request
:type :positive
:text message}])))
([state _event]
(js/console.log "on-press state" (clj->js state))
(let [{:keys [public-key message]} state]
;; (rf/dispatch [:hide-bottom-sheet])
;; (rf/dispatch [:contact.ui/send-contact-request
;; public-key message])
(rf/dispatch [:toasts/upsert
{:id :send-contact-request
:type :positive
:text message}]))))
(defn combine-subs-helper [subs-map]
(reduce (fn [result [name sub]]
(assoc result name @sub))
{}
subs-map))
(defn combine-subs [subs-map]
(reagent.ratom/track combine-subs-helper subs-map))
(defn view
[]
(let [{:keys [public-key customization-color]
:as profile} (rf/sub [:contacts/current-contact])
(let [profile-sub (re-frame/subscribe [:contacts/current-contact])
{:keys [public-key customization-color]
:as profile} (deref profile-sub)
customization-color customization-color
full-name (profile.utils/displayed-name profile)
profile-picture (profile.utils/photo profile)
input-ref (rn/use-ref-atom nil)
message-sub (re-frame/subscribe [:contact-request-message public-key])
[message set-message] (rn/use-state "")
on-message-change (rn/use-callback #(set-message %))
on-message-submit (rn/use-callback (fn []
(rf/dispatch [:hide-bottom-sheet])
(rf/dispatch [:contact.ui/send-contact-request
public-key message])
(rf/dispatch [:toasts/upsert
{:id :send-contact-request
:type :positive
:text (i18n/label
:t/contact-request-was-sent)}]))
[public-key message])]
on-message-change (rn/use-callback #(do
(rf/dispatch-sync [:ui/set-contact-request-message public-key %])
(set-message %)))
state-sub (combine-subs {:message message-sub
:public-key (reagent.ratom/cursor
profile-sub
[:public-key])})
bind-sub (use-bind-sub state-sub)
bind-past-present-sub (use-bind-past-present-sub state-sub)
bind-snapshot (use-bind-snapshot {:public-key public-key
:message message})]
(rn/use-unmount
(fn []
(rf/dispatch [:ui/set-contact-request-message public-key ""])))
(rn/use-mount
(fn []
(let [listener (.addListener rn/keyboard
@@ -58,6 +246,7 @@
:auto-focus true
:accessibility-label :contact-request-message
:label (i18n/label :t/message)
:value @message-sub
:on-change-text on-message-change
:container-style {:flex-shrink 1}
:input-container-style {:flex-shrink 1}}]]
@@ -66,7 +255,18 @@
:actions :one-action
:button-one-props {:disabled? (string/blank? message)
:accessibility-label :send-contact-request
:customization-color customization-color
:on-press on-message-submit}
:button-one-label (i18n/label :t/send-contact-request)}]]))
:customization-color :purple
:on-press (bind-past-present-sub on-message-submit)}
:button-one-label "Past Present Sub"}]
[quo/bottom-actions
{:container-style {:style {:flex 1}}
:actions :two-actions
:button-one-props {:accessibility-label :send-contact-request
:customization-color :blue
:on-press (bind-snapshot on-message-submit)}
:button-one-label "Snapshot"
:button-two-props {:accessibility-label :test-button
:customization-color :orange
:on-press (bind-sub on-message-submit)}
:button-two-label "Sub"}]]))