introduced desktop

Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
Andrey Shovkoplyas 2018-03-22 17:48:04 +03:00
parent d692ddc7d9
commit a1d55f30bb
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
19 changed files with 731 additions and 133 deletions

View File

@ -73,7 +73,7 @@
;; Accessor methods for React Components
(defn add-font-style [style-key {:keys [font] :as opts :or {font :default}}]
(let [font (get-in platform/platform-specific [:fonts (keyword font)])
(let [font (get-in platform/platform-specific [:fonts (keyword font)])
style (get opts style-key)]
(-> opts
(dissoc :font)
@ -98,13 +98,13 @@
:or {font :default}} text]
(let [font (get-in platform/platform-specific [:fonts (keyword font)])]
[text-input-class (merge
{:underline-color-android :transparent
:placeholder-text-color styles/text2-color
:placeholder (i18n/label :t/type-a-message)
:value text}
(-> opts
(dissoc :font)
(assoc :style (merge style font))))]))
{:underline-color-android :transparent
:placeholder-text-color styles/text2-color
:placeholder (i18n/label :t/type-a-message)
:value text}
(-> opts
(dissoc :font)
(assoc :style (merge style font))))]))
(defn icon
([n] (icon n styles/icon-default))
@ -202,10 +202,10 @@
(views current-view)
(= views current-view))
style (if current-view?
{:flex 1}
{:opacity 0
:flex 0})
style (if current-view?
{:flex 1}
{:opacity 0
:flex 0})
component' (if (fn? component) [component] component)]
@ -226,8 +226,8 @@
(defmulti create-main-screen-view #(cond
platform/iphone-x? :iphone-x
platform/ios? :ios
platform/android? :android))
platform/ios? :ios
platform/android? :android))
(defmethod create-main-screen-view :iphone-x [current-view]
(fn [props & children]
@ -254,17 +254,17 @@
:contact-list-modal) styles/color-white
:transparent)})
children (cond-> children
(#{:wallet
:recent-recipients
:wallet-send-assets
:wallet-request-assets} current-view)
(conj [view {:background-color styles/color-white
:position :absolute
:bottom 0
:right 0
:left 0
:height 100
:z-index -1000}]))]
(#{:wallet
:recent-recipients
:wallet-send-assets
:wallet-request-assets} current-view)
(conj [view {:background-color styles/color-white
:position :absolute
:bottom 0
:right 0
:left 0
:height 100
:z-index -1000}]))]
(apply vector safe-area-view props children))))
(defmethod create-main-screen-view :default [_]

View File

@ -0,0 +1,41 @@
(ns status-im.ui.components.svg
(:require [clojure.string :as string]
[hickory.core :as hickory]))
(def svg-tags #{:svg :g :rect :path :use :defs})
(defmacro slurp-svg [file]
"Reads svg file, and return function (fn [color] ..), which returns hiccup structure for react-native-svg lib
Example
SVG:
<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"25\" height=\"25\" viewBox=\"0 0 25 25\">\n
<path fill=\"\" fill-rule=\"evenodd\" d=\"M13.5416667,11.4583333 L13.5416667,6.2571307 C13.5416667,5.67702996 13.0752966,5.20833333 12.5,5.20833333 C11.9206925,5.20833333 11.4583333,5.67789591 11.4583333,6.2571307 L11.4583333,11.4583333 L6.2571307,11.4583333 C5.67702996,11.4583333 5.20833333,11.9247034 5.20833333,12.5 C5.20833333,13.0793075 5.67789591,13.5416667 6.2571307,13.5416667 L11.4583333,13.5416667 L11.4583333,18.7428693 C11.4583333,19.32297 11.9247034,19.7916667 12.5,19.7916667 C13.0793075,19.7916667 13.5416667,19.3221041 13.5416667,18.7428693 L13.5416667,13.5416667 L18.7428693,13.5416667 C19.32297,13.5416667 19.7916667,13.0752966 19.7916667,12.5 C19.7916667,11.9206925 19.3221041,11.4583333 18.7428693,11.4583333 L13.5416667,11.4583333 Z\"/>\n
</svg>
Result:
(fn [color]
[path {:fill color :fill-rule \"evenodd\" :d \"M13.5416667...\"}]
Attention!!!: Please make sure svg file has fill field, and has structure like above
"
(let [svg (-> (clojure.core/slurp file)
(string/replace #"[\n]\s*" ""))
svg-hiccup (hickory/as-hiccup (first (hickory/parse-fragment svg)))
color (gensym "args")]
`(fn [~color]
~(into []
(clojure.walk/postwalk-replace
{:viewbox :viewBox} ;; See https://github.com/jhy/jsoup/issues/272
(clojure.walk/prewalk
(fn [node]
(if (svg-tags node)
(symbol (name node))
(if (vector? node)
(let [[k v] node]
(if (and (= :fill k) v)
[k color]
node))
node)))
svg-hiccup))))))

28
env/dev/env/desktop/main.cljs vendored Normal file
View File

@ -0,0 +1,28 @@
(ns ^:figwheel-no-load env.desktop.main
(:require [reagent.core :as r]
[re-frisk-remote.core :as rr]
[status-im.desktop.core :as core]
[status-im.utils.handlers :as utils.handlers]
[figwheel.client :as figwheel]
[env.config :as conf]
[env.utils]))
(enable-console-print!)
(assert (exists? core/init) "Fatal Error - Your core.cljs file doesn't define an 'init' function!!! - Perhaps there was a compilation failure?")
(assert (exists? core/app-root) "Fatal Error - Your core.cljs file doesn't define an 'app-root' function!!! - Perhaps there was a compilation failure?")
(def cnt (r/atom 0))
(defn reloader [] @cnt [core/app-root])
;; Do not delete, root-el is used by the figwheel-bridge.js
(def root-el (r/as-element [reloader]))
(figwheel/start {:websocket-url (:ios conf/figwheel-urls)
:heads-up-display false
:jsload-callback #(swap! cnt inc)})
(utils.handlers/add-pre-event-callback rr/pre-event-callback)
(rr/enable-re-frisk-remote! {:host (env.utils/re-frisk-url (:ios conf/figwheel-urls))
:on-init core/init})

View File

@ -3,7 +3,14 @@
(defn system-options [builds-to-start]
{:nrepl-port 7888
:builds [{:id :ios
:builds [{:id :desktop
:source-paths ["react-native/src" "src" "env/dev"]
:compiler {:output-to "target/ios/desktop.js"
:main "env.desktop.main"
:output-dir "target/desktop"
:optimizations :none}
:figwheel true}
{:id :ios
:source-paths ["react-native/src" "src" "env/dev"]
:compiler {:output-to "target/ios/app.js"
:main "env.ios.main"

View File

@ -31,18 +31,24 @@
:profiles {:dev {:dependencies [[com.cemerick/piggieback "0.2.2"]]
:cljsbuild {:builds
{:ios
{:source-paths ["react-native/src" "src"]
{:source-paths ["components/src" "react-native/src" "src"]
:compiler {:output-to "target/ios/app.js"
:main "env.ios.main"
:output-dir "target/ios"
:optimizations :none}}
:android
{:source-paths ["react-native/src" "src"]
{:source-paths ["components/src" "react-native/src" "src"]
:compiler {:output-to "target/android/app.js"
:main "env.android.main"
:output-dir "target/android"
:optimizations :none}
:warning-handlers [status-im.utils.build/warning-handler]}}}
:warning-handlers [status-im.utils.build/warning-handler]}
:desktop
{:source-paths ["components/src" "react-native/src" "src"]
:compiler {:output-to "target/desktop/app.js"
:main "env.desktop.main"
:output-dir "target/desktop"
:optimizations :none}}}}
:repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]
:timeout 240000}}
:figwheel [:dev
@ -50,12 +56,12 @@
[re-frisk-remote "0.5.3"]
[re-frisk-sidecar "0.5.4"]
[hawk "0.2.11"]]
:source-paths ["src" "env/dev"]}]
:source-paths ["src" "env/dev" "react-native/src" "components/src"]}]
:test {:dependencies [[day8.re-frame/test "0.1.5"]]
:plugins [[lein-doo "0.1.7"]]
:cljsbuild {:builds
[{:id "test"
:source-paths ["src" "test/cljs"]
:source-paths ["components/src" "src" "test/cljs"]
:compiler {:main status-im.test.runner
:output-to "target/test/test.js"
:output-dir "target/test"
@ -78,7 +84,7 @@
:target :nodejs}}]}}
:prod {:cljsbuild {:builds
{:ios
{:source-paths ["react-native/src" "src" "env/prod"]
{:source-paths ["components/src" "react-native/src" "src" "env/prod"]
:compiler {:output-to "index.ios.js"
:main "env.ios.main"
:output-dir "target/ios-prod"
@ -91,7 +97,7 @@
:language-in :ecmascript5}
:warning-handlers [status-im.utils.build/warning-handler]}
:android
{:source-paths ["react-native/src" "src" "env/prod"]
{:source-paths ["components/src" "react-native/src" "src" "env/prod"]
:compiler {:output-to "index.android.js"
:main "env.android.main"
:output-dir "target/android-prod"

View File

@ -0,0 +1,20 @@
(ns status-im.desktop.core
(:require [reagent.core :as reagent]
status-im.utils.db
status-im.ui.screens.db
status-im.ui.screens.events
status-im.ui.screens.subs
status-im.data-store.core
[status-im.ui.screens.desktop.views :as views]
[status-im.core :as core]
[status-im.ui.components.react :as react]))
(defn app-root []
(reagent/create-class
{:component-will-mount
(fn []
(.hide react/splash-screen))
:reagent-render views/main}))
(defn init []
(core/init app-root))

View File

@ -0,0 +1,23 @@
(ns status-im.desktop.platform)
(def fonts
{:light {:font-family "Roboto-Light"}
:default {:font-family "Roboto-Regular"}
:medium {:font-family "Roboto-Medium"}
:toolbar-title {:font-family "Roboto-Regular"}
:roboto-mono {:font-family "RobotoMono-Medium"}})
;; Structure to be exported
(def platform-specific
{:fonts fonts
:tabs {:tab-shadows? true}
:chats {:action-button? true
:new-chat-in-toolbar? false
:render-separator? false}
:contacts {:action-button? true
:new-contact-in-toolbar? false}
:group-block-shadows? true
:discover {:uppercase-subtitles? false}
:status-bar-default-height 25})

View File

@ -0,0 +1,48 @@
(ns status-im.ui.components.desktop.tabs
(:require [re-frame.core :as re-frame]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.react :as react]
[status-im.ui.screens.main-tabs.styles :as tabs.styles])
(:require-macros [status-im.utils.views :as views]))
;;TODO copy-pate with minimum modifications of status-react tabs
(def tabs-list-data
[{:view-id :home
:content {:title "Home"
:icon-inactive :icons/home
:icon-active :icons/home-active}}
#_{:view-id :wallet
:content {:title "Wallet"
:icon-inactive :icons/wallet
:icon-active :icons/wallet-active}}
{:view-id :profile
:content {:title "Profile"
:icon-inactive :icons/profile
:icon-active :icons/profile-active}}])
(defn- tab-content [{:keys [title icon-active icon-inactive]}]
(fn [active?]
[react/view {:style tabs.styles/tab-container}
(let [icon (if active? icon-active icon-inactive)]
[react/view
[icons/icon icon {:color (:color (tabs.styles/tab-icon active?))}]])
[react/view
[react/text {:style (tabs.styles/tab-title active?)}
title]]]))
(def tabs-list-indexed (map-indexed vector (map #(update % :content tab-content) tabs-list-data)))
(defn tab [index content view-id active?]
[react/touchable-highlight {:style (merge tabs.styles/tab-container {:flex 1})
:disabled active?
:on-press #(re-frame/dispatch [:set-in [:desktop/desktop :tab-view-id] view-id])}
[react/view
[content active?]]])
(views/defview main-tabs []
(views/letsubs [current-tab [:get :left-view-id]]
[react/view
[react/view {:style tabs.styles/tabs-container}
(for [[index {:keys [content view-id]}] tabs-list-indexed]
^{:key index} [tab index content view-id (= current-tab view-id)])]]))

View File

@ -0,0 +1,85 @@
(ns status-im.ui.components.desktop.views
(:require
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.common.styles :as styles]
[status-im.ui.components.action-button.styles :as st]
[status-im.ui.components.styles :as common]
[status-im.ui.components.checkbox.styles :as checkbox.styles]
[status-im.ui.components.react :as react]))
(defn checkbox [{:keys [on-value-change checked?]}]
[react/touchable-highlight {:style checkbox.styles/wrapper :on-press #(do (when on-value-change (on-value-change (not checked?))))}
[react/view {:style (checkbox.styles/icon-check-container checked?)}
(when checked?
[icons/icon :icons/ok {:style checkbox.styles/check-icon}])]])
;; TODO copy-pate with minimum modifications of status-react components
(defn action-button [{:keys [label icon icon-opts on-press label-style cyrcle-color]}]
[react/touchable-highlight {:on-press on-press}
[react/view {:style st/action-button}
[react/view {:style (st/action-button-icon-container cyrcle-color)}
[icons/icon icon icon-opts]]
[react/view {:style st/action-button-label-container}
[react/text {:style (merge st/action-button-label label-style)}
label]]]])
(defn separator [style & [wrapper-style]]
[react/view {:style (merge styles/separator-wrapper wrapper-style)}
[react/view {:style (merge styles/separator style)}]])
(def sticky-button-style
{:flex-direction :row
:height 52
:justify-content :center
:align-items :center
:background-color common/color-light-blue})
(def sticky-button-label-style
{:color common/color-white
:font-size 17
:line-height 20
:letter-spacing -0.2})
(defn sticky-button [label on-press]
[react/touchable-highlight {:on-press on-press}
[react/view {:style sticky-button-style}
[react/text {:style sticky-button-label-style}
label]]])
(defn button-label-style [enabled?]
{:color "#4360df"
:font-size 15
:opacity (if enabled? 1 0.3)
:letter-spacing -0.2})
(defn button [label enabled? on-press]
[react/touchable-highlight {:on-press on-press :disabled (not enabled?)}
[react/view {:style {:width 290
:height 52
:align-items :center
:justify-content :center
:border-radius 8
:background-color :white}}
;:box-shadow "0 2px 6px 0 rgba(0, 0, 0, 0.25)"}}
[react/text {:style (button-label-style enabled?)}
label]]])
(def text-button-label-style
{:color :white
:height 23
:font-size 15
:letter-spacing -0.2})
(defn text-button [label on-press]
[react/touchable-highlight {:on-press on-press}
[react/view
[react/text {:style text-button-label-style}
label]]])
(defn back-button [on-press]
[react/view {:style {:position :absolute :left 32 :top 32 :z-index 1000}}
[react/touchable-highlight {:on-press on-press}
[react/view {:style {:flex-direction :row :align-items :center}}
[icons/icon :icons/back {:color :white}]
[react/text {:style {:margin-left 16 :font-size 15 :color :white}} "Back"]]]])

View File

@ -1,5 +1,5 @@
(ns status-im.ui.components.icons.vector-icons
(:require-macros [status-im.utils.slurp :as slurp])
(:require-macros [status-im.ui.components.svg :as components.svg])
(:require [goog.object :as object]
[reagent.core :as reagent]
[status-im.utils.platform :as platform]
@ -25,62 +25,62 @@
(def use (get-class "Use"))
(def defs (get-class "Defs"))
(def icons {:icons/discover (slurp/slurp-svg "./resources/icons/bottom/discover_gray.svg")
:icons/contacts (slurp/slurp-svg "./resources/icons/bottom/contacts_gray.svg")
:icons/home (slurp/slurp-svg "./resources/icons/bottom/home_gray.svg")
:icons/home-active (slurp/slurp-svg "./resources/icons/bottom/home_blue.svg")
:icons/profile (slurp/slurp-svg "./resources/icons/bottom/profile_gray.svg")
:icons/profile-active (slurp/slurp-svg "./resources/icons/bottom/profile_blue.svg")
:icons/wallet (slurp/slurp-svg "./resources/icons/bottom/wallet_gray.svg")
:icons/wallet-active (slurp/slurp-svg "./resources/icons/bottom/wallet_active.svg")
:icons/speaker (slurp/slurp-svg "./resources/icons/speaker.svg")
:icons/speaker-off (slurp/slurp-svg "./resources/icons/speaker_off.svg")
:icons/transaction-history (slurp/slurp-svg "./resources/icons/transaction_history.svg")
:icons/add (slurp/slurp-svg "./resources/icons/add.svg")
:icons/add-contact (slurp/slurp-svg "./resources/icons/add_contact.svg")
:icons/add-wallet (slurp/slurp-svg "./resources/icons/add_wallet.svg")
:icons/address (slurp/slurp-svg "./resources/icons/address.svg")
:icons/arrow-left (slurp/slurp-svg "./resources/icons/arrow_left.svg")
:icons/arrow-right (slurp/slurp-svg "./resources/icons/arrow_right.svg")
:icons/flash-active (slurp/slurp-svg "./resources/icons/flash_active.svg")
:icons/flash-inactive (slurp/slurp-svg "./resources/icons/flash_inactive.svg")
:icons/attach (slurp/slurp-svg "./resources/icons/attach.svg")
:icons/browse (slurp/slurp-svg "./resources/icons/browse.svg")
:icons/close (slurp/slurp-svg "./resources/icons/close.svg")
:icons/copy-from (slurp/slurp-svg "./resources/icons/copy_from.svg")
:icons/delete (slurp/slurp-svg "./resources/icons/delete.svg")
:icons/dots-horizontal (slurp/slurp-svg "./resources/icons/dots_horizontal.svg")
:icons/dots-vertical (slurp/slurp-svg "./resources/icons/dots_vertical.svg")
:icons/exclamation_mark (slurp/slurp-svg "./resources/icons/exclamation_mark.svg")
:icons/filter (slurp/slurp-svg "./resources/icons/filter.svg")
:icons/fullscreen (slurp/slurp-svg "./resources/icons/fullscreen.svg")
:icons/group-big (slurp/slurp-svg "./resources/icons/group_big.svg")
:icons/group-chat (slurp/slurp-svg "./resources/icons/group_chat.svg")
:icons/chats (slurp/slurp-svg "./resources/icons/chats.svg")
:icons/hamburger (slurp/slurp-svg "./resources/icons/hamburger.svg")
:icons/hidden (slurp/slurp-svg "./resources/icons/hidden.svg")
:icons/in-contacts (slurp/slurp-svg "./resources/icons/in_contacts.svg")
:icons/mic (slurp/slurp-svg "./resources/icons/mic.svg")
:icons/ok (slurp/slurp-svg "./resources/icons/ok.svg")
:icons/public (slurp/slurp-svg "./resources/icons/public.svg")
:icons/public-chat (slurp/slurp-svg "./resources/icons/public_chat.svg")
:icons/qr (slurp/slurp-svg "./resources/icons/QR.svg")
:icons/input-commands (slurp/slurp-svg "./resources/icons/input_commands.svg")
:icons/input-send (slurp/slurp-svg "./resources/icons/input_send.svg")
:icons/back (slurp/slurp-svg "./resources/icons/back.svg")
:icons/forward (slurp/slurp-svg "./resources/icons/forward.svg")
:icons/dropdown-up (slurp/slurp-svg "./resources/icons/dropdown_up.svg")
:icons/up (slurp/slurp-svg "./resources/icons/up.svg")
:icons/down (slurp/slurp-svg "./resources/icons/down.svg")
:icons/grab (slurp/slurp-svg "./resources/icons/grab.svg")
:icons/share (slurp/slurp-svg "./resources/icons/share.svg")
:icons/tooltip-triangle (slurp/slurp-svg "./resources/icons/tooltip-triangle.svg")
:icons/open (slurp/slurp-svg "./resources/icons/open.svg")
:icons/network (slurp/slurp-svg "./resources/icons/network.svg")
:icons/wnode (slurp/slurp-svg "./resources/icons/wnode.svg")
:icons/refresh (slurp/slurp-svg "./resources/icons/refresh.svg")
:icons/newchat (slurp/slurp-svg "./resources/icons/newchat.svg")
:icons/logo (slurp/slurp-svg "./resources/icons/logo.svg")})
(def icons {:icons/discover (components.svg/slurp-svg "./resources/icons/bottom/discover_gray.svg")
:icons/contacts (components.svg/slurp-svg "./resources/icons/bottom/contacts_gray.svg")
:icons/home (components.svg/slurp-svg "./resources/icons/bottom/home_gray.svg")
:icons/home-active (components.svg/slurp-svg "./resources/icons/bottom/home_blue.svg")
:icons/profile (components.svg/slurp-svg "./resources/icons/bottom/profile_gray.svg")
:icons/profile-active (components.svg/slurp-svg "./resources/icons/bottom/profile_blue.svg")
:icons/wallet (components.svg/slurp-svg "./resources/icons/bottom/wallet_gray.svg")
:icons/wallet-active (components.svg/slurp-svg "./resources/icons/bottom/wallet_active.svg")
:icons/speaker (components.svg/slurp-svg "./resources/icons/speaker.svg")
:icons/speaker-off (components.svg/slurp-svg "./resources/icons/speaker_off.svg")
:icons/transaction-history (components.svg/slurp-svg "./resources/icons/transaction_history.svg")
:icons/add (components.svg/slurp-svg "./resources/icons/add.svg")
:icons/add-contact (components.svg/slurp-svg "./resources/icons/add_contact.svg")
:icons/add-wallet (components.svg/slurp-svg "./resources/icons/add_wallet.svg")
:icons/address (components.svg/slurp-svg "./resources/icons/address.svg")
:icons/arrow-left (components.svg/slurp-svg "./resources/icons/arrow_left.svg")
:icons/arrow-right (components.svg/slurp-svg "./resources/icons/arrow_right.svg")
:icons/flash-active (components.svg/slurp-svg "./resources/icons/flash_active.svg")
:icons/flash-inactive (components.svg/slurp-svg "./resources/icons/flash_inactive.svg")
:icons/attach (components.svg/slurp-svg "./resources/icons/attach.svg")
:icons/browse (components.svg/slurp-svg "./resources/icons/browse.svg")
:icons/close (components.svg/slurp-svg "./resources/icons/close.svg")
:icons/copy-from (components.svg/slurp-svg "./resources/icons/copy_from.svg")
:icons/delete (components.svg/slurp-svg "./resources/icons/delete.svg")
:icons/dots-horizontal (components.svg/slurp-svg "./resources/icons/dots_horizontal.svg")
:icons/dots-vertical (components.svg/slurp-svg "./resources/icons/dots_vertical.svg")
:icons/exclamation_mark (components.svg/slurp-svg "./resources/icons/exclamation_mark.svg")
:icons/filter (components.svg/slurp-svg "./resources/icons/filter.svg")
:icons/fullscreen (components.svg/slurp-svg "./resources/icons/fullscreen.svg")
:icons/group-big (components.svg/slurp-svg "./resources/icons/group_big.svg")
:icons/group-chat (components.svg/slurp-svg "./resources/icons/group_chat.svg")
:icons/chats (components.svg/slurp-svg "./resources/icons/chats.svg")
:icons/hamburger (components.svg/slurp-svg "./resources/icons/hamburger.svg")
:icons/hidden (components.svg/slurp-svg "./resources/icons/hidden.svg")
:icons/in-contacts (components.svg/slurp-svg "./resources/icons/in_contacts.svg")
:icons/mic (components.svg/slurp-svg "./resources/icons/mic.svg")
:icons/ok (components.svg/slurp-svg "./resources/icons/ok.svg")
:icons/public (components.svg/slurp-svg "./resources/icons/public.svg")
:icons/public-chat (components.svg/slurp-svg "./resources/icons/public_chat.svg")
:icons/qr (components.svg/slurp-svg "./resources/icons/QR.svg")
:icons/input-commands (components.svg/slurp-svg "./resources/icons/input_commands.svg")
:icons/input-send (components.svg/slurp-svg "./resources/icons/input_send.svg")
:icons/back (components.svg/slurp-svg "./resources/icons/back.svg")
:icons/forward (components.svg/slurp-svg "./resources/icons/forward.svg")
:icons/dropdown-up (components.svg/slurp-svg "./resources/icons/dropdown_up.svg")
:icons/up (components.svg/slurp-svg "./resources/icons/up.svg")
:icons/down (components.svg/slurp-svg "./resources/icons/down.svg")
:icons/grab (components.svg/slurp-svg "./resources/icons/grab.svg")
:icons/share (components.svg/slurp-svg "./resources/icons/share.svg")
:icons/tooltip-triangle (components.svg/slurp-svg "./resources/icons/tooltip-triangle.svg")
:icons/open (components.svg/slurp-svg "./resources/icons/open.svg")
:icons/network (components.svg/slurp-svg "./resources/icons/network.svg")
:icons/wnode (components.svg/slurp-svg "./resources/icons/wnode.svg")
:icons/refresh (components.svg/slurp-svg "./resources/icons/refresh.svg")
:icons/newchat (components.svg/slurp-svg "./resources/icons/newchat.svg")
:icons/logo (components.svg/slurp-svg "./resources/icons/logo.svg")})
(defn normalize-property-name [n]
(if (= n :icons/options)

View File

@ -45,7 +45,8 @@
:inbox/wnodes constants/default-wnodes
:inbox/topic constants/inbox-topic
:inbox/password constants/inbox-password
:my-profile/editing? false})
:my-profile/editing? false
:desktop/desktop {:tab-view-id :home}})
;;;;GLOBAL
@ -116,6 +117,8 @@
:navigation.screen-params/edit-contact-group
:navigation.screen-params/dapp-description])))
(spec/def :desktop/desktop (spec/nilable any?))
;;;;NETWORK
(spec/def ::network (spec/nilable string?))
@ -164,7 +167,8 @@
:browser/browsers
:browser/options
:new/open-dapp
:navigation/screen-params]
:navigation/screen-params
:desktop/desktop]
:opt-un
[::current-public-key
::modal

View File

@ -0,0 +1,47 @@
(ns status-im.ui.screens.desktop.main.add-new.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.icons.vector-icons :as icons]
[re-frame.core :as re-frame]
[status-im.ui.components.react :as react]))
(views/defview new-contact []
(views/letsubs [new-contact-identity [:get :contacts/new-identity]
topic [:get :public-group-topic]]
[react/view {:style {:flex 1 :background-color "#eef2f5"}}
^{:key "newcontact"}
[react/view {:style {:height 64 :align-items :center :padding-horizontal 11 :justify-content :center}}
[react/text {:style {:font-size 16 :color :black :font-weight "600"}}
"Add new contact"]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/view {:style {:height 90 :margin-horizontal 16 :margin-bottom 16 :background-color :white :border-radius 12}}
;:box-shadow "0 0.5px 4.5px 0 rgba(0, 0, 0, 0.04)"}}
[react/view {:style {:flex-direction :row :margin-horizontal 16 :margin-top 16}}
[react/view {:style {:flex 1}}
[react/text-input {:placeholder "Contact key"
:on-change (fn [e]
(let [native-event (.-nativeEvent e)
text (.-text native-event)]
(re-frame/dispatch [:set :contacts/new-identity text])))}]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:add-contact-handler new-contact-identity])}
[react/view {:style {:margin-left 16 :width 30 :height 30 :border-radius 15 :background-color "#eef2f5" :align-items :center
:justify-content :center}}
[icons/icon :icons/ok]]]]]
^{:key "publicchat"}
[react/view {:style {:height 64 :align-items :center :padding-horizontal 11 :justify-content :center}}
[react/text {:style {:font-size 16 :color :black :font-weight "600"}}
"Join to public chat"]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/view {:style {:height 90 :margin-horizontal 16 :margin-bottom 16 :background-color :white :border-radius 12}}
;:box-shadow "0 0.5px 4.5px 0 rgba(0, 0, 0, 0.04)"}}
[react/view {:style {:flex-direction :row :margin-horizontal 16 :margin-top 16}}
[react/text "#"]
[react/view {:style {:flex 1}}
[react/text-input {:placeholder "topic"
:on-change (fn [e]
(let [native-event (.-nativeEvent e)
text (.-text native-event)]
(re-frame/dispatch [:set :public-group-topic text])))}]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:create-new-public-chat topic])}
[react/view {:style {:margin-left 16 :width 30 :height 30 :border-radius 15 :background-color "#eef2f5" :align-items :center
:justify-content :center}}
[icons/icon :icons/ok]]]]]]))

View File

@ -0,0 +1,175 @@
(ns status-im.ui.screens.desktop.main.chat.views
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[status-im.ui.components.icons.vector-icons :as icons]
[clojure.string :as string]
[status-im.chat.styles.message.message :as message.style]
[status-im.utils.gfycat.core :as gfycat.core]
[status-im.utils.gfycat.core :as gfycat]
[status-im.constants :as constants]
[status-im.utils.identicon :as identicon]
[status-im.utils.datetime :as time]
[status-im.ui.components.styles :as styles]
[status-im.ui.components.react :as react]))
(views/defview toolbar-chat-view []
(views/letsubs [{:keys [name public? group-chat]} [:get-current-chat]
chat-id [:get-current-chat-id]
pending-contact? [:current-contact :pending?]
public-key [:chat :public-key]]
(let [chat-name (str
(if public? "#" "")
(if (string/blank? name)
(gfycat.core/generate-gfy public-key)
(or name
"Chat name")))]
[react/view {:style {:align-items :center :padding 11 :justify-content :center}}
[react/view {:style {:flex-direction :row}}
(when public?
[icons/icon :icons/public-chat])
(when (and group-chat (not public?))
[icons/icon :icons/group-chat])
[react/text {:style {:font-size 16 :color :black :font-weight "600"}}
chat-name]]
(when pending-contact?
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:add-pending-contact chat-id])}
[react/view {:style {:background-color :white :border-radius 6 :margin-top 3 :padding 4}} ;style/add-contact
[react/text {:style {:font-size 14 :color "#939ba1"}}
"Add to contacts"]]])])))
(views/defview message-author-name [{:keys [outgoing from] :as message}]
(views/letsubs [current-account [:get-current-account]
incoming-name [:contact-name-by-identity from]]
(if outgoing
[react/text {:style message.style/author} (:name current-account)]
(let [name (or incoming-name (gfycat/generate-gfy from))]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:show-contact-dialog from name (boolean incoming-name)])}
[react/text {:style message.style/author} name]]))))
(def photo-style
{:borderRadius 15
:width 30
:height 30})
(views/defview member-photo [from]
(views/letsubs [photo-path nil];[:photo-path from]]
[react/view
[react/image {:source {:uri (if (string/blank? photo-path)
(identicon/identicon from)
photo-path)}
:style photo-style}]]))
(views/defview my-photo [from]
(views/letsubs [account [:get-current-account]]
(let [{:keys [photo-path]} account]
[react/view
[react/image {:source {:uri (if (string/blank? photo-path)
(identicon/identicon from)
photo-path)}
:style photo-style}]])))
(defn message [text me? {:keys [outgoing message-id chat-id message-status user-statuses timestamp
from current-public-key content-type group-chat type value] :as message}]
(if (= type :datemark)
^{:key (str "datemark" message-id)}
[react/view {:style {:margin-vertical 20}}
[react/text
(string/capitalize (last (string/split value #"-")))]
[react/view {:style {:height 1 :background-color "#e8ebec"}}]]
(when (= content-type constants/text-content-type)
(reagent.core/create-class
{:component-did-mount
#(when (and message-id
chat-id
(not outgoing)
(not= :seen message-status)
(not= :seen (keyword (get-in user-statuses [current-public-key :status]))))
(re-frame/dispatch [:send-seen! {:chat-id chat-id
:from from
:message-id message-id}]))
:reagent-render
(fn []
^{:key (str "message" message-id)}
[react/view {:style {:flex-direction :row :flex 1 :margin-vertical 12}}
(if outgoing
[my-photo from]
[member-photo from])
[react/view {:style {:padding-horizontal 12 :background-color :white :border-radius 8 :flex 1}}
[react/view {:style {:flex-direction :row}}
[message-author-name message]
[react/view {:style {:flex 1}}]
[react/text {:style {:color styles/color-gray4 :font-size 12}} (time/timestamp->time timestamp)]]
;;TODO use https://github.com/status-im/status-react/pull/3299
;;[rn-hl/hyperlink {:linkStyle {:color "#2980b9"} :on-press #(re-frame/dispatch [:show-link-dialog %1])}
[react/text
text]]])}))))
(views/defview messages-view [{:keys [chat-id group-chat]}]
(views/letsubs [chat-id* (atom nil)
scroll-ref (atom nil)
scroll-timer (atom nil)
scroll-height (atom nil)]
(let [_ (when (or (not @chat-id*) (not= @chat-id* chat-id))
(reset! chat-id* chat-id)
(js/setTimeout #(when scroll-ref (.scrollToEnd @scroll-ref)) 400))
messages (re-frame/subscribe [:get-current-chat-messages])
current-public-key (re-frame/subscribe [:get-current-public-key])]
[react/view {:style {:flex 1 :background-color :white :margin-horizontal 16}}
[react/scroll-view {:scrollEventThrottle 16
:on-scroll (fn [e]
(let [ne (.-nativeEvent e)
y (.-y (.-contentOffset ne))]
(when (zero? y)
(when @scroll-timer (js/clearTimeout @scroll-timer))
(reset! scroll-timer (js/setTimeout #(re-frame/dispatch [:load-more-messages]) 300)))
(reset! scroll-height (+ y (.-height (.-layoutMeasurement ne))))))
:on-content-size-change #(when (or (not @scroll-height) (< (- %2 @scroll-height) 500))
(.scrollToEnd @scroll-ref))
:ref #(reset! scroll-ref %)}
[react/view {:style {:padding-vertical 60}}
(doall
(for [[index {:keys [from content message-id] :as message-obj}] (map-indexed vector (reverse @messages))]
^{:key (str message index)}
[message content (= from @current-public-key) (assoc message-obj :group-chat group-chat)]))]]])))
(views/defview chat-text-input []
(views/letsubs [input-text [:chat :input-text]
inp-ref (atom nil)]
[react/view {:style {:height 90 :margin-horizontal 16 :background-color :white :border-radius 12}}
[react/view {:style {:flex-direction :row :margin-horizontal 16 :margin-top 16 :flex 1 :margin-bottom 16}}
[react/view {:style {:flex 1}}
[react/text-input {:default-value (or input-text "")
:placeholder "Type a message..."
:auto-focus true
:multiline true
:blur-on-submit true
:style {:flex 1}
:ref #(reset! inp-ref %)
:on-key-press (fn [e]
(let [native-event (.-nativeEvent e)
key (.-key native-event)]
(when (= key "Enter")
(js/setTimeout #(do
(.clear @inp-ref)
(.focus @inp-ref)) 200)
(re-frame/dispatch [:send-current-message]))))
:on-change (fn [e]
(let [native-event (.-nativeEvent e)
text (.-text native-event)]
(re-frame/dispatch [:set-chat-input-text text])))}]]
[react/touchable-highlight {:on-press (fn []
(js/setTimeout #(do (.clear @inp-ref)(.focus @inp-ref)) 200)
(re-frame/dispatch [:send-current-message]))}
[react/view {:style {:margin-left 16 :width 30 :height 30 :border-radius 15 :background-color "#eef2f5" :align-items :center
:justify-content :center :transform [{ :rotate "90deg"}]}}
[icons/icon :icons/arrow-left]]]]]))
(views/defview chat-view []
(views/letsubs [current-chat [:get-current-chat]]
[react/view {:style {:flex 1 :background-color :white}}
[toolbar-chat-view]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[messages-view current-chat]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[chat-text-input]]))

View File

@ -0,0 +1,47 @@
(ns status-im.ui.screens.desktop.main.tabs.home.views
(:require-macros [status-im.utils.views :as views])
(:require [re-frame.core :as re-frame]
[status-im.utils.gfycat.core :as gfycat]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.react :as react]))
(views/defview unviewed-indicator [chat-id]
(let [unviewed-messages-count (re-frame/subscribe [:unviewed-messages-count chat-id])]
(when (pos? @unviewed-messages-count)
[react/view
[react/text {:font :medium}
@unviewed-messages-count]])))
(views/defview chat-list-item-inner-view [{:keys [chat-id name group-chat public? public-key]}]
(let [name (str
(if public? "#" "")
(or name
(gfycat/generate-gfy public-key)))]
[react/view {:style {:padding 12 :background-color :white :flex-direction :row :align-items :center}}
(when public?
[icons/icon :icons/public-chat])
(when (and group-chat (not public?))
[icons/icon :icons/group-chat])
[react/text
name]
[react/view {:style {:flex 1}}]
[unviewed-indicator chat-id]]))
(defn chat-list-item [[chat-id chat]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-chat chat-id])}
[react/view
[chat-list-item-inner-view (assoc chat :chat-id chat-id)]]])
(views/defview chat-list-view []
(views/letsubs [home-items [:home-items]]
[react/view {:style {:flex 1 :background-color :white}}
[react/view {:style {:align-items :center :flex-direction :row :padding 11}}
[react/view {:style {:flex 1}}]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :new-contact])}
[icons/icon :icons/add]]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/scroll-view
[react/view
(for [[index chat] (map-indexed vector home-items)]
^{:key (str chat index)}
[chat-list-item chat])]]]))

View File

@ -0,0 +1,40 @@
(ns status-im.ui.screens.desktop.main.tabs.profile.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.components.react :as react]
[status-im.ui.screens.profile.user.views :as profile]))
(defn profile-badge [{:keys [name]}]
[react/view {:margin-vertical 10}
[react/text {:style {:font-weight :bold}
:number-of-lines 1}
name]])
(defn profile-info-item [{:keys [label value]}]
[react/view
[react/view
[react/text
label]
[react/view {:height 10}]
[react/text {:number-of-lines 1
:ellipsizeMode :middle}
value]]])
(defn my-profile-info [{:keys [public-key]}]
[react/view
[profile-info-item
{:label "Contact Key"
:value public-key}]])
(views/defview profile []
(views/letsubs [current-account [:get-current-account]]
[react/view {:margin-top 40 :margin-horizontal 10}
[react/view
[profile-badge current-account]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/view
[my-profile-info current-account]]
[react/view {:style {:height 1 :background-color "#e8ebec" :margin-horizontal 16}}]
[react/touchable-highlight {:on-press #(profile/navigate-to-accounts false)
:style {:margin-top 60}}
[react/view
[react/text {:style {:color :red}} "Log out"]]]]))

View File

@ -0,0 +1,40 @@
(ns status-im.ui.screens.desktop.main.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.screens.desktop.main.tabs.profile.views :as profile.views]
[status-im.ui.screens.desktop.main.tabs.home.views :as home.views]
[status-im.ui.screens.desktop.main.chat.views :as chat.views]
[status-im.ui.screens.desktop.main.add-new.views :as add-new.views]
[status-im.ui.components.desktop.tabs :as tabs]
[status-im.ui.components.react :as react]))
(views/defview status-view []
[react/view {:style {:flex 1 :background-color "#eef2f5" :align-items :center :justify-content :center}}
[react/text {:style {:font-size 18 :color "#939ba1"}}
"Status.im"]])
(views/defview tab-views []
(views/letsubs [tab [:get-in [:desktop/desktop :tab-view-id]]]
(let [component (case tab
:profile profile.views/profile
:home home.views/chat-list-view
react/view)]
[react/view {:style {:flex 1}}
[component]])))
(views/defview main-view []
(views/letsubs [view-id [:get :view-id]]
(let [component (case view-id
:chat chat.views/chat-view
:new-contact add-new.views/new-contact
status-view)]
[react/view {:style {:flex 1}}
[component]])))
(views/defview main-views []
[react/view {:style {:flex 1 :flex-direction :row}}
[react/view {:style {:width 280 :background-color :white}}
[react/view {:style {:flex 1}}
[tab-views]]
[tabs/main-tabs]]
[react/view {:style {:width 1 :background-color "#e8ebec"}}]
[main-view]])

View File

@ -0,0 +1,24 @@
(ns status-im.ui.screens.desktop.views
(:require-macros [status-im.utils.views :as views])
(:require [status-im.ui.screens.desktop.main.views :as main.views]
[status-im.ui.components.react :as react]
[status-im.ui.screens.intro.views :as intro.views]
[status-im.ui.screens.accounts.create.views :as create.views]
[status-im.ui.screens.usage-data.views :as usage-data.views]
[status-im.ui.screens.accounts.login.views :as login.views]
[status-im.ui.screens.accounts.recover.views :as recover.views]
[status-im.ui.screens.accounts.views :as accounts.views]))
(views/defview main []
(views/letsubs [view-id [:get :view-id]]
(let [component (case view-id
:intro intro.views/intro
:usage-data usage-data.views/usage-data
:accounts accounts.views/accounts
:recover recover.views/recover
:create-account create.views/create-account
(:new-contact :chat :home) main.views/main-views
:login login.views/login
react/view)]
[react/view {:style {:flex 1}}
[component]])))

View File

@ -1,6 +1,7 @@
(ns status-im.utils.platform
(:require [status-im.android.platform :as android]
[status-im.ios.platform :as ios]
[status-im.desktop.platform :as desktop]
[status-im.react-native.js-dependencies :as rn-dependencies]))
(def platform
@ -22,4 +23,4 @@
(cond
android? android/platform-specific
ios? ios/platform-specific
:else {}))
:else desktop/platform-specific))

View File

@ -1,7 +1,6 @@
(ns status-im.utils.slurp
(:refer-clojure :exclude [slurp])
(:require [clojure.string :as string]
[hickory.core :as hickory]))
(:require [clojure.string :as string]))
(defmacro slurp [file]
(clojure.core/slurp file))
@ -13,41 +12,4 @@
(clojure.core/slurp
(string/join "/" ["resources/js/bots" (name bot-name) file-name]))
(catch Exception _ ""))))
(apply str)))
(def svg-tags #{:svg :g :rect :path :use :defs})
(defmacro slurp-svg [file]
"Reads svg file, and return function (fn [color] ..), which returns hiccup structure for react-native-svg lib
Example
SVG:
<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"25\" height=\"25\" viewBox=\"0 0 25 25\">\n
<path fill=\"\" fill-rule=\"evenodd\" d=\"M13.5416667,11.4583333 L13.5416667,6.2571307 C13.5416667,5.67702996 13.0752966,5.20833333 12.5,5.20833333 C11.9206925,5.20833333 11.4583333,5.67789591 11.4583333,6.2571307 L11.4583333,11.4583333 L6.2571307,11.4583333 C5.67702996,11.4583333 5.20833333,11.9247034 5.20833333,12.5 C5.20833333,13.0793075 5.67789591,13.5416667 6.2571307,13.5416667 L11.4583333,13.5416667 L11.4583333,18.7428693 C11.4583333,19.32297 11.9247034,19.7916667 12.5,19.7916667 C13.0793075,19.7916667 13.5416667,19.3221041 13.5416667,18.7428693 L13.5416667,13.5416667 L18.7428693,13.5416667 C19.32297,13.5416667 19.7916667,13.0752966 19.7916667,12.5 C19.7916667,11.9206925 19.3221041,11.4583333 18.7428693,11.4583333 L13.5416667,11.4583333 Z\"/>\n
</svg>
Result:
(fn [color]
[path {:fill color :fill-rule \"evenodd\" :d \"M13.5416667...\"}]
Attention!!!: Please make sure svg file has fill field, and has structure like above
"
(let [svg (-> (clojure.core/slurp file)
(string/replace #"[\n]\s*" ""))
svg-hiccup (hickory/as-hiccup (first (hickory/parse-fragment svg)))
color (gensym "args")]
`(fn [~color]
~(into []
(clojure.walk/postwalk-replace {:viewbox :viewBox} ;; See https://github.com/jhy/jsoup/issues/272
(clojure.walk/prewalk
(fn [node]
(if (svg-tags node)
(symbol (name node))
(if (vector? node)
(let [[k v] node]
(if (and (= :fill k) v)
[k color]
node))
node)))
svg-hiccup))))))
(apply str)))