Skip to content
This repository was archived by the owner on Dec 28, 2019. It is now read-only.

working hot-reload and overkill refactor #1

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ build/
.gradle
local.properties
*.iml
pom.xml

# node.js
#
Expand Down
13 changes: 3 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,10 @@ Goal
----
Make Shadow-CLJS-based hot reloading work with [React Native Navigation][1].

Status
------
Status: Done
------------
* Hot Reloading works.
* React Native Navigation does not work (pressing button "press me" crashes app).

Problem to be solved
--------------------
The wrapper `reload-wrapper` in `app.cljs` is given a `componentId` prop by
React Native Navigation when it is registered (`.registerComponent Navigation ...`).
This componentId prop must be passed on to the wrapped component for
React Native Navigation to work.
* React Native Navigation works.

Development
-----------
Expand Down
13,343 changes: 13,343 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

81 changes: 23 additions & 58 deletions src/main/cogito/app.cljs
Original file line number Diff line number Diff line change
@@ -1,60 +1,25 @@
(ns cogito.app
(:require
[reagent.core :as r :refer [atom]]
["react-native" :as ReactNative]
["react-native-navigation" :as ReactNativeNavigation :refer (Navigation)]
["create-react-class" :as crc]
[cogito.home :as home]
[cogito.identity-manager :as identity-manager]
[cogito.create-identity :as create-identity]))

(defonce root-ref (atom nil))
(defonce reload-wrapper-ref (atom nil))

(defn reload-wrapper [component-name root]
(let [first-call? (nil? @root-ref)]
(println "reload wrapper ref" @reload-wrapper-ref root)
(reset! root-ref (r/as-element [root]))
(if-not first-call?
(when-let [wrapper @reload-wrapper-ref]
(println "!!! force update !!!")
(.forceUpdate wrapper))
(let [wrapper (crc #js
{:displayName "ReloadWrapper"

:componentDidMount
(fn []
(println "wrapper did mount")
(this-as this (reset! reload-wrapper-ref this)))

:componentWillUnmount
(fn [] (reset! reload-wrapper-ref nil))

:render
(fn []
(this-as this
(js/console.log (.-props this) @root-ref)
(let [body @root-ref]
body)))})] ;; todo pass props to body somehow

(.registerComponent Navigation
component-name
(fn [] wrapper)
#(r/reactify-component root))
(.registerComponent Navigation "IdentityManager" identity-manager/screen)
(.registerComponent Navigation "CreateIdentity" create-identity/screen)))))

(defn init {:dev/after-load true} []
(reload-wrapper "Home" home/screen)

(let [events (.events Navigation)]
(.registerAppLaunchedListener
events
#(.setRoot
Navigation
#js {:root #js
{:stack #js
{:children #js [#js
{:component #js
{:name "Home"}}]
:options #js {:topBar #js {:visible "false"}}}}}))))
[reagent.core :as r :refer [atom]]
["react-native" :as ReactNative]
["react-native-navigation" :as rnn]
[cogito.env :as env]
[cogito.home :as home]
[cogito.identity-manager :as identity-manager]
[cogito.create-identity :as create-identity]))


(defn init []
(env/register "Home")
(env/register "IdentityManager")
(env/register "CreateIdentity")

(-> (rnn/Navigation.events)
(.registerAppLaunchedListener
(fn []
(->> {:root
{:stack
{:children [{:component {:name "Home"}}]
:options {:topBar {:visible "false"}}}}}
(clj->js)
(rnn/Navigation.setRoot))))))
43 changes: 18 additions & 25 deletions src/main/cogito/create_identity.cljs
Original file line number Diff line number Diff line change
@@ -1,37 +1,30 @@
(ns cogito.create-identity
(:require
[reagent.core :as r :refer [atom]]
["react" :refer (createElement)]
["react-native" :as rn]
["react-native-navigation" :as ReactNativeNavigation :refer (Navigation)]
[cogito.env :as env]
[cogito.toolbar-button :as btn :refer [toolbar-button]]))

(def platform (.-Platform rn))

(defn screen-layout []
[:> rn/View {:style {:flex-direction "column"
:margin 40
:align-items "center"}}

[:> rn/Text {:style {:font-size 30
:font-weight "100"
:margin-bottom 20
:text-align "center"}}
"Create Identity"]])

(defn screen []
(r/create-class
{:display-name "create-identity"

:get-initial-state
(fn [this]
(let [events (.events Navigation)]
(.bindComponent events this)))

:reagent-render screen-layout

:navigation-button-pressed
(fn [props]
(.dismissModal Navigation (.-componentId props)))}))
(env/add-screen "CreateIdentity"
{:navigation-button-pressed
(fn [{:keys [component-id] :as props}]
(.dismissModal Navigation component-id))

:render
(fn [props]
[:> rn/View {:style {:flex-direction "column"
:margin 40
:align-items "center"}}

[:> rn/Text {:style {:font-size 30
:font-weight "100"
:margin-bottom 20
:text-align "center"}}
"Create Identity"]])})

(defn presentation-options []
(if (= "ios" (.-OS platform))
Expand Down
92 changes: 92 additions & 0 deletions src/main/cogito/env.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
(ns cogito.env
(:require
["react-native" :as rn]
["react-native-navigation" :as rnn]
["create-react-class" :as crc]
[reagent.core :as r]))

(defonce id-seq-ref (atom 0))
(defonce mounted-ref (atom {}))
(defonce screens-ref (atom {}))

(defn register [key]
(let [get-props
(fn [this]
{::key key
::id (-> this .-state .-id)
:component-id (-> this .-props .-componentId)})

wrapper
(crc #js
{:getInitialState
(fn []
#js {:key key
:id (swap! id-seq-ref inc)})

:componentDidMount
(fn []
(this-as ^js this
(-> (rnn/Navigation.events)
(.bindComponent this))

(swap! mounted-ref
assoc-in [key (-> this .-state .-id)] this)))

:componentWillUnmount
(fn []
(this-as ^js this
(swap! mounted-ref
update key dissoc (-> this .-state .-id))))


;; FIXME: forward other lifecycles the same way
:navigationButtonPressed
(fn []
(this-as this
(let
[{:keys [navigation-button-pressed]}
(get @screens-ref key)

props
(get-props this)]

(js/console.log "navigationButtonPressed"
key
(boolean navigation-button-pressed)
(pr-str props))
(when navigation-button-pressed
(navigation-button-pressed props)))))

:componentDidAppear
(fn []
(this-as this
(js/console.log "componentDidAppear" key)))

:componentDidDisappear
(fn []
(this-as this
(js/console.log "componentDidDisappear" key)))

:render
(fn []
(this-as this
(let [{:keys [render]}
(get @screens-ref key)

props
(get-props this)]

(js/console.log "render" key (pr-str props))
(-> (render props)
(r/as-element)))))})]

(rnn/Navigation.registerComponent key (fn [] wrapper))))

(defn reload {:dev/after-load true} []
(doseq [[key instances] @mounted-ref
[id inst] instances]
(js/console.log "forceUpdate" key id)
(.forceUpdate ^js inst)))

(defn add-screen [key screen-def]
(swap! screens-ref assoc key screen-def))
45 changes: 25 additions & 20 deletions src/main/cogito/home.cljs
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
(ns cogito.home
(:require
[reagent.core :as r :refer [atom]]
["react-native" :as rn]
["react-native-navigation" :as ReactNativeNavigation :refer (Navigation)]
[cogito.identity-manager :as identity-manager]))
[reagent.core :as r :refer [atom]]
["react-native" :as rn]
["react-native-navigation" :as rnn]
[cogito.identity-manager :as identity-manager]
[cogito.env :as env]))

(defn show-identity-manager [componentId]
(.push
Navigation
componentId
#js {:component #js {:name "IdentityManager"
:options identity-manager/push-options}}))
(js/console.log "push" componentId)
(rnn/Navigation.push
componentId
(clj->js
{:component {:name "IdentityManager"
:options identity-manager/push-options}})))

