diff --git a/README.md b/README.md index 77a6cf0..152ccef 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ Artur Girenko, MIT License This project is a fork of [dmotz/natal](https://github.com/dmotz/natal) by Dan Motzenbecker with the goal of generating skeleton of native app for iOS and Android based on -[Reagent](https://github.com/reagent-project/reagent) and [re-frame](https://github.com/Day8/re-frame). +[Reagent](https://github.com/reagent-project/reagent) and [re-frame](https://github.com/Day8/re-frame) +or [Om.Next](https://github.com/omcljs/om/wiki/Quick-Start-(om.next)). The support of Figwheel is based on brilliant solution developed by Will Decker [decker405/figwheel-react-native](https://github.com/decker405/figwheel-react-native) which works in both platforms. @@ -54,12 +55,15 @@ To bootstrap a new app, run `re-natal init` with your app's name as an argument: ``` $ re-natal init FutureApp ``` +Or, for Om.Next project: + +``` +$ re-natal init FutureApp -i om-next +``` If your app's name is more than a single word, be sure to type it in CamelCase. A corresponding hyphenated Clojure namespace will be created. -Re-Natal will create a simple skeleton based on the current -version of [Reagent](https://github.com/reagent-project/reagent) and [Day8/re-frame](https://github.com/Day8/re-frame). If all goes well you should see printed out basic instructions how to run in iOS simulator. ``` diff --git a/re-natal.coffee b/re-natal.coffee index 5b63368..bdc5bd7 100644 --- a/re-natal.coffee +++ b/re-natal.coffee @@ -37,11 +37,26 @@ interfaceConf = ios: ["core.cljs"] android: ["core.cljs"] common: ["handlers.cljs", "subs.cljs", "db.cljs"] + other: [] deps: ['[reagent "0.5.1" :exclusions [cljsjs/react]]' '[re-frame "0.6.0"]' '[prismatic/schema "1.0.4"]'] shims: ["cljsjs.react"] + sampleCommandNs: '(in-ns \'$PROJECT_NAME_HYPHENATED$.ios.core)' sampleCommand: '(dispatch [:set-greeting "Hello Native World!"])' + 'om-next': + cljsDir: "cljs-om-next" + sources: + ios: ["core.cljs"] + android: ["core.cljs"] + common: ["state.cljs"] + other: [["support.cljs","re_natal/support.cljs"]] + deps: ['[org.omcljs/om "1.0.0-alpha28" :exclusions [cljsjs/react cljsjs/react-dom]]' + '[natal-shell "0.1.6"]'] + shims: ["cljsjs.react", "cljsjs.react.dom"] + sampleCommandNs: '(in-ns \'$PROJECT_NAME_HYPHENATED$.state)' + sampleCommand: '(swap! app-state assoc :app/msg "Hello Native World!")' +interfaceNames = Object.keys interfaceConf log = (s, color = 'green') -> console.log chalk[color] s @@ -246,6 +261,7 @@ shimCljsNamespace = (ns) -> copySrcFiles = (interfaceName, projName, projNameUs, projNameHyph) -> cljsDir = interfaceConf[interfaceName].cljsDir + fileNames = interfaceConf[interfaceName].sources.common; for fileName in fileNames path = "src/#{projNameUs}/#{fileName}" @@ -261,6 +277,12 @@ copySrcFiles = (interfaceName, projName, projNameUs, projNameHyph) -> fs.copySync("#{resources}/#{cljsDir}/#{fileName}", path) edit path, [[projNameHyphRx, projNameHyph], [projNameRx, projName], [platformRx, platform]] + otherFiles = interfaceConf[interfaceName].sources.other; + for cpFile in otherFiles + from = "#{resources}/#{cljsDir}/#{cpFile[0]}" + to = "src/#{cpFile[1]}" + fs.copySync(from, to) + shims = fileNames = interfaceConf[interfaceName].shims; for namespace in shims shimCljsNamespace(namespace) @@ -348,7 +370,7 @@ init = (interfaceName, projName) -> log 'Reload the app in simulator' log '' log 'At the REPL prompt type this:', 'yellow' - log "(in-ns '#{projNameHyph}.ios.core)", 'inverse' + log interfaceConf[interfaceName].sampleCommandNs.replace(projNameHyphRx, projNameHyph), 'inverse' log '' log 'Changes you make via the REPL or by changing your .cljs files should appear live.', 'yellow' log '' @@ -476,15 +498,17 @@ cli.version pkgJson.version cli.command 'init ' .description 'create a new ClojureScript React Native project' - .action (name) -> + .option "-i, --interface [#{interfaceNames.join ' '}]", 'specify React interface', 'reagent' + .action (name, cmd) -> if typeof name isnt 'string' logErr ''' re-natal init requires a project name as the first argument. e.g. re-natal init HelloWorld ''' - - ensureFreePort -> init('reagent', name) + unless interfaceConf[cmd.interface] + logErr "Unsupported React interface: #{cmd.interface}, one of [#{interfaceNames}] was expected." + ensureFreePort -> init(cmd.interface, name) cli.command 'upgrade' .description 'upgrades project files to current installed version of re-natal (the upgrade of re-natal itself is done via npm)' diff --git a/resources/cljs-om-next/core.cljs b/resources/cljs-om-next/core.cljs new file mode 100644 index 0000000..cf3d254 --- /dev/null +++ b/resources/cljs-om-next/core.cljs @@ -0,0 +1,34 @@ +(ns $PROJECT_NAME_HYPHENATED$.$PLATFORM$.core + (:require-macros [natal-shell.components :refer [view text image touchable-highlight]] + [natal-shell.alert :refer [alert]]) + (:require [om.next :as om :refer-macros [defui]] + [re-natal.support :as sup] + [$PROJECT_NAME_HYPHENATED$.state :as state])) + +(set! js/React (js/require "react-native")) + +(def app-registry (.-AppRegistry js/React)) +(def logo-img (js/require "./images/cljs.png")) + +(defui AppRoot + static om/IQuery + (query [this] + '[:app/msg]) + Object + (render [this] + (.log js/console "rendering app") + (let [{:keys [app/msg]} (om/props this)] + (view {:style {:flexDirection "column" :margin 40 :alignItems "center"}} + (text {:style {:fontSize 30 :fontWeight "100" :marginBottom 20 :textAlign "center"}} msg) + (image {:source logo-img + :style {:width 80 :height 80 :marginBottom 30}}) + (touchable-highlight {:style {:backgroundColor "#999" :padding 10 :borderRadius 5} + :onPress #(alert "HELLO!")} + (text {:style {:color "white" :textAlign "center" :fontWeight "bold"}} "press me")))))) + +(defonce RootNode (sup/root-node 1)) +(defonce app-root (om/factory RootNode)) + +(defn init [] + (om/add-root! state/reconciler AppRoot 1) + (.registerComponent app-registry "$PROJECT_NAME$" (fn [] app-root))) \ No newline at end of file diff --git a/resources/cljs-om-next/main_dev.cljs b/resources/cljs-om-next/main_dev.cljs new file mode 100644 index 0000000..96910f7 --- /dev/null +++ b/resources/cljs-om-next/main_dev.cljs @@ -0,0 +1,16 @@ +(ns ^:figwheel-no-load env.$PLATFORM$.main + (:require [om.next :as om] + [$PROJECT_NAME_HYPHENATED$.$PLATFORM$.core :as core] + [$PROJECT_NAME_HYPHENATED$.state :as state] + [figwheel.client :as figwheel :include-macros true])) + +(enable-console-print!) + +(figwheel/watch-and-reload + :websocket-url "ws://localhost:3449/figwheel-ws" + :heads-up-display true + :jsload-callback #(om/add-root! state/reconciler core/AppRoot 1)) + +(core/init) + +(def root-el (core/app-root)) \ No newline at end of file diff --git a/resources/cljs-om-next/main_prod.cljs b/resources/cljs-om-next/main_prod.cljs new file mode 100644 index 0000000..859a0cf --- /dev/null +++ b/resources/cljs-om-next/main_prod.cljs @@ -0,0 +1,4 @@ +(ns env.$PLATFORM$.main + (:require [$PROJECT_NAME_HYPHENATED$.$PLATFORM$.core :as core])) + +(core/init) \ No newline at end of file diff --git a/resources/cljs-om-next/state.cljs b/resources/cljs-om-next/state.cljs new file mode 100644 index 0000000..bfb3763 --- /dev/null +++ b/resources/cljs-om-next/state.cljs @@ -0,0 +1,20 @@ +(ns $PROJECT_NAME_HYPHENATED$.state + (:require [om.next :as om] + [re-natal.support :as sup])) + +(defonce app-state (atom {:app/msg "Hello Clojure in iOS and Android!"})) + +(defmulti read om/dispatch) +(defmethod read :default + [{:keys [state]} k _] + (let [st @state] + (if-let [[_ v] (find st k)] + {:value v} + {:value :not-found}))) + +(defonce reconciler + (om/reconciler + {:state app-state + :parser (om/parser {:read read}) + :root-render sup/root-render + :root-unmount sup/root-unmount})) \ No newline at end of file diff --git a/resources/cljs-om-next/support.cljs b/resources/cljs-om-next/support.cljs new file mode 100644 index 0000000..8ea56f9 --- /dev/null +++ b/resources/cljs-om-next/support.cljs @@ -0,0 +1,35 @@ +(ns re-natal.support + (:require [om.next :refer-macros [ui]])) + +(def root-nodes (atom {})) + +(defn root-node + "A substitute for a real root node (1) for mounting om-next component. + You have to call function :on-render and :on-unmount in reconciler :root-render :root-unmount function." + [id] + (let [content (atom nil) + instance (atom nil) + class (ui Object + (componentWillMount [this] (reset! instance this)) + (render [_] @content))] + (swap! root-nodes assoc id {:on-render (fn [el] + (reset! content el) + (when @instance + (.forceUpdate @instance))) + :on-unmount (fn []) + :class class}) + class)) +(defn root-render + "Use this as reconciler :root-render function." + [el id] + (let [node (get @root-nodes id) + on-render (:on-render node)] + (when on-render (on-render el)))) + +(defn root-unmount + "Use this as reconciler :root-unmount function." + [id] + (let [node (get @root-nodes id) + unmount-fn (:on-unmount node)] + (when unmount-fn (unmount-fn)))) +