diff --git a/.gitignore b/.gitignore index 14773fe52f..3e79181fd8 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,12 @@ target/ # figwheel_server.log .nrepl-port + +# Lein +# +.lein-failures + +## Doo +# +out +doo-index.html diff --git a/env/dev/env/android/main.cljs b/env/dev/env/android/main.cljs index f0c3d96fca..1bf474e505 100644 --- a/env/dev/env/android/main.cljs +++ b/env/dev/env/android/main.cljs @@ -10,7 +10,7 @@ (def root-el (r/as-element [reloader])) (figwheel/watch-and-reload - :websocket-url "ws://10.0.3.2:3449/figwheel-ws" + :websocket-url "ws://localhost:3449/figwheel-ws" :heads-up-display false :jsload-callback #(swap! cnt inc)) diff --git a/project.clj b/project.clj index 647d7ad498..4007f3100d 100644 --- a/project.clj +++ b/project.clj @@ -8,7 +8,7 @@ [reagent "0.5.1" :exclusions [cljsjs/react]] [re-frame "0.6.0"] [prismatic/schema "1.0.4"] - ^{:voom {:repo "https://github.com/status-im/status-lib.git" + ^{:voom {:repo "git@github.com:status-im/status-lib.git" :branch "master"}} [status-im/protocol "0.1.1-20160525_083359-g53ab2c2"] [natal-shell "0.1.6"] @@ -20,9 +20,12 @@ ["do" "clean" ["with-profile" "prod" "cljsbuild" "once" "ios"] ["with-profile" "prod" "cljsbuild" "once" "android"]]} + :test-paths ["test/clj"] :figwheel {:nrepl-port 7888} :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.0-2"] - [com.cemerick/piggieback "0.2.1"]] + [com.cemerick/piggieback "0.2.1"] + [io.appium/java-client "3.4.1"]] + :plugins [[lein-doo "0.1.6"]] :source-paths ["src" "env/dev"] :cljsbuild {:builds {:ios {:source-paths ["src" "env/dev"] :figwheel true @@ -35,7 +38,13 @@ :compiler {:output-to "target/android/not-used.js" :main "env.android.main" :output-dir "target/android" - :optimizations :none}}}} + :optimizations :none}} + :test {:source-paths ["src" "test/cljs"] + :compiler + {:main status-im.test.runner + :output-to "target/test/test.js" + :optimizations :none + :target :nodejs}}}} :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}} :prod {:cljsbuild {:builds {:ios {:source-paths ["src" "env/prod"] :compiler {:output-to "index.ios.js" @@ -46,5 +55,4 @@ :compiler {:output-to "index.android.js" :main "env.android.main" :output-dir "target/android" - :optimizations :simple}}}} - }}) + :optimizations :simple}}}}}}) diff --git a/run-osx.sh b/run-osx.sh index 9f89a05857..64a86b653f 100755 --- a/run-osx.sh +++ b/run-osx.sh @@ -17,11 +17,13 @@ function tab () { fi osascript &>/dev/null <> (name command) + (str "request-"))) + (defn message-content-command-request [{:keys [msg-id content from incoming-group]}] (let [commands-atom (subscribe [:get-commands])] (fn [{:keys [msg-id content from incoming-group]}] (let [commands @commands-atom {:keys [command content]} (parse-command-request commands content)] - [touchable-highlight {:onPress #(set-chat-command msg-id command)} + [touchable-highlight {:onPress #(set-chat-command msg-id command) + :accessibility-label (label command)} [view st/comand-request-view [view st/command-request-message-view (when incoming-group diff --git a/src/status_im/chat/views/plain_input.cljs b/src/status_im/chat/views/plain_input.cljs index 509e961456..9c03cf12a9 100644 --- a/src/status_im/chat/views/plain_input.cljs +++ b/src/status_im/chat/views/plain_input.cljs @@ -90,6 +90,8 @@ input-message dismiss-keyboard) (command/try-send input-command validator))} + (when command + {:accessibility-label :command-input}) input-options) (if message-input? input-message @@ -102,12 +104,14 @@ [touchable-highlight {:disabled animation? :on-press #(try-send staged-commands input-message - dismiss-keyboard)} + dismiss-keyboard) + :accessibility-label :send-message} [view st/send-container [icon :send st/send-icon]]]) (if (command/valid? input-command validator) [touchable-highlight {:disabled animation? - :on-press command/send-command} + :on-press command/send-command + :accessibility-label :stage-command} [view st/send-container [icon :send st/send-icon]]] (when-not response? [touchable-highlight {:disabled animation? diff --git a/src/status_im/chats_list/screen.cljs b/src/status_im/chats_list/screen.cljs index 980c21146e..a82849d7e6 100644 --- a/src/status_im/chats_list/screen.cljs +++ b/src/status_im/chats_list/screen.cljs @@ -16,13 +16,14 @@ toolbar-background2]] [status-im.components.toolbar :refer [toolbar]] [status-im.components.icons.ionicons :refer [icon]] + [status-im.i18n :refer [label]] [status-im.chats-list.styles :as st])) (defn chats-list-toolbar [] [toolbar {:nav-action {:image {:source {:uri :icon_hamburger} :style st/hamburger-icon} :handler open-drawer} - :title "Chats" + :title (label :t/chats) :background-color toolbar-background2 ;; TODO implement search :action {:image {:source {:uri :icon_search} @@ -43,13 +44,13 @@ :offsetY 16 :offsetX 16} [action-button-item - {:title "New Chat" + {:title (label :t/new-chat) :buttonColor :#9b59b6 :onPress #(dispatch [:navigate-to :contact-list])} [icon {:name :android-create :style st/create-icon}]] [action-button-item - {:title "New Group Chat" + {:title (label :t/new-group-chat) :buttonColor :#1abc9c :onPress #(dispatch [:show-group-new])} [icon {:name :person-stalker diff --git a/src/status_im/components/action_button.cljs b/src/status_im/components/action_button.cljs index db303f5f6d..6ee896d1ed 100644 --- a/src/status_im/components/action_button.cljs +++ b/src/status_im/components/action_button.cljs @@ -1,7 +1,7 @@ (ns status-im.components.action-button (:require [reagent.core :as r])) -(set! js/window.ActionButton (js/require "react-native-action-button")) +(def class (js/require "react-native-action-button")) -(def action-button (r/adapt-react-class (.-default js/ActionButton))) -(def action-button-item (r/adapt-react-class (.. js/ActionButton -default -Item))) \ No newline at end of file +(def action-button (r/adapt-react-class (.-default class))) +(def action-button-item (r/adapt-react-class (.. class -default -Item))) diff --git a/src/status_im/components/carousel/carousel.cljs b/src/status_im/components/carousel/carousel.cljs index c3181df6ce..8c61cb114e 100644 --- a/src/status_im/components/carousel/carousel.cljs +++ b/src/status_im/components/carousel/carousel.cljs @@ -5,11 +5,12 @@ touchable-without-feedback text]] [status-im.components.carousel.styles :as st] - [status-im.utils.logging :as log])) + [status-im.utils.logging :as log] + [status-im.components.react :as r])) (defn window-page-width [] - (.-width (.get (.. js/React -Dimensions) "window"))) + (.-width (.get (.. r/react -Dimensions) "window"))) (def defaults {:gap 10 :sneak 10 diff --git a/src/status_im/components/chat_icon/screen.cljs b/src/status_im/components/chat_icon/screen.cljs index e44d334e33..0d93e38cbb 100644 --- a/src/status_im/components/chat_icon/screen.cljs +++ b/src/status_im/components/chat_icon/screen.cljs @@ -6,7 +6,8 @@ image icon]] [status-im.components.chat-icon.styles :as st] - [status-im.components.styles :refer [color-purple]])) + [status-im.components.styles :refer [color-purple]] + [clojure.string :as s])) (defn default-chat-icon [name styles] [view (:default-chat-icon styles) @@ -26,10 +27,10 @@ (defview chat-icon-view [chat-id group-chat name online styles] [photo-path [:chat-photo chat-id]] [view (:container styles) - (if (and photo-path (not (empty? photo-path))) + (if-not (s/blank? photo-path) [chat-icon photo-path styles] [default-chat-icon name styles]) - (when (not group-chat) + (when-not group-chat [contact-online online styles])]) (defn chat-icon-view-chat-list [chat-id group-chat name color online] @@ -80,7 +81,7 @@ [contact [:contact]] (let [;; TODO stub data online true - color color-purple] + color color-purple] [profile-icon-view (:photo-path contact) (:name contact) color online])) (defview my-profile-icon [] @@ -88,5 +89,5 @@ photo-path [:get :photo-path]] (let [;; TODO stub data online true - color color-purple] + color color-purple] [profile-icon-view photo-path name color online])) diff --git a/src/status_im/components/drawer/view.cljs b/src/status_im/components/drawer/view.cljs index 5101c6eaaa..a98fb9d542 100644 --- a/src/status_im/components/drawer/view.cljs +++ b/src/status_im/components/drawer/view.cljs @@ -2,16 +2,17 @@ (:require [clojure.string :as s] [re-frame.core :refer [subscribe dispatch dispatch-sync]] [reagent.core :as r] - [status-im.components.react :refer [android? - view - text - image - navigator - toolbar-android - drawer-layout-android - touchable-opacity]] + [status-im.components.react :refer [react + view + text + image + navigator + toolbar-android + drawer-layout-android + touchable-opacity]] [status-im.resources :as res] - [status-im.components.drawer.styles :as st])) + [status-im.components.drawer.styles :as st] + [status-im.i18n :refer [label]])) (defonce drawer-atom (atom)) @@ -28,7 +29,7 @@ :style st/user-photo}]) (defn menu-item [{:keys [name handler]}] - [touchable-opacity {:style st/menu-item-touchable + [touchable-opacity {:style st/menu-item-touchable :onPress (fn [] (close-drawer) (handler))} @@ -39,40 +40,40 @@ (let [username (subscribe [:get :username])] (fn [] [view st/drawer-menu - [view st/user-photo-container - [user-photo {}]] - [view st/name-container - [text {:style st/name-text} - @username]] - [view st/menu-items-container - [menu-item {:name "Profile" - :handler #(dispatch [:navigate-to :my-profile])}] - [menu-item {:name "Settings" - :handler (fn [] - ;; TODO not implemented - )}] - [menu-item {:name "Discovery" - :handler #(dispatch [:navigate-to :discovery])}] - [menu-item {:name "Contacts" - :handler #(dispatch [:show-contacts navigator])}] - [menu-item {:name "Invite friends" - :handler (fn [] - ;; TODO not implemented - )}] - [menu-item {:name "FAQ" - :handler (fn [])}]] - [view st/switch-users-container - [touchable-opacity {:onPress (fn [] - (close-drawer) - ;; TODO not implemented - )} - [text {:style st/switch-users-text} - "Switch users"]]]]))) + [view st/user-photo-container + [user-photo {}]] + [view st/name-container + [text {:style st/name-text} + @username]] + [view st/menu-items-container + [menu-item {:name (label :t/profile) + :handler #(dispatch [:navigate-to :my-profile])}] + [menu-item {:name (label :t/settings) + :handler (fn [] + ;; TODO not implemented + )}] + [menu-item {:name (label :t/discovery) + :handler #(dispatch [:navigate-to :discovery])}] + [menu-item {:name (label :t/contacts) + :handler #(dispatch [:show-contacts navigator])}] + [menu-item {:name (label :t/invite-friends) + :handler (fn [] + ;; TODO not implemented + )}] + [menu-item {:name (label :t/faq) + :handler (fn [])}]] + [view st/switch-users-container + [touchable-opacity {:onPress (fn [] + (close-drawer) + ;; TODO not implemented + )} + [text {:style st/switch-users-text} + (label :t/switch-users)]]]]))) (defn drawer-view [items] [drawer-layout-android {:drawerWidth 260 :drawerPosition js/React.DrawerLayoutAndroid.positions.Left :render-navigation-view #(r/as-element [drawer-menu]) - :ref (fn [drawer] - (reset! drawer-atom drawer))} + :ref (fn [drawer] + (reset! drawer-atom drawer))} items]) diff --git a/src/status_im/components/icons/ionicons.cljs b/src/status_im/components/icons/ionicons.cljs index 885af057d5..5bc4a06ddc 100644 --- a/src/status_im/components/icons/ionicons.cljs +++ b/src/status_im/components/icons/ionicons.cljs @@ -1,6 +1,4 @@ (ns status-im.components.icons.ionicons (:require [reagent.core :as r])) -(set! js/window.Ionicons (js/require "react-native-vector-icons/Ionicons")) - -(def icon (r/adapt-react-class js/Ionicons)) +(def icon (r/adapt-react-class (js/require "react-native-vector-icons/Ionicons"))) diff --git a/src/status_im/components/invertible_scroll_view.cljs b/src/status_im/components/invertible_scroll_view.cljs index daf873a773..429d66d6ed 100644 --- a/src/status_im/components/invertible_scroll_view.cljs +++ b/src/status_im/components/invertible_scroll_view.cljs @@ -1,8 +1,8 @@ -(ns status-im.components.invertible-scroll-view) +(ns status-im.components.invertible-scroll-view + (:require [reagent.core :as r])) -(set! js/window.InvertibleScrollView (js/require "react-native-invertible-scroll-view")) +(def class (js/require "react-native-invertible-scroll-view")) (defn invertible-scroll-view [props] - (js/React.createElement js/InvertibleScrollView - (clj->js (merge {:inverted true} props)))) + (r/create-element class (clj->js (merge {:inverted true} props)))) diff --git a/src/status_im/components/item_checkbox.cljs b/src/status_im/components/item_checkbox.cljs index 97d43ecd37..45c667141d 100644 --- a/src/status_im/components/item_checkbox.cljs +++ b/src/status_im/components/item_checkbox.cljs @@ -1,7 +1,5 @@ (ns status-im.components.item-checkbox (:require [reagent.core :as r])) -(set! js/window.ItemCheckbox (js/require "react-native-circle-checkbox")) - -(def item-checkbox (r/adapt-react-class js/ItemCheckbox)) +(def item-checkbox (r/adapt-react-class (js/require "react-native-circle-checkbox"))) diff --git a/src/status_im/components/main_tabs.cljs b/src/status_im/components/main_tabs.cljs index b25ade7f4b..16fb5b074d 100644 --- a/src/status_im/components/main_tabs.cljs +++ b/src/status_im/components/main_tabs.cljs @@ -12,19 +12,19 @@ [status-im.components.tabs.tabs :refer [tabs]] [status-im.components.tabs.styles :as st] [status-im.components.styles :as common-st] - [status-im.utils.logging :as log])) + [status-im.i18n :refer [label]])) (def tab-list [{:view-id :chat-list - :title "Chats" + :title (label :t/chats) :screen chats-list :icon :icon_tab_chats} {:view-id :discovery - :title "Discover" + :title (label :t/discovery) :screen discovery :icon :icon_tab_discovery} {:view-id :contact-list - :title "Contacts" + :title (label :t/contacts) :screen contact-list :icon :icon_tab_contacts}]) diff --git a/src/status_im/components/react.cljs b/src/status_im/components/react.cljs index bd534c2ec1..1347f22ea2 100644 --- a/src/status_im/components/react.cljs +++ b/src/status_im/components/react.cljs @@ -1,26 +1,36 @@ (ns status-im.components.react (:require [reagent.core :as r] - [status-im.components.styles :as st])) + [status-im.components.styles :as st] + [status-im.utils.utils :as u])) -(set! js/window.React (js/require "react-native")) +(def react (u/require "react-native")) -(def app-registry (.-AppRegistry js/React)) -(def navigator (r/adapt-react-class (.-Navigator js/React))) -(def text (r/adapt-react-class (.-Text js/React))) -(def view (r/adapt-react-class (.-View js/React))) -(def image (r/adapt-react-class (.-Image js/React))) -(def touchable-highlight-class (r/adapt-react-class (.-TouchableHighlight js/React))) +(defn get-react-property [name] + (aget react name)) + +(defn adapt-class [class] + (when class (r/adapt-react-class class))) + +(defn get-class [name] + (adapt-class (get-react-property name))) + +(def app-registry (get-react-property "AppRegistry")) +(def navigator (get-class "Navigator")) +(def text (get-class "Text")) +(def view (get-class "View")) +(def image (get-class "Image")) +(def touchable-highlight-class (get-class "TouchableHighlight")) (defn touchable-highlight [props content] [touchable-highlight-class (merge {:underlay-color :transparent} props) content]) -(def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React))) -(def list-view-class (r/adapt-react-class (.-ListView js/React))) +(def toolbar-android (get-class "ToolbarAndroid")) +(def list-view-class (get-class "ListView")) (defn list-view [props] [list-view-class (merge {:enableEmptySections true} props)]) -(def scroll-view (r/adapt-react-class (.-ScrollView js/React))) -(def touchable-without-feedback (r/adapt-react-class (.-TouchableWithoutFeedback js/React))) -(def text-input-class (r/adapt-react-class (.-TextInput js/React))) +(def scroll-view (get-class "ScrollView")) +(def touchable-without-feedback (get-class "TouchableWithoutFeedback")) +(def text-input-class (get-class "TextInput")) (defn text-input [props text] [text-input-class (merge {:underlineColorAndroid :transparent @@ -28,11 +38,13 @@ :placeholder "Type"} props) text]) -(def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid js/React))) -(def touchable-opacity (r/adapt-react-class (.-TouchableOpacity js/React))) -(def modal (r/adapt-react-class (.-Modal js/React))) -(def picker (r/adapt-react-class (.-Picker js/React))) -(def picker-item (r/adapt-react-class (.-Item (.-Picker js/React)))) +(def drawer-layout-android (get-class "DrawerLayoutAndroid")) +(def touchable-opacity (get-class "TouchableOpacity")) +(def modal (get-class "Modal")) +(def picker (get-class "Picker")) +(def picker-item + (when-let [picker (get-react-property "Picker")] + (adapt-class (.-Item picker)))) (def pan-responder (.-PanResponder js/React)) (def animated (.-Animated js/React)) @@ -49,20 +61,18 @@ [image {:source {:uri (keyword (str "icon_" (name n)))} :style style}])) -;(def react-linear-gradient (.-default (js/require "react-native-linear-gradient"))) -;(def linear-gradient (r/adapt-react-class react-linear-gradient)) - -(set! js/window.LinearGradient (js/require "react-native-linear-gradient")) +(def linear-gradient-class (u/require "react-native-linear-gradient")) (defn linear-gradient [props] - (js/React.createElement js/LinearGradient - (clj->js (merge {:inverted true} props)))) + (r/create-element linear-gradient-class + (clj->js (merge {:inverted true} props)))) -(def platform (.. js/React -Platform -OS)) +(def platform + (when-let [pl (.-Platform react)] (.-OS pl))) (def android? (= platform "android")) (defn list-item [component] (r/as-element component)) -(def dismiss-keyboard! (js/require "dismissKeyboard")) +(def dismiss-keyboard! (u/require "dismissKeyboard")) diff --git a/src/status_im/components/realm.cljs b/src/status_im/components/realm.cljs deleted file mode 100644 index eff8c1fee5..0000000000 --- a/src/status_im/components/realm.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns status-im.components.realm - (:require [reagent.core :as r])) - -(set! js/window.RealmReactNative (js/require "realm/react-native")) - -(def list-view-class (r/adapt-react-class (.-ListView js/RealmReactNative))) - -(defn list-view [props] - [list-view-class (merge {:enableEmptySections true} props)]) diff --git a/src/status_im/components/toolbar.cljs b/src/status_im/components/toolbar.cljs index d7a164a624..0591c1aba1 100644 --- a/src/status_im/components/toolbar.cljs +++ b/src/status_im/components/toolbar.cljs @@ -12,13 +12,11 @@ color-purple text1-color text2-color - toolbar-background1]] - [status-im.components.realm :refer [list-view]] - [reagent.core :as r])) + toolbar-background1]])) (defn toolbar [{:keys [title nav-action hide-nav? action custom-action background-color custom-content style]}] - (let [style (merge {:flexDirection "row" + (let [style (merge {:flexDirection :row :backgroundColor (or background-color toolbar-background1) :height 56 :elevation 2} style)] @@ -28,21 +26,21 @@ [touchable-highlight {:on-press (:handler nav-action)} [view {:width 56 :height 56 - :alignItems "center" - :justifyContent "center"} + :alignItems :center + :justifyContent :center} [image (:image nav-action)]]] [touchable-highlight {:on-press #(dispatch [:navigate-back])} [view {:width 56 :height 56} - [image {:source {:uri "icon_back"} + [image {:source {:uri :icon_back} :style {:marginTop 21 :marginLeft 23 :width 8 :height 14}}]]])) (or custom-content [view {:style {:flex 1 - :alignItems "center" - :justifyContent "center"}} + :alignItems :center + :justifyContent :center}} [text {:style {:marginTop -2.5 :color text1-color :fontSize 16 @@ -54,7 +52,7 @@ [view {:width 56 :height 56 - :alignItems "center" - :justifyContent "center"} + :alignItems :center + :justifyContent :center} [image (:image action)]]])])) diff --git a/src/status_im/contacts/handlers.cljs b/src/status_im/contacts/handlers.cljs index 268b701815..acb9be9b6c 100644 --- a/src/status_im/contacts/handlers.cljs +++ b/src/status_im/contacts/handlers.cljs @@ -5,7 +5,8 @@ [clojure.string :as s] [status-im.utils.utils :refer [http-post]] [status-im.utils.phone-number :refer [format-phone-number]] - [status-im.utils.handlers :as u])) + [status-im.utils.handlers :as u] + [status-im.utils.utils :refer [require]])) (defn save-contact [_ [_ contact]] @@ -26,7 +27,7 @@ (register-handler :load-contacts load-contacts!) ;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45 -(def react-native-contacts (js/require "react-native-contacts")) +(def react-native-contacts (require "react-native-contacts")) (defn contact-name [contact] (->> contact @@ -91,10 +92,14 @@ (defn add-new-contacts [{:keys [contacts] :as db} [_ new-contacts]] (let [identities (set (map :whisper-identity contacts)) - new-contacts' (remove #(identities (:whisper-identity %)) new-contacts)] + new-contacts' (->> new-contacts + (remove #(identities (:whisper-identity %))) + (map #(vector (:whisper-identity %) %)) + (into {}))] + (println new-contacts') (-> db - (update :contacts concat new-contacts') - (assoc :new-contacts new-contacts')))) + (update :contacts merge new-contacts') + (assoc :new-contacts (vals new-contacts'))))) (register-handler :add-contacts (after save-contacts!) diff --git a/src/status_im/contacts/screen.cljs b/src/status_im/contacts/screen.cljs index 3ed6af93bf..b8672807da 100644 --- a/src/status_im/contacts/screen.cljs +++ b/src/status_im/contacts/screen.cljs @@ -10,13 +10,14 @@ [status-im.components.styles :refer [toolbar-background2]] [status-im.components.toolbar :refer [toolbar]] [status-im.contacts.styles :as st] - [status-im.utils.listview :as lw])) + [status-im.utils.listview :as lw] + [status-im.i18n :refer [label]])) (defn render-row [row _ _] (list-item [contact-view row])) (defn contact-list-toolbar [] - [toolbar {:title "Contacts" + [toolbar {:title (label :t/contacts) :background-color toolbar-background2 :action {:image {:source {:uri :icon_search} :style st/search-icon} diff --git a/src/status_im/contacts/views/contact_inner.cljs b/src/status_im/contacts/views/contact_inner.cljs index 0c62dbd650..05502d793b 100644 --- a/src/status_im/contacts/views/contact_inner.cljs +++ b/src/status_im/contacts/views/contact_inner.cljs @@ -2,7 +2,8 @@ (:require [clojure.string :as s] [status-im.components.react :refer [view image text]] [status-im.resources :as res] - [status-im.contacts.styles :as st])) + [status-im.contacts.styles :as st] + [status-im.i18n :refer [label]])) (defn contact-photo [{:keys [photo-path]}] [view st/contact-photo-container @@ -27,4 +28,4 @@ (if (pos? (count name)) name ;; todo is this correct behaviour? - "Noname")]]]) + (label :t/no-name))]]]) diff --git a/src/status_im/discovery/screen.cljs b/src/status_im/discovery/screen.cljs index fc06443fa7..b7d8e37a7b 100644 --- a/src/status_im/discovery/screen.cljs +++ b/src/status_im/discovery/screen.cljs @@ -9,7 +9,8 @@ [status-im.components.toolbar :refer [toolbar]] [status-im.discovery.views.popular :refer [popular]] [status-im.discovery.views.recent :refer [discovery-recent]] - [status-im.discovery.styles :as st])) + [status-im.discovery.styles :as st] + [status-im.i18n :refer [label]])) (defn get-hashtags [status] (let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))] @@ -20,13 +21,13 @@ (if show-search [text-input {:style st/discovery-search-input :autoFocus true - :placeholder "Type your search tags here" + :placeholder (label :t/search-tags) :onSubmitEditing (fn [e] (let [search (aget e "nativeEvent" "text") hashtags (get-hashtags search)] (dispatch [:broadcast-status search hashtags])))}] [view - [text {:style st/discovery-title} "Discover"]])]) + [text {:style st/discovery-title} (label :t/discovery)]])]) (defn toogle-search [current-value] (dispatch [:set ::show-search (not current-value)])) @@ -45,8 +46,8 @@ :handler #(toogle-search show-search)}}] [scroll-view st/scroll-view-container [view st/section-spacing - [text {:style st/discovery-subtitle} "Popular tags"]] + [text {:style st/discovery-subtitle} (label :t/popular-tags)]] [popular] [view st/section-spacing - [text {:style st/discovery-subtitle} "Recent"]] + [text {:style st/discovery-subtitle} (label :t/recent)]] [discovery-recent]]]) diff --git a/src/status_im/discovery/tag.cljs b/src/status_im/discovery/tag.cljs index a872b48000..aeda1f4d3c 100644 --- a/src/status_im/discovery/tag.cljs +++ b/src/status_im/discovery/tag.cljs @@ -1,7 +1,6 @@ (ns status-im.discovery.tag (:require [re-frame.core :refer [subscribe dispatch]] - [status-im.utils.logging :as log] [status-im.utils.listview :refer [to-datasource]] [status-im.components.react :refer [view text list-view list-item]] [status-im.components.toolbar :refer [toolbar]] @@ -23,7 +22,6 @@ (defn discovery-tag [] (let [tag (subscribe [:get :current-tag]) discoveries (subscribe [:get-discoveries-by-tag])] - (log/debug "Got discoveries: " @discoveries) (fn [] (let [items @discoveries datasource (to-datasource items)] @@ -31,7 +29,6 @@ [toolbar {:nav-action {:image {:source {:uri :icon_back} :style st/icon-back} :handler #(dispatch [:navigate-back])} - :title "Add Participants" :custom-content (title-content @tag) :action {:image {:source {:uri :icon_search} :style st/icon-search} diff --git a/src/status_im/discovery/views/popular.cljs b/src/status_im/discovery/views/popular.cljs index 65b47fdf05..b91b233603 100644 --- a/src/status_im/discovery/views/popular.cljs +++ b/src/status_im/discovery/views/popular.cljs @@ -2,15 +2,16 @@ (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe]] - [status-im.utils.logging :as log] [status-im.components.react :refer [android? text]] [status-im.components.carousel.carousel :refer [carousel]] [status-im.discovery.styles :as st] - [status-im.discovery.views.popular-list :refer [discovery-popular-list]])) + [status-im.discovery.views.popular-list :refer [discovery-popular-list]] + [status-im.i18n :refer [label]] + [status-im.components.react :as r])) (defn page-width [] - (.-width (.get (.. js/React -Dimensions) "window"))) + (.-width (.get (.. r/react -Dimensions) "window"))) (defview popular [] [popular-tags [:get-popular-tags 3]] @@ -19,4 +20,4 @@ :sneak 20} (for [{:keys [name count]} popular-tags] [discovery-popular-list name count])] - [text "None"])) + [text (label :t/none)])) diff --git a/src/status_im/group_settings/screen.cljs b/src/status_im/group_settings/screen.cljs index 97174ea6f3..a35bdbcf53 100644 --- a/src/status_im/group_settings/screen.cljs +++ b/src/status_im/group_settings/screen.cljs @@ -14,7 +14,8 @@ [status-im.components.toolbar :refer [toolbar]] [status-im.components.chat-icon.screen :refer [chat-icon-view-action]] [status-im.group-settings.styles.group-settings :as st] - [status-im.group-settings.views.member :refer [member-view]])) + [status-im.group-settings.views.member :refer [member-view]] + [status-im.i18n :refer [label]])) (defn remove-member [] (dispatch [:remove-participants])) @@ -35,7 +36,7 @@ [text {:style st/modal-member-name} name] [touchable-highlight {:on-press remove-member} [text {:style st/modal-remove-text} - "Remove"]]]]])) + (label :t/remove)]]]]])) (defview chat-members [] [members [:current-chat-contacts]] @@ -75,13 +76,13 @@ [view st/modal-color-picker-inner-container [picker {:selectedValue new-color :onValueChange #(dispatch [:set :new-chat-color %])} - [picker-item {:label "Blue" :value "#7099e6"}] - [picker-item {:label "Purple" :value "#a187d5"}] - [picker-item {:label "Green" :value "green"}] - [picker-item {:label "Red" :value "red"}]] + [picker-item {:label (label :t/blue) :value "#7099e6"}] + [picker-item {:label (label :t/purple) :value "#a187d5"}] + [picker-item {:label (label :t/green) :value "green"}] + [picker-item {:label (label :t/red) :value "red"}]] [touchable-highlight {:on-press set-chat-color} [text {:style st/modal-color-picker-save-btn-text} - "Save"]]]]]) + (label :t/save)]]]]]) (defview chat-color-icon [] [chat-color [:chat :color]] @@ -92,11 +93,11 @@ (defn settings-view [] (let [settings [{:custom-icon [chat-color-icon] - :title "Change color" + :title (label :t/change-color) :handler show-chat-color-picker} ;; TODO not implemented: Notifications - (merge {:title "Notifications and sounds" - :subtitle "!not implemented" + (merge {:title (label :t/notifications-title) + :subtitle (label :t/not-implemented) :handler nil} (if true {:icon :notifications-on @@ -108,13 +109,13 @@ {:icon :close-gray :icon-style {:width 12 :height 12} - :title "Clear history" + :title (label :t/clear-history) ;; TODO show confirmation dialog? :handler #(dispatch [:clear-history])} {:icon :bin :icon-style {:width 12 :height 18} - :title "Delete and leave" + :title (label :t/delete-and-leave) ;; TODO show confirmation dialog? :handler #(dispatch [:leave-group-chat])}]] [view st/settings-container @@ -130,7 +131,7 @@ [chat-icon-view-action chat-id group-chat name color false]]) (defn new-group-toolbar [] - [toolbar {:title "Chat settings" + [toolbar {:title (label :t/chat-settings) :custom-action [chat-icon]}]) (defn focus [] @@ -149,7 +150,7 @@ focused? [:get ::name-input-focused] valid? [:new-chat-name-valid?]] [view - [text {:style st/chat-name-text} "Chat name"] + [text {:style st/chat-name-text} (label :t/chat-name)] [view (st/chat-name-value-container focused?) [text-input {:style st/chat-name-value :ref #(when (and % focused?) (.focus %)) @@ -163,7 +164,7 @@ [view [icon :ok-purple st/add-members-icon]]] [touchable-highlight {:style (st/chat-name-btn-edit-container true) :on-press focus} - [text {:style st/chat-name-btn-edit-text} "Edit"]])] + [text {:style st/chat-name-btn-edit-text} (label :t/edit)]])] (when (pos? (count validation-messages)) [text {:style st/chat-name-validation-message} (first validation-messages)])]) @@ -173,16 +174,16 @@ [new-group-toolbar] [scroll-view st/body [chat-name] - [text {:style st/members-text} "Members"] + [text {:style st/members-text} (label :t/members-title)] [touchable-highlight {:on-press #(dispatch [:navigate-to :add-participants])} ;; TODO add participants view is not in design [view st/add-members-container [icon :add-gray st/add-members-icon] [text {:style st/add-members-text} - "Add members"]]] + (label :t/add-members)]]] [chat-members] [text {:style st/settings-text} - "Settings"] + (label :t/settings)] [settings-view]] (when show-color-picker [chat-color-picker]) diff --git a/src/status_im/group_settings/views/member.cljs b/src/status_im/group_settings/views/member.cljs index 240d6a09c8..c5292daa36 100644 --- a/src/status_im/group_settings/views/member.cljs +++ b/src/status_im/group_settings/views/member.cljs @@ -7,7 +7,8 @@ icon touchable-highlight]] [status-im.resources :as res] - [status-im.group-settings.styles.member :as st])) + [status-im.group-settings.styles.member :as st] + [status-im.i18n :refer [label]])) (defn contact-photo [{:keys [photo-path]}] [view st/contact-photo-container @@ -32,7 +33,7 @@ (if (pos? (count name)) name ;; todo is this correct behaviour? - "Noname")] + (label :t/no-name))] ;; TODO implement :role property for group chat contact (when role [text {:style st/role-text} diff --git a/src/status_im/handlers.cljs b/src/status_im/handlers.cljs index 4010049665..6aa74b5c5f 100644 --- a/src/status_im/handlers.cljs +++ b/src/status_im/handlers.cljs @@ -35,15 +35,19 @@ ;; -- Common -------------------------------------------------------------- +(defn set-el [db [_ k v]] + (assoc db k v)) + (register-handler :set - (debug - (fn [db [_ k v]] - (assoc db k v)))) + debug + set-el) + +(defn set-in [db [_ path v]] + (assoc-in db path v)) (register-handler :set-in - (debug - (fn [db [_ path v]] - (assoc-in db path v)))) + debug + set-in) (register-handler :initialize-db (fn [_ _] diff --git a/src/status_im/i18n.cljs b/src/status_im/i18n.cljs new file mode 100644 index 0000000000..bdc113852c --- /dev/null +++ b/src/status_im/i18n.cljs @@ -0,0 +1,21 @@ +(ns status-im.i18n + (:require + [status-im.translations.en :as en] + [status-im.utils.utils :as u])) + +(def i18n (u/require "react-native-i18n")) +(set! (.-fallbacks i18n) true) +(set! (.-defaultSeparator i18n) "/") + +(set! (.-translations i18n) (clj->js {:en en/translations})) + +(defn label [path & options] + (if (exists? i18n.t) + (.t i18n (name path) (clj->js options)) + (name path))) + +(defn label-pluralize [count path & options] + (if (exists? i18n.t) + (.p i18n count (name path) (clj->js options)) + (name path))) + diff --git a/src/status_im/models/commands.cljs b/src/status_im/models/commands.cljs index 55428ae3d8..b02c18ef28 100644 --- a/src/status_im/models/commands.cljs +++ b/src/status_im/models/commands.cljs @@ -4,32 +4,33 @@ [re-frame.core :refer [subscribe dispatch]] [status-im.db :as db] [status-im.components.animation :as anim] - [status-im.components.styles :refer [color-blue color-dark-mint]])) + [status-im.components.styles :refer [color-blue color-dark-mint]] + [status-im.i18n :refer [label]])) ;; todo delete (def commands [{:command :money :text "!money" - :description "Send money" + :description (label :t/money-command-description) :color color-dark-mint :request-icon {:uri "icon_lock_white"} :icon {:uri "icon_lock_gray"} :suggestion true} {:command :location :text "!location" - :description "Send location" + :description (label :t/location-command-description) :color "#9a5dcf" :suggestion true} {:command :phone :text "!phone" - :description "Send phone number" + :description (label :t/phone-command-description) :color color-dark-mint - :request-text "Phone number request" + :request-text (label :t/phone-request-text) :suggestion true :handler #(dispatch [:sign-up %])} {:command :confirmation-code :text "!confirmationCode" - :description "Send confirmation code" - :request-text "Confirmation code request" + :description (label :t/confirmation-code-command-description) + :request-text (label :t/confirmation-code-request-text) :color color-blue :request-icon {:uri "icon_lock_white"} :icon {:uri "icon_lock_gray"} @@ -37,17 +38,17 @@ :handler #(dispatch [:sign-up-confirm %])} {:command :send :text "!send" - :description "Send location" + :description (label :t/send-command-description) :color "#9a5dcf" :suggestion true} {:command :request :text "!request" - :description "Send request" + :description (label :t/request-command-description) :color "#48ba30" :suggestion true} {:command :keypair-password - :text "!keypairPassword" - :description "" + :text "!keypair-password" + :description (label :t/keypair-password-command-description) :color color-blue :request-icon {:uri "icon_lock_white"} :icon {:uri "icon_lock_gray"} @@ -55,7 +56,7 @@ :handler #(dispatch [:save-password %])} {:command :help :text "!help" - :description "Help" + :description (label :t/help-command-description) :color "#9a5dcf" :suggestion true}]) diff --git a/src/status_im/new_group/screen.cljs b/src/status_im/new_group/screen.cljs index 3a397f0272..97da481782 100644 --- a/src/status_im/new_group/screen.cljs +++ b/src/status_im/new_group/screen.cljs @@ -14,7 +14,8 @@ [status-im.components.toolbar :refer [toolbar]] [status-im.utils.listview :refer [to-datasource]] [status-im.new-group.views.contact :refer [new-group-contact]] - [status-im.new-group.styles :as st])) + [status-im.new-group.styles :as st] + [status-im.i18n :refer [label]])) (defview new-group-toolbar [] @@ -23,7 +24,7 @@ valid? [:new-chat-name-valid?]] (let [create-btn-enabled? (and valid? (not creation-disabled?))] [toolbar - {:title "New group chat" + {:title (label :t/new-group-chat) :action {:image {:source res/v ;; {:uri "icon_search"} :style (st/toolbar-icon create-btn-enabled?)} :handler (when create-btn-enabled? @@ -37,7 +38,7 @@ {:underlineColorAndroid color-purple :style st/group-name-input :autoFocus true - :placeholder "Group Name" + :placeholder (label :t/group-name) :onChangeText #(dispatch [:set :new-chat-name %])} group-name] (when (pos? (count validation-messages)) @@ -48,13 +49,13 @@ [view st/new-group-container [new-group-toolbar] [view st/chat-name-container - [text {:style st/chat-name-text} "Chat name"] + [text {:style st/chat-name-text} (label :t/chat-name)] [group-name-input] - [text {:style st/members-text} "Members"] + [text {:style st/members-text} (label :t/members-title)] [touchable-highlight {:on-press (fn [])} [view st/add-container [icon :add_gray st/add-icon] - [text {:style st/add-text} "Add members"]]] + [text {:style st/add-text} (label :t/add-members)]]] [list-view {:dataSource (to-datasource contacts) :renderRow (fn [row _ _] diff --git a/src/status_im/participants/views/add.cljs b/src/status_im/participants/views/add.cljs index 5bab93ea1c..e708b9a7c2 100644 --- a/src/status_im/participants/views/add.cljs +++ b/src/status_im/participants/views/add.cljs @@ -7,11 +7,12 @@ [status-im.utils.listview :refer [to-datasource]] [status-im.participants.views.contact :refer [participant-contact]] [reagent.core :as r] - [status-im.participants.styles :as st])) + [status-im.participants.styles :as st] + [status-im.i18n :refer [label]])) (defn new-participants-toolbar [] [toolbar - {:title "Add Participants" + {:title (label :t/add-participants) :action {:image {:source res/v ;; {:uri "icon_search"} :style st/new-participant-image} :handler #(do (dispatch [:add-new-participants]) diff --git a/src/status_im/participants/views/remove.cljs b/src/status_im/participants/views/remove.cljs index e7b8103ffe..6469371a3f 100644 --- a/src/status_im/participants/views/remove.cljs +++ b/src/status_im/participants/views/remove.cljs @@ -10,11 +10,12 @@ [status-im.participants.views.contact :refer [participant-contact]] [reagent.core :as r] - [status-im.participants.styles :as st])) + [status-im.participants.styles :as st] + [status-im.i18n :refer [label]])) (defn remove-participants-toolbar [] [toolbar - {:title "Remove Participants" + {:title (label :t/remove-participants) :action {:handler #(do (dispatch [:remove-participants]) (dispatch [:navigate-back])) :image {:source res/trash-icon ;; {:uri "icon_search"} diff --git a/src/status_im/persistence/realm.cljs b/src/status_im/persistence/realm.cljs index a7d564f143..5d304828fe 100644 --- a/src/status_im/persistence/realm.cljs +++ b/src/status_im/persistence/realm.cljs @@ -2,11 +2,10 @@ (:require [cljs.reader :refer [read-string]] [status-im.components.styles :refer [default-chat-color]] [status-im.utils.logging :as log] - [status-im.utils.types :refer [to-string]]) + [status-im.utils.types :refer [to-string]] + [status-im.utils.utils :as u]) (:refer-clojure :exclude [exists?])) -(set! js/window.Realm (js/require "realm")) - (def opts {:schema [{:name :contacts :primaryKey :whisper-identity :properties {:phone-number {:type "string" @@ -70,7 +69,10 @@ :objectType "tag"} :last-updated "date"}}]}) -(def realm (js/Realm. (clj->js opts))) +(def realm-class (u/require "realm")) + +(def realm (when (cljs.core/exists? js/window) + (realm-class. (clj->js opts)))) (def schema-by-name (->> (:schema opts) (mapv (fn [{:keys [name] :as schema}] diff --git a/src/status_im/profile/screen.cljs b/src/status_im/profile/screen.cljs index 83470681dc..bb0c52a1d8 100644 --- a/src/status_im/profile/screen.cljs +++ b/src/status_im/profile/screen.cljs @@ -2,15 +2,16 @@ (:require-macros [status-im.utils.views :refer [defview]]) (:require [re-frame.core :refer [subscribe dispatch]] [status-im.components.react :refer [view - text - image - icon - scroll-view - touchable-highlight - touchable-opacity]] + text + image + icon + scroll-view + touchable-highlight + touchable-opacity]] [status-im.components.chat-icon.screen :refer [profile-icon - my-profile-icon]] - [status-im.profile.styles :as st])) + my-profile-icon]] + [status-im.profile.styles :as st] + [status-im.i18n :refer [label]])) (defn profile-property-view [{:keys [name value]}] [view st/profile-property-view-container @@ -34,36 +35,36 @@ [profile-icon]] [text {:style st/user-name} name] ;; TODO stub data - [text {:style st/status} "!not implemented"] + [text {:style st/status} (label :t/not-implemented)] [view st/btns-container [touchable-highlight {:onPress #(message-user whisper-identity)} [view st/message-btn - [text {:style st/message-btn-text} "Message"]]] + [text {:style st/message-btn-text} (label :t/message)]]] [touchable-highlight {:onPress (fn [] ;; TODO not implemented )} [view st/more-btn [icon :more_vertical_blue st/more-btn-image]]]]] [view st/profile-properties-container - [profile-property-view {:name "Username" + [profile-property-view {:name (label :t/username) :value name}] - [profile-property-view {:name "Phone number" + [profile-property-view {:name (label :t/phone-number) :value phone-number}] ;; TODO stub data - [profile-property-view {:name "Email" - :value "!not implemented"}] + [profile-property-view {:name (label :t/email) + :value (label :t/not-implemented)}] [view st/report-user-container [touchable-highlight {:on-press (fn [] ;; TODO not implemented )} - [text {:style st/report-user-text} "REPORT USER"]]]]]) + [text {:style st/report-user-text} (label :t/report-user)]]]]]) (defview my-profile [] - [username [:get :username] - photo-path [:get :photo-path] + [username [:get :username] + photo-path [:get :photo-path] phone-number [:get :phone-number] - email [:get :email] - status [:get :status]] + email [:get :email] + status [:get :status]] [scroll-view {:style st/profile} [touchable-highlight {:style st/back-btn-touchable :on-press #(dispatch [:navigate-back])} @@ -81,9 +82,9 @@ [text {:style st/user-name} username] [text {:style st/status} status]] [view st/profile-properties-container - [profile-property-view {:name "Username" + [profile-property-view {:name (label :t/username) :value username}] - [profile-property-view {:name "Phone number" + [profile-property-view {:name (label :t/phone-number) :value phone-number}] - [profile-property-view {:name "Email" + [profile-property-view {:name (label :t/email) :value email}]]]) diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs index 4b51c9ec65..a9b94e5541 100644 --- a/src/status_im/protocol/handlers.cljs +++ b/src/status_im/protocol/handlers.cljs @@ -12,7 +12,8 @@ set-initialized]] [status-im.constants :refer [text-content-type]] [status-im.models.messages :as messages] - [status-im.models.chats :as chats])) + [status-im.models.chats :as chats] + [status-im.i18n :refer [label]])) (register-handler :initialize-protocol (u/side-effect! @@ -35,35 +36,35 @@ (let [contact-name (:name (contacts/contact-by-identity from))] (messages/save-message chat-id {:from "system" :msg-id (str msg-id "_" from) - :content (str (or contact-name from) " received chat invitation") + :content (str (or contact-name from) " " (label :t/received-invitation)) :content-type text-content-type}))) (defn participant-invited-to-group-msg [chat-id identity from msg-id] (let [inviter-name (:name (contacts/contact-by-identity from)) invitee-name (if (= identity (api/my-identity)) - "You" + (label :t/You) (:name (contacts/contact-by-identity identity)))] (messages/save-message chat-id {:from "system" :msg-id msg-id - :content (str (or inviter-name from) " invited " (or invitee-name identity)) + :content (str (or inviter-name from) " " (label :t/invited) " " (or invitee-name identity)) :content-type text-content-type}))) (defn participant-removed-from-group-msg [chat-id identity from msg-id] (let [remover-name (:name (contacts/contact-by-identity from)) removed-name (:name (contacts/contact-by-identity identity))] - (->> (str (or remover-name from) " removed " (or removed-name identity)) + (->> (str (or remover-name from) " " (label :t/removed) " " (or removed-name identity)) (system-message msg-id) (messages/save-message chat-id)))) (defn you-removed-from-group-msg [chat-id from msg-id] (let [remover-name (:name (contacts/contact-by-identity from))] - (->> (str (or remover-name from) " removed you from group chat") + (->> (str (or remover-name from) " " (label :t/removed-from-chat)) (system-message msg-id) (messages/save-message chat-id)))) (defn participant-left-group-msg [chat-id from msg-id] (let [left-name (:name (contacts/contact-by-identity from))] - (->> (str (or left-name from) " left") + (->> (str (or left-name from) " " (label :t/left)) (system-message msg-id) (messages/save-message chat-id)))) diff --git a/src/status_im/translations/en.cljs b/src/status_im/translations/en.cljs new file mode 100644 index 0000000000..588332922d --- /dev/null +++ b/src/status_im/translations/en.cljs @@ -0,0 +1,122 @@ +(ns status-im.translations.en) + +(def translations + { + ;common + :members-title "Members" + :not-implemented "!not implemented" + :chat-name "Chat name" + :notifications-title "Notifications and sounds" + + ;drawer + :invite-friends "Invite friends" + :faq "FAQ" + :switch-users "Switch users" + + ;chat + :is-typing "is typing" + :and-you "and you" + :search-chat "Search chat" + :members {:one "1 member, 1 active" + :other "{{count}} members, {{count}} active" + :zero "no members"} + :last-active "Active a minute ago" + + ;profile + :profile "Profile" + :report-user "REPORT USER" + :message "Message" + :username "Username" + :phone-number "Phone number" + :email "Email" + + ;sign-up + :contacts-syncronized "Your contacts have been synchronized" + :confirmation-code (str "Thanks! We've sent you a text message with a confirmation " + "code. Please provide that code to confirm your phone number") + :password-saved (str "OK great! Your password has been saved. Just to let you " + "know you can always change it in the Console by the way " + "it's me the Console nice to meet you!") + :generate-passphrase (str "I'll generate a passphrase for you so you can restore your " + "access or log in from another device") + :passphrase "Here's your passphrase:" + :written-down "Make sure you had securely written it down" + :phone-number-required (str "Your phone number is also required to use the app. Type the " + "exclamation mark or hit the icon to open the command list " + "and choose the !phone command") + :intro-status (str "The brash businessman’s braggadocio " + "and public exchange with candidates " + "in the US presidential election") + :intro-message1 "Hello there! It's Status a Dapp browser in your phone." + :intro-message2 (str "Status1 uses a highly secure key-pair authentication type " + "to provide you a reliable way to access your account") + :keypair-generated (str "A key pair has been generated and saved to your device. " + "Create a password to secure your key") + + ;chats + :chats "Chats" + :new-chat "New Chat" + :new-group-chat "New Group Chat" + + ;discover + :discovery "Discovery" + :none "None" + :search-tags "Type your search tags here" + :popular-tags "Popular tags" + :recent "Recent" + + ;settings + :settings "Settings" + + ;contacts + :contacts "Contacts" + :no-name "Noname" + :new-contact "New Contact" + + ;group-settings + :remove "Remove" + :save "Save" + :change-color "Change color" + :clear-history "Clear history" + :delete-and-leave "Delete and leave" + :chat-settings "Chat settings" + :edit "Edit" + :add-members "Add Members" + :blue "Blue" + :purple "Purple" + :green "Green" + :red "Red" + + ;commands + :money-command-description "Send money" + :location-command-description "Send location" + :phone-command-description "Send phone number" + :phone-request-text "Phone number request" + :confirmation-code-command-description "Send confirmation code" + :confirmation-code-request-text "Confirmation code request" + :send-command-description "Send location" + :request-command-description "Send request" + :keypair-password-command-description "" + :help-command-description "Help" + + ;new-group + :group-name "Group Name" + + ;participants + :add-participants "Add Participants" + :remove-participants "Remove Participants" + + ;protocol + :received-invitation "received chat invitation" + :removed-from-chat "removed you from group chat" + :left "left" + :invited "invited" + :removed "removed" + :You "You" + + ;new-contact + :import-qr "Import from QR" + :contact-name "Contact Name" + :contact-address "Contact Address" + + }) \ No newline at end of file diff --git a/src/status_im/utils/crypt.cljs b/src/status_im/utils/crypt.cljs index 722581d10c..0b3f2dd7d0 100644 --- a/src/status_im/utils/crypt.cljs +++ b/src/status_im/utils/crypt.cljs @@ -1,9 +1,10 @@ (ns status-im.utils.crypt (:require [goog.crypt :refer [byteArrayToHex]] - [clojure.string :as s]) + [clojure.string :as s] + [status-im.utils.utils :as u]) (:import goog.crypt.Sha256)) -(set! js/window.RnRandomBytes (js/require "react-native-randombytes")) +(def random-bytes (u/require "react-native-randombytes")) (def sha-256 (Sha256.)) @@ -19,7 +20,7 @@ (byteArrayToHex (.digest sha-256))) (defn gen-random-bytes [length cb] - (.randomBytes js/window.RnRandomBytes length (fn [& [err buf]] - (if err - (cb {:error err}) - (cb {:buffer buf}))))) + #_(.randomBytes random-bytes length (fn [& [err buf]] + (if err + (cb {:error err}) + (cb {:buffer buf}))))) diff --git a/src/status_im/utils/listview.cljs b/src/status_im/utils/listview.cljs index bd9dfe5dc5..dcd63da8bd 100644 --- a/src/status_im/utils/listview.cljs +++ b/src/status_im/utils/listview.cljs @@ -1,6 +1,5 @@ (ns status-im.utils.listview - (:require-macros [natal-shell.data-source :refer [data-source]]) - (:require [status-im.components.realm])) + (:require-macros [natal-shell.data-source :refer [data-source]])) (defn clone-with-rows [ds rows] (.cloneWithRows ds (reduce (fn [ac el] (.push ac el) ac) diff --git a/src/status_im/utils/phone_number.cljs b/src/status_im/utils/phone_number.cljs index 137a1c37b5..da0ada6733 100644 --- a/src/status_im/utils/phone_number.cljs +++ b/src/status_im/utils/phone_number.cljs @@ -1,16 +1,17 @@ -(ns status-im.utils.phone-number) +(ns status-im.utils.phone-number + (:require [status-im.utils.utils :as u])) -(def i18n (js/require "react-native-i18n")) -(def locale (.-locale i18n)) +(def i18n (u/require "react-native-i18n")) +(def locale (or (.-locale i18n) "___en")) (def country-code (subs locale 3 5)) -(set! js/PhoneNumber (js/require "awesome-phonenumber")) +(def awesome-phonenumber (u/require "awesome-phonenumber")) ;; todo check wrong numbers, .getNumber returns empty string (defn format-phone-number [number] - (str (.getNumber (js/PhoneNumber. number country-code "international")))) + (str (.getNumber (awesome-phonenumber. number country-code "international")))) (defn valid-mobile-number? [number] (when (string? number) - (let [number-obj (js/PhoneNumber. number country-code "international")] + (let [number-obj (awesome-phonenumber. number country-code "international")] (and (.isValid number-obj) (.isMobile number-obj))))) diff --git a/src/status_im/utils/sms_listener.cljs b/src/status_im/utils/sms_listener.cljs index f00d54b7e9..04204116a2 100644 --- a/src/status_im/utils/sms_listener.cljs +++ b/src/status_im/utils/sms_listener.cljs @@ -1,7 +1,8 @@ (ns status-im.utils.sms-listener - (:require [status-im.components.react :refer [android?]])) + (:require [status-im.components.react :refer [android?]] + [status-im.utils.utils :as u])) -(def sms-listener (.-default (js/require "react-native-android-sms-listener"))) +(def sms-listener (.-default (u/require "react-native-android-sms-listener"))) ;; Only android is supported! diff --git a/src/status_im/utils/subs.cljs b/src/status_im/utils/subs.cljs index 3be283c37e..60bb24c53b 100644 --- a/src/status_im/utils/subs.cljs +++ b/src/status_im/utils/subs.cljs @@ -5,7 +5,6 @@ "Creates subscrition that cheks if collection (map or set) contains element" [collection] (fn [db [_ element]] - (println "WWWWWWWWWW" (type db)) (-> (collection @db) (contains? element) (reaction)))) diff --git a/src/status_im/utils/utils.cljs b/src/status_im/utils/utils.cljs index 02d54d3f88..c20a6c1e2a 100644 --- a/src/status_im/utils/utils.cljs +++ b/src/status_im/utils/utils.cljs @@ -5,6 +5,11 @@ [natal-shell.toast-android :as toast]) (:require [status-im.constants :as const])) +(defn require [module] + (if (exists? js/window) + (js/require module) + #js {})) + (defn log [obj] (.log js/console obj)) diff --git a/test/clj/status_im/appium.clj b/test/clj/status_im/appium.clj new file mode 100644 index 0000000000..d3a77bc435 --- /dev/null +++ b/test/clj/status_im/appium.clj @@ -0,0 +1,86 @@ +(ns status-im.appium + (:require [clojure.java.io :as io] + [clojure.test :refer :all]) + (:import (org.openqa.selenium.remote DesiredCapabilities) + (org.openqa.selenium By) + (io.appium.java_client.android AndroidDriver) + (java.net URL) + (java.util.concurrent TimeUnit))) + + +(defn init [] + (let [dir (io/file (str (System/getProperty "user.dir") + "/android/app/build/outputs/apk")) + app (io/file dir "app-debug.apk") + capabilities (doto (DesiredCapabilities.) + (.setCapability "deviceName" "device") + (.setCapability "platformVersion" "6.0.0") + (.setCapability "app" (.getAbsolutePath app)) + (.setCapability "appPackage" "com.statusim") + (.setCapability "appActivity" ".MainActivity")) + driver (AndroidDriver. (URL. "http://127.0.0.1:4723/wd/hub") capabilities)] + (-> driver + .manage + .timeouts + (.implicitlyWait 25 TimeUnit/SECONDS)) + driver)) + +(defn by-xpath [driver xpath] + (.findElement driver (By/xpath xpath))) + +(defn elements-by-xpath [driver xpath] + (.findElements driver (By/xpath xpath))) + +(defn by-id [driver id] + (.findElementByAccessibilityId driver (name id))) + +(defn get-element [driver id] + (if (keyword? id) + (by-id driver id) + (by-xpath driver id))) + +(defn click [driver id] + (.click (get-element driver id))) + +(defn write [driver input-xpath text] + (.sendKeys (get-element driver input-xpath) (into-array [text]))) + +(defn get-text [driver xpath] + (.getText (by-xpath driver xpath))) + +(defn xpath-by-text [text] + (str ".//*[@text='" text "']")) + +(defn contains-text [driver text] + (is (= 1 (->> (xpath-by-text text) + (elements-by-xpath driver) + (.size))))) + +(defn quit [driver] + (.quit driver)) + +(defmacro appium-test + "Defines test which will create new appium session and will pass that + session as first argument to each command inside it's body. After execution + of all commands session will be closed. + + For instance, + + (appium-test my-test + (click :button) + (write :input \"oops\")) + + will be expanded to + + (deftest my-test + (let [session (init)] + (click session :button) + (write session :input \"oops\") + (quit session)))" + [name & body] + (let [sym (gensym)] + `(deftest ~name + (let [~sym (init)] + ~@(for [[f & rest] body] + `(~f ~sym ~@rest)) + (quit ~sym))))) diff --git a/test/clj/status_im/console.clj b/test/clj/status_im/console.clj new file mode 100644 index 0000000000..5cdf17ae45 --- /dev/null +++ b/test/clj/status_im/console.clj @@ -0,0 +1,15 @@ +(ns status-im.console + (:require [clojure.test :refer :all] + [status-im.appium :refer :all])) + +(def message-text + (str "Your phone number is also required to use the app. Type" + " the exclamation mark or hit the icon to open the command " + "list and choose the !phone command")) + +(appium-test console-test + (click :request-keypair-password) + (write :command-input "123") + (click :stage-command) + (click :send-message) + (contains-text message-text)) diff --git a/test/cljs/status_im/test/handlers.cljs b/test/cljs/status_im/test/handlers.cljs new file mode 100644 index 0000000000..70f4ae44c6 --- /dev/null +++ b/test/cljs/status_im/test/handlers.cljs @@ -0,0 +1,6 @@ +(ns status-im.test.handlers + (:require [cljs.test :refer-macros [deftest is]] + [status-im.handlers :as h])) + +(deftest test-set-val + (is (= {:key :val} (h/set-el {} [nil :key :val])))) diff --git a/test/cljs/status_im/test/runner.cljs b/test/cljs/status_im/test/runner.cljs new file mode 100644 index 0000000000..14bda7de00 --- /dev/null +++ b/test/cljs/status_im/test/runner.cljs @@ -0,0 +1,5 @@ +(ns status-im.test.runner + (:require [doo.runner :refer-macros [doo-tests]] + [status-im.test.handlers])) + +(doo-tests 'status-im.test.handlers) diff --git a/test/status_im/core_test.clj b/test/status_im/core_test.clj deleted file mode 100644 index 1224033d1c..0000000000 --- a/test/status_im/core_test.clj +++ /dev/null @@ -1,7 +0,0 @@ -(ns status-im.core-test - (:require [clojure.test :refer :all] - [status-im.core :refer :all])) - -(deftest a-test - (testing "FIXME, I fail." - (is (= 0 1))))