(defn screen [props]
[:> rn/View {:style {:flex-direction "column"
:margin 40
:align-items "center"}}
(env/add-screen "Home"
{:render
(fn [{:keys [component-id] :as props}]
[:> rn/View {:style {:flex-direction "column"
:margin 40
:align-items "center"}}

[:> rn/Text {:style {:font-size 30
:font-weight "100"
:margin-bottom 20
:text-align "center"}}
"Hi Shadow CLJS!"]
[:> rn/Text {:style {:font-size 30
:font-weight "100"
:margin-bottom 20
:text-align "center"}}
"Hi Shadow CLJS!"]

[:> rn/Button {:title "Press me"
:on-press #(show-identity-manager component-id)}]])})

[:> rn/Button {:title "Press me"
:on-press #(show-identity-manager (:componentId props))}]])
54 changes: 24 additions & 30 deletions src/main/cogito/identity_manager.cljs
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
(ns cogito.identity-manager
(:require
[reagent.core :as r :refer [atom]]
["react-native" :as rn]
["react-native-navigation" :as ReactNativeNavigation :refer (Navigation)]
[cogito.create-identity :as create-identity]
[cogito.toolbar-button :as btn :refer [toolbar-button]]))
[reagent.core :as r :refer [atom]]
["react" :refer (createElement)]
["react-native" :as rn]
["react-native-navigation" :as ReactNativeNavigation :refer (Navigation)]
[cogito.create-identity :as create-identity]
[cogito.toolbar-button :as btn :refer [toolbar-button]]
[cogito.env :as env]))

(defn screen-layout []
[:> rn/View {:style {:flex-direction "column"
:margin 40
:align-items "center"}}

[:> rn/Text {:style {:font-size 30
:font-weight "100"
:margin-bottom 20
:text-align "center"}}
"Identities"]])
(env/add-screen "IdentityManager"
;; FIXME: somehow feels wrong to register these on the screen?
{:navigation-button-pressed
#(.showModal Navigation create-identity/modal-presentation-layout)

(defn screen []
(r/create-class
{:display-name "identity-manager"
:render
(fn [props]
[:> rn/View {:style {:flex-direction "column"
:margin 40
:align-items "center"}}

:get-initial-state
(fn [this]
(println "get initial state")
(let [events (.events Navigation)]
(.bindComponent events this)))

:reagent-render screen-layout

:navigation-button-pressed
#(.showModal Navigation create-identity/modal-presentation-layout)}))
[:> rn/Text {:style {:font-size 30
:font-weight "100"
:margin-bottom 20
:text-align "center"}}
"Identities"]])})

(def push-options
(clj->js {:topBar {:visible "true"
:title {:text "Me, myself and I"}
:rightButtons [(toolbar-button "add")]}}))
{:topBar {:visible "true"
:title {:text "Me, myself and I"}
:rightButtons [(toolbar-button "add")]}})
6 changes: 2 additions & 4 deletions src/main/cogito/toolbar_button.cljs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
(ns cogito.toolbar-button
(:require
["react-native" :as ReactNative]))

(def platform (.-Platform ReactNative))
["react-native" :as rn]))

(defn toolbar-button-android [id] {:id id :text id})
(defn toolbar-button-ios [id] {:id id :systemItem id})

(defn toolbar-button [id]
(if (= "ios" (.-OS platform))
(if (= "ios" rn/Platform.OS)
(toolbar-button-ios id)
(toolbar-button-android id)))