diff --git a/src/syng_im/android/core.cljs b/src/syng_im/android/core.cljs index 8bee230a48..b26063092b 100644 --- a/src/syng_im/android/core.cljs +++ b/src/syng_im/android/core.cljs @@ -2,30 +2,24 @@ (:require-macros [natal-shell.back-android :refer [add-event-listener remove-event-listener]]) (:require [reagent.core :as r :refer [atom]] - [cljs.core :as cljs] [re-frame.core :refer [subscribe dispatch dispatch-sync]] [syng-im.handlers] [syng-im.subs] [syng-im.components.react :refer [navigator app-registry]] - [syng-im.components.contact-list.contact-list :refer [contact-list]] - [syng-im.components.discovery.discovery :refer [discovery]] - [syng-im.components.discovery.discovery-tag :refer [discovery-tag]] - [syng-im.components.chat :refer [chat]] - [syng-im.components.chats.chats-list :refer [chats-list]] - [syng-im.components.chats.new-group :refer [new-group]] - [syng-im.components.chat.new-participants :refer [new-participants]] - [syng-im.components.chat.remove-participants :refer [remove-participants]] + [syng-im.contacts.screen :refer [contact-list]] + [syng-im.discovery.screen :refer [discovery]] + [syng-im.discovery.tag :refer [discovery-tag]] + [syng-im.chat.screen :refer [chat]] + [syng-im.chats-list.screen :refer [chats-list]] + [syng-im.new-group.screen :refer [new-group]] + [syng-im.participants.views.create :refer [new-participants]] + [syng-im.participants.views.remove :refer [remove-participants]] [syng-im.group-settings.group-settings :refer [group-settings]] [syng-im.group-settings.views.chat-name-edit :refer [chat-name-edit]] - [syng-im.components.profile :refer [profile my-profile]] - [syng-im.utils.logging :as log] + [syng-im.profile.screen :refer [profile my-profile]] [syng-im.utils.utils :refer [toast]] - [syng-im.navigation :as nav] [syng-im.utils.encryption])) -(def back-button-handler (cljs/atom {:nav nil - :handler nil})) - (defn init-back-button-handler! [] (let [new-listener (fn [] ;; todo: it might be better always return false from @@ -39,7 +33,7 @@ (defn app-root [] (let [signed-up (subscribe [:signed-up]) - view-id (subscribe [:view-id])] + view-id (subscribe [:view-id])] (fn [] (case (if @signed-up @view-id :chat) :discovery [discovery] @@ -61,7 +55,7 @@ (dispatch [:initialize-chats]) (dispatch [:initialize-protocol]) (dispatch [:load-user-phone-number]) - (dispatch [:load-syng-contacts]) + (dispatch [:load-contacts]) ;; load commands from remote server (todo: uncomment) ;; (dispatch [:load-commands]) (dispatch [:init-console-chat]) diff --git a/src/syng_im/chat/handlers.cljs b/src/syng_im/chat/handlers.cljs new file mode 100644 index 0000000000..b8c65037fb --- /dev/null +++ b/src/syng_im/chat/handlers.cljs @@ -0,0 +1,346 @@ +(ns syng-im.chat.handlers + (:require [re-frame.core :refer [register-handler enrich after debug dispatch]] + [syng-im.models.commands :as commands] + [clojure.string :as str] + [syng-im.chat.suggestions :as suggestions] + [syng-im.protocol.api :as api] + [syng-im.models.messages :as messages] + [syng-im.constants :refer [text-content-type + content-type-command]] + [syng-im.utils.random :as random] + [syng-im.components.react :as r] + [syng-im.handlers.sign-up :as sign-up-service] + [syng-im.models.chats :as chats] + [syng-im.navigation.handlers :as nav] + [syng-im.models.chats :as c])) + +(register-handler :set-show-actions + (fn [db [_ show-actions]] + (assoc db :show-actions show-actions))) + +(register-handler :load-more-messages + (fn [db _] + db + ;; TODO implement + #_(let [chat-id (get-in db [:chat :current-chat-id]) + messages [:chats chat-id :messages] + new-messages (gen-messages 10)] + (update-in db messages concat new-messages)))) + +(defn safe-trim [s] + (when (string? s) + (str/trim s))) + +(register-handler :cancel-command + (fn [{:keys [current-chat-id] :as db} _] + (-> db + (assoc-in [:chats current-chat-id :command-input] {}) + (update-in [:chats current-chat-id :input-text] safe-trim)))) + +(register-handler :set-chat-command-content + (fn [db [_ content]] + (commands/set-chat-command-content db content))) + +(defn update-input-text + [{:keys [current-chat-id] :as db} text] + (assoc-in db [:chats current-chat-id :input-text] text)) + +(register-handler :stage-command + (fn [{:keys [current-chat-id] :as db} _] + (let [db (update-input-text db nil) + {:keys [command content]} + (get-in db [:chats current-chat-id :command-input]) + command-info {:command command + :content content + :handler (:handler command)}] + (commands/stage-command db command-info)))) + +(register-handler :set-response-chat-command + (fn [db [_ to-msg-id command-key]] + (commands/set-response-chat-command db to-msg-id command-key))) + +(defn update-text + [db [_ text]] + (update-input-text db text)) + +(defn update-command [db [_ text]] + (let [{:keys [command]} (suggestions/check-suggestion db text)] + (commands/set-chat-command db command))) + +(register-handler :set-chat-input-text + ((enrich update-command) update-text)) + +(register-handler :send-group-chat-msg + (fn [db [_ chat-id text]] + (let [{msg-id :msg-id + {from :from} :msg} (api/send-group-user-msg {:group-id chat-id + :content text}) + msg {:msg-id msg-id + :from from + :to nil + :content text + :content-type text-content-type + :outgoing true}] + (messages/save-message chat-id msg)))) + +(defn console? [s] + (= "console" s)) + +(def not-console? + (complement console?)) + +(defn check-author-direction + [db chat-id {:keys [from outgoing] :as message}] + (let [previous-message (first (get-in db [:chats chat-id :messages]))] + (merge message + {:same-author (if previous-message + (= (:from previous-message) from) + true) + :same-direction (if previous-message + (= (:outgoing previous-message) outgoing) + true)}))) + +(defn add-message-to-db + [db chat-id message] + (let [messages [:chats chat-id :messages]] + (update-in db messages conj message))) + +(defn prepare-message + [{:keys [identity current-chat-id] :as db} _] + (let [text (get-in db [:chats current-chat-id :input-text]) + {:keys [command]} (suggestions/check-suggestion db (str text " ")) + message (check-author-direction + db current-chat-id + {:msg-id (random/id) + :chat-id current-chat-id + :content text + :to current-chat-id + :from identity + :content-type text-content-type + :outgoing true})] + (if command + (commands/set-chat-command db command) + (assoc db :new-message (when-not (str/blank? text) message))))) + +(defn prepare-command [identity chat-id staged-command] + (let [command-key (get-in staged-command [:command :command]) + content {:command (name command-key) + :content (:content staged-command)}] + {:msg-id (random/id) + :from identity + :to chat-id + :content content + :content-type content-type-command + :outgoing true + :handler (:handler staged-command)})) + +(defn prepare-staged-commans + [{:keys [current-chat-id identity] :as db} _] + (let [staged-commands (get-in db [:chats current-chat-id :staged-commands])] + (->> staged-commands + (map #(prepare-command identity current-chat-id %)) + ;todo this is wrong :( + (map #(check-author-direction db current-chat-id %)) + (assoc db :new-commands)))) + +(defn add-message + [{:keys [new-message current-chat-id] :as db}] + (if new-message + (add-message-to-db db current-chat-id new-message) + db)) + +(defn add-commands + [{:keys [new-commands current-chat-id] :as db}] + (reduce + #(add-message-to-db %1 current-chat-id %2) + db + new-commands)) + +(defn clear-input + [{:keys [current-chat-id new-message] :as db} _] + (if new-message + (assoc-in db [:chats current-chat-id :input-text] nil) + db)) + +(defn clear-staged-commands + [{:keys [current-chat-id] :as db} _] + (assoc-in db [:chats current-chat-id :staged-commands] [])) + +(defn send-message! + [{:keys [new-message current-chat-id]} _] + (when (and new-message (not-console? current-chat-id)) + (api/send-user-msg {:to current-chat-id + :content (:content new-message)}))) + +(defn save-message-to-realm! + [{:keys [new-message current-chat-id]} _] + (when new-message + (messages/save-message current-chat-id new-message))) + +(defn save-commands-to-realm! + [{:keys [new-commands current-chat-id]} _] + (doseq [new-command new-commands] + (messages/save-message current-chat-id (dissoc new-command :handler)))) + +(defn handle-commands + [{:keys [new-commands]}] + (doseq [{{content :content} :content + handler :handler} new-commands] + (when handler + (handler content)))) + +(register-handler :send-chat-msg + (-> prepare-message + ((enrich prepare-staged-commans)) + ((enrich add-message)) + ((enrich add-commands)) + ((enrich clear-input)) + ((enrich clear-staged-commands)) + ((after (fn [_ _] (r/dismiss-keyboard!)))) + ((after send-message!)) + ((after save-message-to-realm!)) + ((after save-commands-to-realm!)) + ((after handle-commands)))) + +(register-handler :unstage-command + (fn [db [_ staged-command]] + (let [] + (commands/unstage-command db staged-command)))) + +(register-handler :set-chat-command + (fn [db [_ command-key]] + ;; todo what is going on there?! + (commands/set-chat-command db command-key))) + +(register-handler :init-console-chat + (fn [db [_]] + (sign-up-service/init db))) + +(register-handler :save-password + (fn [db [_ password]] + (sign-up-service/save-password password) + (assoc db :password-saved true))) + +(register-handler :sign-up + (-> (fn [db [_ phone-number]] + ;; todo save phone number to db + (assoc db :user-phone-number phone-number)) + ((after (fn [& _] (sign-up-service/on-sign-up-response)))))) + +(register-handler :sign-up-confirm + (fn [db [_ confirmation-code]] + (sign-up-service/on-send-code-response confirmation-code) + (sign-up-service/set-signed-up db true))) + +(register-handler :set-signed-up + (fn [db [_ signed-up]] + (sign-up-service/set-signed-up db signed-up))) + + +(defn load-messages! + ([db] (load-messages! db nil)) + ([db _] + (->> (:current-chat-id db) + messages/get-messages + (assoc db :messages)))) + +(defn init-chat + ([db] (init-chat db nil)) + ([{:keys [messages current-chat-id] :as db} _] + (assoc-in db [:chats current-chat-id :messages] messages))) + +(register-handler :init-chat + (-> load-messages! + ((enrich init-chat)) + debug)) + +(defn initialize-chats + [{:keys [loaded-chats] :as db} _] + (let [chats (->> loaded-chats + (map (fn [{:keys [chat-id] :as chat}] + [chat-id chat])) + (into {})) + ids (set (keys chats))] + (-> db + (assoc :chats chats) + (assoc :chats-ids ids) + (dissoc :loaded-chats)))) + +(defn load-chats! + [db _] + (assoc db :loaded-chats (chats/chats-list))) + +(register-handler :initialize-chats + ((enrich initialize-chats) load-chats!)) + +(defn store-message! + [{:keys [new-message]} [_ {chat-id :from}]] + (messages/save-message chat-id new-message)) + +(defn receive-message + [db [_ {chat-id :from :as message}]] + (let [message' (check-author-direction db chat-id message)] + (-> db + (add-message-to-db chat-id message') + (assoc :new-message message')))) + +(register-handler :received-msg + (-> receive-message + ((after store-message!)))) + +(register-handler :group-received-msg + (fn [db [_ {chat-id :group-id :as msg}]] + (messages/save-message chat-id msg) + db)) + +(defn load-chat! + [{:keys [chats current-chat-id] :as db}] + (when-not (chats current-chat-id) + (c/create-chat {})) + db) + +(defmethod nav/preload-data! :chat + [{:keys [current-chat-id] :as db} [_ _ id]] + (-> db + (assoc :current-chat-id (or id current-chat-id)) + load-messages! + init-chat)) + +(defn prepare-chat + [{:keys [contacts] :as db} [_ contcat-id]] + (let [name (get-in contacts [contcat-id :name]) + chat {:chat-id contcat-id + :name name + :group-chat false + :is-active true + :timestamp (.getTime (js/Date.)) + ;; todo how to choose color? + ;; todo do we need to have some color for not group chat? + :contacts [{:identity contcat-id + :text-color :#FFFFFF + :background-color :#AB7967}]}] + (assoc db :new-chat chat))) + +(defn add-chat [{:keys [new-chat] :as db} [_ chat-id]] + (-> db + (update :chats assoc chat-id new-chat) + (update :chats-ids conj chat-id))) + +(defn save-chat! + [{:keys [new-chat]} _] + (chats/create-chat new-chat)) + +(defn open-chat! + [_ [_ chat-id]] + (dispatch [:navigate-to :chat chat-id])) + +(register-handler :start-chat + (-> prepare-chat + ((enrich add-chat)) + ((after save-chat!)) + ((after open-chat!)) + debug)) + +(register-handler :switch-command-suggestions + (fn [db [_]] + (suggestions/switch-command-suggestions db))) diff --git a/src/syng_im/components/chat.cljs b/src/syng_im/chat/screen.cljs similarity index 80% rename from src/syng_im/components/chat.cljs rename to src/syng_im/chat/screen.cljs index 8411cb580f..1121e99dab 100644 --- a/src/syng_im/components/chat.cljs +++ b/src/syng_im/chat/screen.cljs @@ -1,25 +1,22 @@ -(ns syng-im.components.chat +(ns syng-im.chat.screen + (:require-macros [syng-im.utils.views :refer [defview]]) (:require [clojure.string :as s] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [re-frame.core :refer [subscribe dispatch]] [syng-im.components.react :refer [view text image icon - navigator touchable-highlight list-view - list-item - android?]] - [syng-im.components.chat-styles :as st] - [syng-im.utils.logging :as log] + list-item]] + [syng-im.chat.styles.screen :as st] [syng-im.resources :as res] - [syng-im.constants :refer [content-type-status]] - [syng-im.utils.listview :refer [to-datasource - to-datasource2]] + [syng-im.utils.listview :refer [to-datasource]] [syng-im.components.invertible-scroll-view :refer [invertible-scroll-view]] [syng-im.components.toolbar :refer [toolbar]] - [syng-im.components.chat.chat-message :refer [chat-message]] - [syng-im.components.chat.chat-message-new :refer [chat-message-new]])) + [syng-im.chat.views.message :refer [chat-message]] + [syng-im.chat.views.new-message :refer [chat-message-new]])) + (defn contacts-by-identity [contacts] (->> contacts @@ -29,8 +26,8 @@ (defn add-msg-color [{:keys [from] :as msg} contact-by-identity] (if (= "system" from) - (assoc msg :text-color "#4A5258" - :background-color "#D3EEEF") + (assoc msg :text-color :#4A5258 + :background-color :#D3EEEF) (let [{:keys [text-color background-color]} (get contact-by-identity from)] (assoc msg :text-color text-color :background-color background-color)))) @@ -70,9 +67,9 @@ (defn on-action-selected [position] (case position - 0 (dispatch [:show-add-participants #_navigator]) - 1 (dispatch [:show-remove-participants #_navigator]) - 2 (dispatch [:leave-group-chat #_navigator]))) + 0 (dispatch [:show-add-participants]) + 1 (dispatch [:show-remove-participants]) + 2 (dispatch [:leave-group-chat]))) (defn overlay [{:keys [on-click-outside]} items] [view st/actions-overlay @@ -216,25 +213,23 @@ :custom-content [toolbar-content] :custom-action [toolbar-action]}]))) -(defn messages-view [group-chat] - (let [messages (subscribe [:chat :messages]) - contacts (subscribe [:chat :contacts])] - (fn [group-chat] - (let [contacts' (contacts-by-identity @contacts)] - [list-view {:renderRow (message-row contacts' group-chat) - :renderScrollComponent #(invertible-scroll-view (js->clj %)) - :onEndReached #(dispatch [:load-more-messages]) - :enableEmptySections true - :dataSource (to-datasource2 @messages)}])))) +(defview messages-view [group-chat] + [messages [:chat :messages] + contacts [:chat :contacts]] + (let [contacts' (contacts-by-identity contacts)] + [list-view {:renderRow (message-row contacts' group-chat) + :renderScrollComponent #(invertible-scroll-view (js->clj %)) + :onEndReached #(dispatch [:load-more-messages]) + :enableEmptySections true + :dataSource (to-datasource messages)}])) -(defn chat [] - (let [is-active (subscribe [:chat :is-active]) - group-chat (subscribe [:chat :group-chat]) - show-actions-atom (subscribe [:show-actions])] - (fn [] - [view st/chat-view - [chat-toolbar] - [messages-view @group-chat] - (when @group-chat [typing-all]) - (when is-active [chat-message-new]) - (when @show-actions-atom [actions-view])]))) +(defview chat [] + [is-active [:chat :is-active] + group-chat [:chat :group-chat] + show-actions-atom [:show-actions]] + [view st/chat-view + [chat-toolbar] + [messages-view group-chat] + (when group-chat [typing-all]) + (when is-active [chat-message-new]) + (when show-actions-atom [actions-view])]) diff --git a/src/syng_im/components/chat/content_suggestions_styles.cljs b/src/syng_im/chat/styles/content_suggestions.cljs similarity index 96% rename from src/syng_im/components/chat/content_suggestions_styles.cljs rename to src/syng_im/chat/styles/content_suggestions.cljs index c0169e8ea3..60c670fada 100644 --- a/src/syng_im/components/chat/content_suggestions_styles.cljs +++ b/src/syng_im/chat/styles/content_suggestions.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.chat.content-suggestions-styles +(ns syng-im.chat.styles.content-suggestions (:require [syng-im.components.styles :refer [font color-light-blue-transparent color-white diff --git a/src/syng_im/components/chat/input/input_styles.cljs b/src/syng_im/chat/styles/input.cljs similarity index 98% rename from src/syng_im/components/chat/input/input_styles.cljs rename to src/syng_im/chat/styles/input.cljs index 7079f4d101..dcbc893f86 100644 --- a/src/syng_im/components/chat/input/input_styles.cljs +++ b/src/syng_im/chat/styles/input.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.chat.input.input-styles +(ns syng-im.chat.styles.input (:require [syng-im.components.styles :refer [font color-white color-blue diff --git a/src/syng_im/components/chat/chat_message_styles.cljs b/src/syng_im/chat/styles/message.cljs similarity index 95% rename from src/syng_im/components/chat/chat_message_styles.cljs rename to src/syng_im/chat/styles/message.cljs index c336fbb8d5..356419841b 100644 --- a/src/syng_im/components/chat/chat_message_styles.cljs +++ b/src/syng_im/chat/styles/message.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.chat.chat-message-styles +(ns syng-im.chat.styles.message (:require [syng-im.components.styles :refer [font color-light-blue-transparent color-white @@ -312,18 +312,3 @@ (def new-message-container {:backgroundColor color-white :elevation 4}) - -(def participants-container - {:flex 1 - :backgroundColor :white}) - -(def participants-list - {:backgroundColor :white}) - -(def new-participant-image - {:width 20 - :height 18}) - -(def remove-participants-image - {:width 22 - :height 30}) diff --git a/src/syng_im/components/chat/plain_message_input_styles.cljs b/src/syng_im/chat/styles/plain_input.cljs similarity index 95% rename from src/syng_im/components/chat/plain_message_input_styles.cljs rename to src/syng_im/chat/styles/plain_input.cljs index 8e31264837..a3a85967a2 100644 --- a/src/syng_im/components/chat/plain_message_input_styles.cljs +++ b/src/syng_im/chat/styles/plain_input.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.chat.plain-message-input-styles +(ns syng-im.chat.styles.plain-input (:require [syng-im.components.styles :refer [font text2-color color-white diff --git a/src/syng_im/components/chat_styles.cljs b/src/syng_im/chat/styles/screen.cljs similarity index 99% rename from src/syng_im/components/chat_styles.cljs rename to src/syng_im/chat/styles/screen.cljs index 43a7aeb373..f3e74ac123 100644 --- a/src/syng_im/components/chat_styles.cljs +++ b/src/syng_im/chat/styles/screen.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.chat-styles +(ns syng-im.chat.styles.screen (:require [syng-im.components.styles :refer [font title-font color-white diff --git a/src/syng_im/components/chat/suggestions_styles.cljs b/src/syng_im/chat/styles/suggestions.cljs similarity index 97% rename from src/syng_im/components/chat/suggestions_styles.cljs rename to src/syng_im/chat/styles/suggestions.cljs index 1e3a1fb352..7eeae72192 100644 --- a/src/syng_im/components/chat/suggestions_styles.cljs +++ b/src/syng_im/chat/styles/suggestions.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.chat.suggestions-styles +(ns syng-im.chat.styles.suggestions (:require [syng-im.components.styles :refer [font color-light-blue-transparent color-white diff --git a/src/syng_im/chat/subs.cljs b/src/syng_im/chat/subs.cljs new file mode 100644 index 0000000000..6e20718820 --- /dev/null +++ b/src/syng_im/chat/subs.cljs @@ -0,0 +1,94 @@ +(ns syng-im.chat.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]] + [syng-im.db :as db] + ;todo handlers in subs?... + [syng-im.chat.suggestions :refer + [get-suggestions typing-command? get-content-suggestions]] + [syng-im.models.commands :as commands] + [syng-im.handlers.content-suggestions :refer [get-content-suggestions]])) + +(register-sub :chat-properties + (fn [db [_ properties]] + (->> properties + (map (fn [k] + [k (-> @db + (get-in [:chats (:current-chat-id @db) k]) + (reaction))])) + (into {})))) + +(register-sub + :show-actions + (fn [db _] + (reaction (:show-actions @db)))) + +(register-sub :chat + (fn [db [_ k]] + (-> @db + (get-in [:chats (:current-chat-id @db) k]) + (reaction)))) + + +(register-sub :get-chat-messages + (fn [db _] + (let [chat-id (:current-chat-id @db)] + (reaction (get-in @db [:chats chat-id :messages]))))) + +(register-sub :get-current-chat-id + (fn [db _] + (reaction (:current-chat-id @db)))) + +(register-sub :get-suggestions + (fn [db _] + (let [input-text (->> (:current-chat-id @db) + db/chat-input-text-path + (get-in @db) + (reaction))] + (reaction (get-suggestions @db @input-text))))) + +(register-sub :get-commands + (fn [db _] + (reaction (commands/get-commands @db)))) + +(register-sub :get-chat-input-text + (fn [db _] + (->> [:chats (:current-chat-id @db) :input-text] + (get-in @db) + (reaction)))) + +(register-sub :get-chat-staged-commands + (fn [db _] + (->> [:chats (:current-chat-id @db) :staged-commands] + (get-in @db) + (reaction)))) + +(register-sub :get-chat-command + (fn [db _] + (reaction (commands/get-chat-command @db)))) + +(register-sub :get-chat-command-content + (fn [db _] + (reaction (commands/get-chat-command-content @db)))) + +(register-sub :chat-command-request + (fn [db _] + (reaction (commands/get-chat-command-request @db)))) + +(register-sub :get-current-chat + (fn [db _] + (let [current-chat-id (:current-chat-id @db)] + (reaction (get-in @db [:chats current-chat-id]))))) + +(register-sub :get-chat + (fn [db [_ chat-id]] + (reaction (get-in @db [:chats chat-id])))) + +(register-sub :typing-command? + (fn [db _] + (reaction (typing-command? @db)))) + +(register-sub :get-content-suggestions + (fn [db _] + (let [command (reaction (commands/get-chat-command @db)) + text (reaction (commands/get-chat-command-content @db))] + (reaction (get-content-suggestions @db @command @text))))) diff --git a/src/syng_im/handlers/suggestions.cljs b/src/syng_im/chat/suggestions.cljs similarity index 85% rename from src/syng_im/handlers/suggestions.cljs rename to src/syng_im/chat/suggestions.cljs index 4b227940bb..d5a04d50f1 100644 --- a/src/syng_im/handlers/suggestions.cljs +++ b/src/syng_im/chat/suggestions.cljs @@ -1,9 +1,6 @@ -(ns syng-im.handlers.suggestions +(ns syng-im.chat.suggestions (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] [syng-im.db :as db] - [syng-im.models.chat :refer [current-chat-id - set-chat-input-text - get-chat-input-text]] [syng-im.models.commands :refer [commands suggestions get-commands @@ -11,7 +8,6 @@ get-chat-command-to-msg-id clear-staged-commands]] [syng-im.utils.utils :refer [log on-error http-get]] - [syng-im.utils.logging :as log] [clojure.string :as s])) (defn suggestion? [text] @@ -41,7 +37,8 @@ (command-handler to-msg-id command-key content))))) (defn apply-staged-commands [db] - (let [staged-commands (get-in db (db/chat-staged-commands-path (current-chat-id db)))] + (let [staged-commands (get-in db (db/chat-staged-commands-path + (:current-chat-id db)))] (dorun (map (fn [staged-command] (when-let [handler (:handler staged-command)] (handler))) @@ -66,8 +63,10 @@ suggestion))) (defn typing-command? [db] - (let [text (get-chat-input-text db)] - (suggestion? text))) + (-> db + (get-in [:chats (:current-chat-id db) :input-text]) + suggestion?)) (defn switch-command-suggestions [db] - (set-chat-input-text db (if (typing-command? db) nil "!"))) + (let [text (if (typing-command? db) nil "!")] + (assoc-in db [:chats (:current-chat-id db) :input-text] text))) diff --git a/src/syng_im/components/chat/input/simple_command.cljs b/src/syng_im/chat/views/command.cljs similarity index 85% rename from src/syng_im/components/chat/input/simple_command.cljs rename to src/syng_im/chat/views/command.cljs index 31292f8df5..688e3d0736 100644 --- a/src/syng_im/components/chat/input/simple_command.cljs +++ b/src/syng_im/chat/views/command.cljs @@ -1,13 +1,13 @@ -(ns syng-im.components.chat.input.simple-command - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] +(ns syng-im.chat.views.command + (:require [re-frame.core :refer [subscribe dispatch]] [syng-im.components.react :refer [view icon text text-input touchable-highlight]] - [syng-im.resources :as res] - [syng-im.components.chat.content-suggestions :refer [content-suggestions-view]] - [syng-im.components.chat.input.input-styles :as st])) + [syng-im.chat.views.content-suggestions :refer + [content-suggestions-view]] + [syng-im.chat.styles.input :as st])) (defn cancel-command-input [] (dispatch [:cancel-command])) diff --git a/src/syng_im/chat/views/confirmation_code.cljs b/src/syng_im/chat/views/confirmation_code.cljs new file mode 100644 index 0000000000..9e98fb9c46 --- /dev/null +++ b/src/syng_im/chat/views/confirmation_code.cljs @@ -0,0 +1,6 @@ +(ns syng-im.chat.views.confirmation-code + (:require + [syng-im.chat.views.command :refer [simple-command-input-view]])) + +(defn confirmation-code-input-view [command] + [simple-command-input-view command {:keyboardType :numeric}]) diff --git a/src/syng_im/chat/views/content_suggestions.cljs b/src/syng_im/chat/views/content_suggestions.cljs new file mode 100644 index 0000000000..a2e67c8cfc --- /dev/null +++ b/src/syng_im/chat/views/content_suggestions.cljs @@ -0,0 +1,36 @@ +(ns syng-im.chat.views.content-suggestions + (:require-macros [syng-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch]] + [syng-im.components.react :refer [view + icon + text + touchable-highlight + list-view + list-item]] + [syng-im.chat.styles.content-suggestions :as st] + [syng-im.utils.listview :refer [to-datasource]])) + +(defn set-command-content [content] + (dispatch [:set-chat-command-content content])) + +(defn suggestion-list-item [{:keys [value description]}] + [touchable-highlight {:onPress #(set-command-content value)} + [view st/suggestion-container + [view st/suggestion-sub-container + [text {:style st/value-text} value] + [text {:style st/description-text} description]]]]) + +(defn render-row [row _ _] + (list-item [suggestion-list-item row])) + +(defview content-suggestions-view [] + [suggestions [:get-content-suggestions]] + (when (seq suggestions) + [view + [touchable-highlight {:style st/drag-down-touchable + ;; TODO hide suggestions? + :onPress (fn [])} + [view [icon :drag_down st/drag-down-icon]]] + [view (st/suggestions-container (count suggestions)) + [list-view {:dataSource (to-datasource suggestions) + :renderRow render-row}]]])) diff --git a/src/syng_im/components/chat/chat_message.cljs b/src/syng_im/chat/views/message.cljs similarity index 97% rename from src/syng_im/components/chat/chat_message.cljs rename to src/syng_im/chat/views/message.cljs index cbf794972b..b7fe165406 100644 --- a/src/syng_im/components/chat/chat_message.cljs +++ b/src/syng_im/chat/views/message.cljs @@ -1,11 +1,11 @@ -(ns syng-im.components.chat.chat-message +(ns syng-im.chat.views.message (:require [clojure.string :as s] [re-frame.core :refer [subscribe dispatch]] [syng-im.components.react :refer [view text image touchable-highlight]] - [syng-im.components.chat.chat-message-styles :as st] + [syng-im.chat.styles.message :as st] [syng-im.models.commands :refer [parse-command-msg-content parse-command-request]] [syng-im.resources :as res] @@ -112,8 +112,7 @@ (defn text-message [{:keys [content] :as message}] [message-view message - [text {:style (st/text-message message)} - content]]) + [text {:style (st/text-message message)} content]]) (defmethod message-content text-content-type [wrapper message] diff --git a/src/syng_im/chat/views/money.cljs b/src/syng_im/chat/views/money.cljs new file mode 100644 index 0000000000..4706009bfd --- /dev/null +++ b/src/syng_im/chat/views/money.cljs @@ -0,0 +1,7 @@ +(ns syng-im.chat.views.money + (:require + [syng-im.chat.views.command :refer [simple-command-input-view]])) + +(defn money-input-view [command] + [simple-command-input-view command + {:keyboardType :numeric}]) diff --git a/src/syng_im/components/chat/chat_message_new.cljs b/src/syng_im/chat/views/new_message.cljs similarity index 65% rename from src/syng_im/components/chat/chat_message_new.cljs rename to src/syng_im/chat/views/new_message.cljs index 545d0052b7..988e0de9d6 100644 --- a/src/syng_im/components/chat/chat_message_new.cljs +++ b/src/syng_im/chat/views/new_message.cljs @@ -1,15 +1,15 @@ -(ns syng-im.components.chat.chat-message-new +(ns syng-im.chat.views.new-message (:require [re-frame.core :refer [subscribe]] [syng-im.components.react :refer [view]] - [syng-im.components.chat.plain-message-input :refer [plain-message-input-view]] - [syng-im.components.chat.input.simple-command :refer [simple-command-input-view]] - [syng-im.components.chat.input.phone :refer [phone-input-view]] - [syng-im.components.chat.input.password :refer [password-input-view]] - [syng-im.components.chat.input.confirmation-code :refer [confirmation-code-input-view]] - [syng-im.components.chat.input.money :refer [money-input-view]] - [syng-im.components.chat.input.simple-command-staged :refer [simple-command-staged-view]] - [syng-im.components.chat.chat-message-styles :as st])) + [syng-im.chat.views.plain-input :refer [plain-message-input-view]] + [syng-im.chat.views.command :refer [simple-command-input-view]] + [syng-im.chat.views.phone :refer [phone-input-view]] + [syng-im.chat.views.password :refer [password-input-view]] + [syng-im.chat.views.confirmation-code :refer [confirmation-code-input-view]] + [syng-im.chat.views.money :refer [money-input-view]] + [syng-im.chat.views.staged-command :refer [simple-command-staged-view]] + [syng-im.chat.styles.message :as st])) (defn staged-command-view [stage-command] [simple-command-staged-view stage-command]) diff --git a/src/syng_im/components/chat/input/password.cljs b/src/syng_im/chat/views/password.cljs similarity index 62% rename from src/syng_im/components/chat/input/password.cljs rename to src/syng_im/chat/views/password.cljs index 8385fa2c1c..253464ce0c 100644 --- a/src/syng_im/components/chat/input/password.cljs +++ b/src/syng_im/chat/views/password.cljs @@ -1,6 +1,6 @@ -(ns syng-im.components.chat.input.password +(ns syng-im.chat.views.password (:require - [syng-im.components.chat.input.simple-command + [syng-im.chat.views.command :refer [simple-command-input-view]])) (defn password-input-view [command] diff --git a/src/syng_im/components/chat/input/phone.cljs b/src/syng_im/chat/views/phone.cljs similarity index 73% rename from src/syng_im/components/chat/input/phone.cljs rename to src/syng_im/chat/views/phone.cljs index 2ff0812b4a..2cf361bf32 100644 --- a/src/syng_im/components/chat/input/phone.cljs +++ b/src/syng_im/chat/views/phone.cljs @@ -1,6 +1,6 @@ -(ns syng-im.components.chat.input.phone +(ns syng-im.chat.views.phone (:require - [syng-im.components.chat.input.simple-command + [syng-im.chat.views.command :refer [simple-command-input-view]] [syng-im.utils.phone-number :refer [valid-mobile-number?]])) diff --git a/src/syng_im/components/chat/plain_message_input.cljs b/src/syng_im/chat/views/plain_input.cljs similarity index 89% rename from src/syng_im/components/chat/plain_message_input.cljs rename to src/syng_im/chat/views/plain_input.cljs index 7931c21b85..0feee3d72c 100644 --- a/src/syng_im/components/chat/plain_message_input.cljs +++ b/src/syng_im/chat/views/plain_input.cljs @@ -1,11 +1,11 @@ -(ns syng-im.components.chat.plain-message-input - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] +(ns syng-im.chat.views.plain-input + (:require [re-frame.core :refer [subscribe dispatch]] [syng-im.components.react :refer [view icon touchable-highlight text-input]] - [syng-im.components.chat.suggestions :refer [suggestions-view]] - [syng-im.components.chat.plain-message-input-styles :as st])) + [syng-im.chat.views.suggestions :refer [suggestions-view]] + [syng-im.chat.styles.plain-input :as st])) (defn set-input-message [message] (dispatch [:set-chat-input-text message])) diff --git a/src/syng_im/components/chat/input/simple_command_staged.cljs b/src/syng_im/chat/views/staged_command.cljs similarity index 90% rename from src/syng_im/components/chat/input/simple_command_staged.cljs rename to src/syng_im/chat/views/staged_command.cljs index a2e0b571ee..c7a0b50568 100644 --- a/src/syng_im/components/chat/input/simple_command_staged.cljs +++ b/src/syng_im/chat/views/staged_command.cljs @@ -1,11 +1,11 @@ -(ns syng-im.components.chat.input.simple-command-staged +(ns syng-im.chat.views.staged-command (:require [re-frame.core :refer [subscribe dispatch]] [syng-im.components.react :refer [view image text touchable-highlight]] [syng-im.resources :as res] - [syng-im.components.chat.input.input-styles :as st])) + [syng-im.chat.styles.input :as st])) (defn cancel-command-input [staged-command] (dispatch [:unstage-command staged-command])) diff --git a/src/syng_im/components/chat/suggestions.cljs b/src/syng_im/chat/views/suggestions.cljs similarity index 69% rename from src/syng_im/components/chat/suggestions.cljs rename to src/syng_im/chat/views/suggestions.cljs index cfef517fbe..854344ae43 100644 --- a/src/syng_im/components/chat/suggestions.cljs +++ b/src/syng_im/chat/views/suggestions.cljs @@ -1,6 +1,4 @@ -(ns syng-im.components.chat.suggestions - (:require-macros - [natal-shell.core :refer [with-error-view]]) +(ns syng-im.chat.views.suggestions (:require [re-frame.core :refer [subscribe dispatch]] [syng-im.components.react :refer [view text @@ -9,38 +7,38 @@ list-view list-item]] [syng-im.utils.listview :refer [to-datasource]] - [syng-im.components.chat.suggestions-styles :as st])) + [syng-im.chat.styles.suggestions :as st])) (defn set-command-input [command] (dispatch [:set-chat-command command])) -(defn suggestion-list-item [suggestion] +(defn suggestion-list-item + [{:keys [description command] + label :text + :as suggestion}] [touchable-highlight - {:onPress #(set-command-input (keyword (:command suggestion)))} + {:onPress #(set-command-input (keyword command))} [view st/suggestion-container [view st/suggestion-sub-container [view (st/suggestion-background suggestion) - [text {:style st/suggestion-text} - (:text suggestion)]] - [text {:style st/value-text} - (:text suggestion)] - [text {:style st/description-text} - (:description suggestion)]]]]) + [text {:style st/suggestion-text} label]] + [text {:style st/value-text} label] + [text {:style st/description-text} description]]]]) (defn render-row [row _ _] - (list-item [suggestion-list-item (js->clj row :keywordize-keys true)])) + (list-item [suggestion-list-item row])) (defn suggestions-view [] (let [suggestions-atom (subscribe [:get-suggestions])] (fn [] (let [suggestions @suggestions-atom] (when (seq suggestions) - [view nil + [view [touchable-highlight {:style st/drag-down-touchable :onPress (fn [] ;; TODO hide suggestions? )} - [view nil + [view [icon :drag_down st/drag-down-icon]]] [view (st/suggestions-container (count suggestions)) [list-view {:dataSource (to-datasource suggestions) diff --git a/src/syng_im/chats_list/screen.cljs b/src/syng_im/chats_list/screen.cljs new file mode 100644 index 0000000000..55c4457540 --- /dev/null +++ b/src/syng_im/chats_list/screen.cljs @@ -0,0 +1,52 @@ +(ns syng-im.chats-list.screen + (:require [re-frame.core :refer [subscribe dispatch]] + [syng-im.components.react :refer [list-view + list-item + view + text + image + touchable-highlight]] + [syng-im.utils.listview :refer [to-datasource]] + [reagent.core :as r] + [syng-im.chats-list.views.chat-list-item :refer [chat-list-item]] + [syng-im.components.action-button :refer [action-button + action-button-item]] + [syng-im.components.drawer.view :refer [drawer-view open-drawer]] + [syng-im.components.styles :refer [color-blue]] + [syng-im.components.toolbar :refer [toolbar]] + [syng-im.components.icons.ionicons :refer [icon]] + [syng-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" + :action {:image {:source {:uri :icon_search} + :style st/search-icon} + :handler (fn [])}}]) + +(defn chats-list [] + (let [chats (subscribe [:get :chats])] + (fn [] + [drawer-view + [view st/chats-container + [chats-list-toolbar] + [list-view {:dataSource (to-datasource (vals @chats)) + :renderRow (fn [row _ _] + (list-item [chat-list-item row])) + :style st/list-container}] + [action-button {:buttonColor color-blue} + [action-button-item + {:title "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" + :buttonColor :#1abc9c + :onPress #(dispatch [:show-group-new])} + [icon {:name :person-stalker + :style st/person-stalker-icon}]]]]]))) diff --git a/src/syng_im/chats_list/styles.cljs b/src/syng_im/chats_list/styles.cljs new file mode 100644 index 0000000000..5b37f27122 --- /dev/null +++ b/src/syng_im/chats_list/styles.cljs @@ -0,0 +1,147 @@ +(ns syng-im.chats-list.styles + (:require [syng-im.components.styles :refer [font + title-font + color-white + color-blue + online-color + text1-color + text2-color + new-messages-count-color]])) + +(def contact-photo-container + {:borderRadius 50}) + +(def contact-photo-image + {:borderRadius 50 + :width 40 + :height 40}) + +(def online-container + {:position :absolute + :top 24 + :left 24 + :width 20 + :height 20 + :borderRadius 50 + :backgroundColor online-color + :borderWidth 2 + :borderColor color-white}) + +(def online-dot + {:position :absolute + :top 6 + :width 4 + :height 4 + :borderRadius 50 + :backgroundColor color-white}) + +(def online-dot-left + (assoc online-dot :left 3)) + +(def online-dot-right + (assoc online-dot :left 9)) + +(def chat-container + {:flexDirection :row + :paddingVertical 15 + :paddingHorizontal 16 + :height 90}) + +(def photo-container + {:marginTop 2 + :width 44 + :height 44}) + +(def item-container + {:flexDirection :column + :marginLeft 12 + :flex 1}) + +(def name-view + {:flexDirection :row}) + +(def name-text + {:marginTop -2.5 + :color text1-color + :fontSize 14 + :fontFamily title-font}) + +(def group-icon + {:marginTop 4 + :marginLeft 8 + :width 14 + :height 9}) + +(def memebers-text + {:marginTop -0.5 + :marginLeft 4 + :fontFamily font + :fontSize 12 + :color text2-color}) + +(def last-message-text + {:marginTop 7 + :marginRight 40 + :color text1-color + :fontFamily font + :fontSize 14 + :lineHeight 20}) + +(def status-container + {:flexDirection :row + :position :absolute + :top 0 + :right 0}) + +(def status-image + {:marginTop 6 + :width 9 + :height 7}) + +(def datetime-text + {:fontFamily font + :fontSize 12 + :color text2-color + :marginLeft 5}) + +(def new-messages-container + {:position :absolute + :top 36 + :right 0 + :width 24 + :height 24 + :backgroundColor new-messages-count-color + :borderRadius 50}) + +(def new-messages-text + {:top 4 + :left 0 + :fontFamily title-font + :fontSize 10 + :color color-blue + :textAlign :center}) + +(def hamburger-icon + {:width 16 + :height 12}) + +(def search-icon + {:width 17 + :height 17}) + +(def chats-container + {:flex 1 + :backgroundColor :white}) + +(def list-container + {:backgroundColor :white}) + +(def create-icon + {:fontSize 20 + :height 22 + :color :white}) + +(def person-stalker-icon + {:fontSize 20 + :height 22 + :color :white}) diff --git a/src/syng_im/chats_list/views/chat_list_item.cljs b/src/syng_im/chats_list/views/chat_list_item.cljs new file mode 100644 index 0000000000..3673701ccc --- /dev/null +++ b/src/syng_im/chats_list/views/chat_list_item.cljs @@ -0,0 +1,20 @@ +(ns syng-im.chats-list.views.chat-list-item + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.components.react :refer [view + text + image + touchable-highlight]] + [syng-im.components.styles :refer [font]] + [syng-im.chats-list.views.inner-item :refer + [chat-list-item-inner-view]])) + +(defn chat-list-item [{:keys [chat-id] :as chat}] + [touchable-highlight + {:on-press #(dispatch [:show-chat chat-id :push])} + ;; TODO add [photo-path delivery-status new-messages-count online] values to chat-obj + [view [chat-list-item-inner-view (merge chat + {:photo-path nil + :delivery-status :seen + :new-messages-count 3 + :timestamp "13:54" + :online true})]]]) diff --git a/src/syng_im/chats_list/views/inner_item.cljs b/src/syng_im/chats_list/views/inner_item.cljs new file mode 100644 index 0000000000..0e43c75426 --- /dev/null +++ b/src/syng_im/chats_list/views/inner_item.cljs @@ -0,0 +1,52 @@ +(ns syng-im.chats-list.views.inner-item + (:require [clojure.string :as s] + [syng-im.components.react :refer [view image icon text]] + [syng-im.chats-list.styles :as st] + [syng-im.resources :as res])) + + +(defn contact-photo [photo-path] + [view st/contact-photo-container + [image {:source (if (s/blank? photo-path) + res/user-no-photo + {:uri photo-path}) + :style st/contact-photo-image}]]) + +(defn contact-online [online] + (when online + [view st/online-container + [view st/online-dot-left] + [view st/online-dot-right]])) + +(defn chat-list-item-inner-view + [{:keys [name photo-path delivery-status timestamp new-messages-count online + group-chat contacts]}] + [view st/chat-container + [view st/photo-container + [contact-photo photo-path] + [contact-online online]] + [view st/item-container + [view st/name-view + [text {:style st/name-text} name] + (when group-chat + [icon :group st/group-icon]) + (when group-chat + [text {:style st/memebers-text} + (if (< 1 (count contacts)) + (str (count contacts) " members") + "1 member")])] + [text {:style st/last-message-text + :numberOfLines 2} + (repeatedly 5 #(str "Hi, I'm " name "! "))]] + [view + [view st/status-container + (when delivery-status + [image {:source (if (= (keyword delivery-status) :seen) + {:uri :icon_ok_small} + ;; todo change icon + {:uri :icon_ok_small}) + :style st/status-image}]) + [text {:style st/datetime-text} timestamp]] + (when (pos? new-messages-count) + [view st/new-messages-container + [text {:style st/new-messages-text} new-messages-count]])]]) diff --git a/src/syng_im/components/carousel/carousel.cljs b/src/syng_im/components/carousel/carousel.cljs index b489482f5d..28831eb3ec 100644 --- a/src/syng_im/components/carousel/carousel.cljs +++ b/src/syng_im/components/carousel/carousel.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.carousel +(ns syng-im.components.carousel.carousel (:require [syng-im.components.react :refer [android? view scroll-view diff --git a/src/syng_im/components/carousel/styles.cljs b/src/syng_im/components/carousel/styles.cljs index def9de8c10..b9a05aaff1 100644 --- a/src/syng_im/components/carousel/styles.cljs +++ b/src/syng_im/components/carousel/styles.cljs @@ -19,6 +19,6 @@ (defn page [page-width margin] {:width page-width - :justifyContent "center" + :justifyContent :center :marginLeft margin - :marginRight margin}) \ No newline at end of file + :marginRight margin}) diff --git a/src/syng_im/components/chat/content_suggestions.cljs b/src/syng_im/components/chat/content_suggestions.cljs deleted file mode 100644 index e79a88bc6f..0000000000 --- a/src/syng_im/components/chat/content_suggestions.cljs +++ /dev/null @@ -1,50 +0,0 @@ -(ns syng-im.components.chat.content-suggestions - (:require-macros - [natal-shell.core :refer [with-error-view]]) - (:require [clojure.string :as cstr] - [reagent.core :as r] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view - icon - text - touchable-highlight - list-view - list-item]] - [syng-im.components.chat.content-suggestions-styles :as st] - [syng-im.utils.listview :refer [to-datasource]] - [syng-im.utils.utils :refer [log toast http-post]] - [syng-im.utils.logging :as log])) - -(defn set-command-content [content] - (dispatch [:set-chat-command-content content])) - -(defn suggestion-list-item [suggestion] - [touchable-highlight {:onPress (fn [] - (set-command-content (:value suggestion))) - :underlay-color :transparent} - [view st/suggestion-container - [view st/suggestion-sub-container - [text {:style st/value-text} - (:value suggestion)] - [text {:style st/description-text} - (:description suggestion)]]]]) - -(defn render-row [row section-id row-id] - (list-item [suggestion-list-item (js->clj row :keywordize-keys true)])) - -(defn content-suggestions-view [] - (let [suggestions-atom (subscribe [:get-content-suggestions])] - (fn [] - (let [suggestions @suggestions-atom] - (when (seq suggestions) - [view nil - [touchable-highlight {:style st/drag-down-touchable - :onPress (fn [] - ;; TODO hide suggestions? - ) - :underlay-color :transparent} - [view nil - [icon :drag_down st/drag-down-icon]]] - [view (st/suggestions-container (count suggestions)) - [list-view {:dataSource (to-datasource suggestions) - :renderRow render-row}]]]))))) diff --git a/src/syng_im/components/chat/input/confirmation_code.cljs b/src/syng_im/components/chat/input/confirmation_code.cljs deleted file mode 100644 index 7ba41043d6..0000000000 --- a/src/syng_im/components/chat/input/confirmation_code.cljs +++ /dev/null @@ -1,7 +0,0 @@ -(ns syng-im.components.chat.input.confirmation-code - (:require - [syng-im.components.chat.input.simple-command - :refer [simple-command-input-view]])) - -(defn confirmation-code-input-view [command] - [simple-command-input-view command {:keyboardType :numeric}]) diff --git a/src/syng_im/components/chat/input/money.cljs b/src/syng_im/components/chat/input/money.cljs deleted file mode 100644 index ac85a9fc9e..0000000000 --- a/src/syng_im/components/chat/input/money.cljs +++ /dev/null @@ -1,9 +0,0 @@ -(ns syng-im.components.chat.input.money - (:require - [syng-im.components.chat.input.simple-command - :refer [simple-command-input-view]] - [syng-im.components.chat.input.input-styles :as st])) - -(defn money-input-view [command] - [simple-command-input-view command - {:keyboardType :numeric}]) diff --git a/src/syng_im/components/chat/new_participants.cljs b/src/syng_im/components/chat/new_participants.cljs deleted file mode 100644 index 91beecf10a..0000000000 --- a/src/syng_im/components/chat/new_participants.cljs +++ /dev/null @@ -1,35 +0,0 @@ -(ns syng-im.components.chat.new-participants - (:require [re-frame.core :refer [subscribe dispatch]] - [syng-im.resources :as res] - [syng-im.components.react :refer [view]] - [syng-im.components.realm :refer [list-view]] - [syng-im.components.toolbar :refer [toolbar]] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.chats.new-participant-contact - :refer [new-participant-contact]] - [reagent.core :as r] - [syng-im.components.chat.chat-message-styles :as st])) - -(defn new-participants-toolbar [navigator] - [toolbar - {:navigator navigator - :title "Add Participants" - :action {:image {:source res/v ;; {:uri "icon_search"} - :style st/new-participant-image} - :handler #(dispatch [:add-new-participants navigator])}}]) - -(defn new-participants-row [navigator] - (fn [row _ _] - (r/as-element - [new-participant-contact (js->clj row :keywordize-keys true) navigator]))) - -(defn new-participants [{:keys [navigator]}] - (let [contacts (subscribe [:all-new-contacts])] - (fn [] - (let [contacts-ds (to-realm-datasource @contacts)] - [view st/participants-container - [new-participants-toolbar navigator] - [list-view {:dataSource contacts-ds - :enableEmptySections true - :renderRow (new-participants-row navigator) - :style st/participants-list}]])))) diff --git a/src/syng_im/components/chat/remove_participants.cljs b/src/syng_im/components/chat/remove_participants.cljs deleted file mode 100644 index d9c5053c2d..0000000000 --- a/src/syng_im/components/chat/remove_participants.cljs +++ /dev/null @@ -1,36 +0,0 @@ -(ns syng-im.components.chat.remove-participants - (:require [re-frame.core :refer [subscribe dispatch]] - [syng-im.resources :as res] - [syng-im.components.react :refer [view text-input text image - touchable-highlight]] - [syng-im.components.realm :refer [list-view]] - [syng-im.components.toolbar :refer [toolbar]] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.chats.new-participant-contact - :refer [new-participant-contact]] - [reagent.core :as r] - [syng-im.components.chat.chat-message-styles :as st])) - -(defn remove-participants-toolbar [navigator] - [toolbar - {:navigator navigator - :title "Remove Participants" - :action {:handler #(dispatch [:remove-selected-participants navigator]) - :image {:source res/trash-icon ;; {:uri "icon_search"} - :style st/remove-participants-image}}}]) - -(defn remove-participants-row [navigator] - (fn [row _ _] - (r/as-element - [new-participant-contact (js->clj row :keywordize-keys true) navigator]))) - -(defn remove-participants [{:keys [navigator]}] - (let [contacts (subscribe [:current-chat-contacts])] - (fn [] - (let [contacts-ds (to-realm-datasource @contacts)] - [view st/participants-container - [remove-participants-toolbar navigator] - [list-view {:dataSource contacts-ds - :enableEmptySections true - :renderRow (remove-participants-row navigator) - :style st/participants-list}]])))) diff --git a/src/syng_im/components/chats/chat_list_item.cljs b/src/syng_im/components/chats/chat_list_item.cljs deleted file mode 100644 index a6cc0dc5a1..0000000000 --- a/src/syng_im/components/chats/chat_list_item.cljs +++ /dev/null @@ -1,37 +0,0 @@ -(ns syng-im.components.chats.chat-list-item - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view - text - image - touchable-highlight]] - [syng-im.components.styles :refer [font]] - [syng-im.components.chats.chat-list-item-inner :refer [chat-list-item-inner-view]] - [syng-im.utils.logging :as log] - [syng-im.resources :as res])) - -(defn chat-list-item [chat-obj navigator] - [touchable-highlight - {:on-press #(dispatch [:show-chat (aget chat-obj "chat-id") navigator :push])} - ;; TODO add [photo-path delivery-status new-messages-count online] values to chat-obj - ;; TODO should chat-obj be clj-map? - [view {} [chat-list-item-inner-view (merge (js->clj chat-obj :keywordize-keys true) - {:photo-path nil - :delivery-status :seen - :new-messages-count 3 - :timestamp "13:54" - :online true})]]]) - -(comment [view {:style {:flexDirection "row" - :width 260 - :marginVertical 5}} - [image {:source res/chat-icon - :style {:borderWidth 2 - :borderColor "#FFFFFF" - :width 32 - :height 30 - :marginRight 5 - :marginLeft 5}}] - [text {:style {:fontSize 14 - :fontFamily font - :color "#4A5258"}} - (subs (aget chat-obj "name") 0 30)]]) diff --git a/src/syng_im/components/chats/chat_list_item_inner.cljs b/src/syng_im/components/chats/chat_list_item_inner.cljs deleted file mode 100644 index 2c59897bfc..0000000000 --- a/src/syng_im/components/chats/chat_list_item_inner.cljs +++ /dev/null @@ -1,131 +0,0 @@ -(ns syng-im.components.chats.chat-list-item-inner - (:require [clojure.string :as s] - [syng-im.components.react :refer [view image text]] - [syng-im.components.styles :refer [font - title-font - color-white - color-blue - online-color - text1-color - text2-color - new-messages-count-color]] - [syng-im.resources :as res])) - -(defn contact-photo [{:keys [photo-path]}] - [view {:borderRadius 50} - [image {:source (if (s/blank? photo-path) - res/user-no-photo - {:uri photo-path}) - :style {:borderRadius 50 - :width 40 - :height 40}}]]) - -(defn contact-online [{:keys [online]}] - (when online - [view {:position "absolute" - :top 24 - :left 24 - :width 20 - :height 20 - :borderRadius 50 - :backgroundColor online-color - :borderWidth 2 - :borderColor color-white} - [view {:position "absolute" - :top 6 - :left 3 - :width 4 - :height 4 - :borderRadius 50 - :backgroundColor color-white}] - [view {:position "absolute" - :top 6 - :left 9 - :width 4 - :height 4 - :borderRadius 50 - :backgroundColor color-white}]])) - -(defn chat-list-item-inner-view - [{:keys [name photo-path delivery-status timestamp new-messages-count online - group-chat contacts]}] - [view {:style {:flexDirection "row" - :paddingVertical 15 - :paddingHorizontal 16 - :height 90}} - [view {:marginTop 2 - :width 44 - :height 44} -;;; photo - [contact-photo {:photo-path photo-path}] -;;; online - [contact-online {:online online}]] - [view {:style {:flexDirection "column" - :marginLeft 12 - :flex 1}} -;;; name - [view {:style {:flexDirection "row"}} - [text {:style {:marginTop -2.5 - :color text1-color - :fontSize 14 - :fontFamily title-font}} name] -;;; group size - (when group-chat - [image {:source {:uri "icon_group"} - :style {:marginTop 4 - :marginLeft 8 - :width 14 - :height 9}}]) - (when group-chat - [text {:style {:marginTop -0.5 - :marginLeft 4 - :fontFamily font - :fontSize 12 - :color text2-color}} - (if (< 1 (count contacts)) - (str (count contacts) " members") - "1 member")])] -;;; last message - [text {:style {:marginTop 7 - :marginRight 40 - :color text1-color - :fontFamily font - :fontSize 14 - :lineHeight 20} - :numberOfLines 2} - (repeatedly 5 #(str "Hi, I'm " name "! "))]] - [view {} -;;; delivery status - [view {:style {:flexDirection "row" - :position "absolute" - :top 0 - :right 0}} - (when delivery-status - [image {:source (if (= (keyword delivery-status) :seen) - {:uri "icon_ok_small"} - {:uri "icon_ok_small"}) - :style {:marginTop 6 - :width 9 - :height 7}}]) -;;; datetime - [text {:style {:fontFamily font - :fontSize 12 - :color text2-color - :marginLeft 5}} - timestamp]] -;;; new messages count - (when (pos? new-messages-count) - [view {:style {:position "absolute" - :top 36 - :right 0 - :width 24 - :height 24 - :backgroundColor new-messages-count-color - :borderRadius 50}} - [text {:style {:top 4 - :left 0 - :fontFamily title-font - :fontSize 10 - :color color-blue - :textAlign "center"}} - new-messages-count]])]]) diff --git a/src/syng_im/components/chats/chats_list.cljs b/src/syng_im/components/chats/chats_list.cljs deleted file mode 100644 index 2b63d8d76f..0000000000 --- a/src/syng_im/components/chats/chats_list.cljs +++ /dev/null @@ -1,71 +0,0 @@ -(ns syng-im.components.chats.chats-list - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view - text - image - touchable-highlight - navigator]] - [syng-im.components.realm :refer [list-view]] - [syng-im.utils.logging :as log] - [syng-im.navigation :refer [nav-pop]] - [syng-im.resources :as res] - [syng-im.utils.listview :refer [to-realm-datasource]] - [reagent.core :as r] - [syng-im.components.chats.chat-list-item :refer [chat-list-item]] - [syng-im.components.drawer :refer [drawer-view open-drawer]] - [syng-im.components.action-button :refer [action-button - action-button-item]] - [syng-im.components.styles :refer [font - title-font - color-white - color-black - color-blue - text1-color - text2-color]] - [syng-im.components.toolbar :refer [toolbar]] - [syng-im.components.icons.ionicons :refer [icon]])) - -(defn chats-list-toolbar [] - [toolbar {:nav-action {:image {:source {:uri "icon_hamburger"} - :style {:width 16 - :height 12}} - :handler open-drawer} - :title "Chats" - :action {:image {:source {:uri "icon_search"} - :style {:width 17 - :height 17}} - :handler (fn [])}}]) - -(defn chats-list [{:keys [navigator]}] - (let [chats (subscribe [:get-chats])] - (fn [] - (let [chats @chats - _ (log/debug "chats=" chats) - datasource (to-realm-datasource chats)] - [drawer-view {:navigator navigator} - [view {:style {:flex 1 - :backgroundColor "white"}} - [chats-list-toolbar] - [list-view {:dataSource datasource - :enableEmptySections true - :renderRow (fn [row section-id row-id] - (r/as-element [chat-list-item row navigator])) - :style {:backgroundColor "white"}}] - [action-button {:buttonColor color-blue} - [action-button-item {:title "New Chat" - :buttonColor "#9b59b6" - :onPress #(dispatch [:navigate-to - :contact-list])} - [icon {:name "android-create" - :style {:fontSize 20 - :height 22 - :color "white"}}]] - [action-button-item {:title "New Group Chat" - :buttonColor "#1abc9c" - :onPress (fn [] - (dispatch [:show-group-new]))} - [icon {:name "person-stalker" - :style {:fontSize 20 - :height 22 - :color "white"}}]]]]])))) diff --git a/src/syng_im/components/chats/new_group.cljs b/src/syng_im/components/chats/new_group.cljs deleted file mode 100644 index 5ec1cb1066..0000000000 --- a/src/syng_im/components/chats/new_group.cljs +++ /dev/null @@ -1,90 +0,0 @@ -(ns syng-im.components.chats.new-group - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.resources :as res] - [syng-im.components.react :refer [view - text-input - text - image - touchable-highlight]] - [syng-im.components.styles :refer [font - title-font - color-white - color-purple - text1-color - text2-color - toolbar-background1]] - [syng-im.components.toolbar :refer [toolbar]] - [syng-im.components.realm :refer [list-view]] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.chats.new-group-contact :refer [new-group-contact]] - [reagent.core :as r] - [syng-im.navigation :refer [nav-pop]])) - -(defn new-group-toolbar [navigator group-name] - [toolbar {:navigator navigator - :title "New group chat" - :action {:image {:source res/v ;; {:uri "icon_search"} - :style {:width 20 - :height 18}} - :handler (fn [] - (dispatch [:create-new-group group-name navigator]))}}]) - -(defn new-group [{:keys [navigator]}] - (let [contacts (subscribe [:all-contacts]) - group-name (r/atom nil)] - (fn [{:keys [navigator]}] - (let [contacts-ds (to-realm-datasource @contacts)] - [view {:style {:flex 1 - :flexDirection "column" - :backgroundColor color-white}} - [new-group-toolbar navigator @group-name] - [view {:style {:marginHorizontal 16}} - [text {:style {:marginTop 24 - :marginBottom 16 - :color text2-color - :fontFamily font - :fontSize 14 - :lineHeight 20}} - "Chat name"] - [text-input {:underlineColorAndroid color-purple - :style {:marginLeft -4 - :fontSize 14 - :fontFamily font - :color text1-color} - :autoFocus true - :placeholder "Group Name" - :placeholderTextColor text2-color - :onChangeText (fn [new-text] - (reset! group-name new-text) - (r/flush)) - :onSubmitEditing (fn [e] - ;(dispatch [:send-chat-msg @chat-id @text]) - (reset! group-name nil))} - @group-name] - [text {:style {:marginTop 24 - :marginBottom 16 - :color text2-color - :fontFamily font - :fontSize 14 - :lineHeight 20}} - "Members"] - [touchable-highlight {:on-press (fn [])} - [view {:style {:flexDirection "row" - :marginBottom 16}} - [image {:source {:uri "icon_add_gray"} - :style {:marginVertical 19 - :marginHorizontal 3 - :width 17 - :height 17}}] - [text {:style {:marginTop 18 - :marginLeft 32 - :color text2-color - :fontFamily font - :fontSize 14 - :lineHeight 20}} - "Add members"]]] - [list-view {:dataSource contacts-ds - :enableEmptySections true - :renderRow (fn [row section-id row-id] - (r/as-element [new-group-contact (js->clj row :keywordize-keys true) navigator])) - :style {:backgroundColor "white"}}]]])))) diff --git a/src/syng_im/components/chats/new_group_contact.cljs b/src/syng_im/components/chats/new_group_contact.cljs deleted file mode 100644 index 79153d775b..0000000000 --- a/src/syng_im/components/chats/new_group_contact.cljs +++ /dev/null @@ -1,20 +0,0 @@ -(ns syng-im.components.chats.new-group-contact - (:require [syng-im.resources :as res] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view]] - [syng-im.components.contact-list.contact-inner :refer [contact-inner-view]] - [syng-im.components.item-checkbox :refer [item-checkbox]] - [syng-im.utils.logging :as log] - [reagent.core :as r])) - -(defn new-group-contact [{:keys [whisper-identity] :as contact} navigator] - (let [checked (r/atom false)] - (fn [] - [view {:style {:flexDirection "row" - :height 56}} - [item-checkbox {:onToggle (fn [checked?] - (reset! checked checked?) - (dispatch [:select-for-new-group whisper-identity checked?])) - :checked @checked - :size 30}] - [contact-inner-view contact]]))) diff --git a/src/syng_im/components/chats/new_participant_contact.cljs b/src/syng_im/components/chats/new_participant_contact.cljs deleted file mode 100644 index 97985c6468..0000000000 --- a/src/syng_im/components/chats/new_participant_contact.cljs +++ /dev/null @@ -1,24 +0,0 @@ -(ns syng-im.components.chats.new-participant-contact - (:require [syng-im.resources :as res] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view]] - [syng-im.components.contact-list.contact-inner :refer [contact-inner-view]] - [syng-im.components.item-checkbox :refer [item-checkbox]] - [syng-im.utils.logging :as log] - [reagent.core :as r])) - -(defn new-participant-contact [{:keys [whisper-identity] :as contact} navigator] - (let [checked (r/atom false)] - (fn [] - [view {:style {:flexDirection "row" - :marginTop 5 - :marginBottom 5 - :paddingLeft 15 - :paddingRight 15 - :height 75}} - [item-checkbox {:onToggle (fn [checked?] - (reset! checked checked?) - (dispatch [:select-new-participant whisper-identity checked?])) - :checked @checked - :size 30}] - [contact-inner-view contact]]))) diff --git a/src/syng_im/components/contact_list/contact.cljs b/src/syng_im/components/contact_list/contact.cljs deleted file mode 100644 index 52bce8b6a7..0000000000 --- a/src/syng_im/components/contact_list/contact.cljs +++ /dev/null @@ -1,13 +0,0 @@ -(ns syng-im.components.contact-list.contact - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view text image touchable-highlight]] - [syng-im.resources :as res] - [syng-im.components.contact-list.contact-inner :refer [contact-inner-view]])) - -(defn show-chat [navigator whisper-identity] - (dispatch [:show-chat whisper-identity navigator :push])) - -(defn contact-view [{:keys [navigator contact]}] - (let [{:keys [whisper-identity]} contact] - [touchable-highlight {:onPress #(show-chat navigator whisper-identity)} - [view {} [contact-inner-view contact]]])) diff --git a/src/syng_im/components/contact_list/contact_inner.cljs b/src/syng_im/components/contact_list/contact_inner.cljs deleted file mode 100644 index 13e79d983b..0000000000 --- a/src/syng_im/components/contact_list/contact_inner.cljs +++ /dev/null @@ -1,66 +0,0 @@ -(ns syng-im.components.contact-list.contact-inner - (:require [clojure.string :as s] - [syng-im.components.react :refer [view image text]] - [syng-im.components.styles :refer [font - title-font - text1-color - color-white - online-color]] - [syng-im.resources :as res])) - -(defn contact-photo [{:keys [photo-path]}] - [view {:borderRadius 50} - [image {:source (if (s/blank? photo-path) - res/user-no-photo - {:uri photo-path}) - :style {:borderRadius 50 - :width 40 - :height 40}}]]) - -(defn contact-online [{:keys [online]}] - (when online - [view {:position "absolute" - :top 24 - :left 24 - :width 20 - :height 20 - :borderRadius 50 - :backgroundColor online-color - :borderWidth 2 - :borderColor color-white} - [view {:position "absolute" - :top 6 - :left 3 - :width 4 - :height 4 - :borderRadius 50 - :backgroundColor color-white}] - [view {:position "absolute" - :top 6 - :left 9 - :width 4 - :height 4 - :borderRadius 50 - :backgroundColor color-white}]])) - -(defn contact-inner-view [{:keys [name photo-path delivery-status datetime new-messages-count - online whisper-identity]}] - [view {:style {:flexDirection "row" - :height 56}} - [view {:style {:marginTop 8 - :marginLeft 16 - :width 44 - :height 44}} -;;; photo - [contact-photo {:photo-path photo-path}] -;;; online - [contact-online {:online online}]] -;;; name - [view {:style {:justifyContent "center"}} - [text {:style {:marginLeft 16 - :fontSize 16 - :fontFamily font - :color text1-color}} - (if (pos? (count name)) - name - "Noname")]]]) diff --git a/src/syng_im/components/contact_list/contact_list.cljs b/src/syng_im/components/contact_list/contact_list.cljs deleted file mode 100644 index 70d5145a9e..0000000000 --- a/src/syng_im/components/contact_list/contact_list.cljs +++ /dev/null @@ -1,52 +0,0 @@ -(ns syng-im.components.contact-list.contact-list - (:require-macros - [natal-shell.data-source :refer [data-source clone-with-rows]] - [natal-shell.core :refer [with-error-view]]) - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [view text image touchable-highlight - navigator list-view - list-item]] - [syng-im.components.contact-list.contact :refer [contact-view]] - [syng-im.components.styles :refer [font - title-font - color-white - color-black - color-blue - text1-color - text2-color - toolbar-background2]] - [syng-im.components.toolbar :refer [toolbar]] - [syng-im.navigation :refer [nav-pop]] - [syng-im.resources :as res] - [syng-im.utils.logging :as log])) - -(defn render-row [navigator row section-id row-id] - (list-item [contact-view {:navigator navigator - :contact (js->clj row :keywordize-keys true)}])) - -(defn get-data-source [contacts] - (clone-with-rows (data-source {:rowHasChanged (fn [row1 row2] - (not= row1 row2))}) - contacts)) - -(defn contact-list-toolbar [navigator] - [toolbar {:navigator navigator - :title "Contacts" - :background-color toolbar-background2 - :action {:image {:source {:uri "icon_search"} - :style {:width 17 - :height 17}} - :handler (fn [])}}]) - -(defn contact-list [{:keys [navigator]}] - (let [contacts (subscribe [:get-contacts])] - (fn [] - (let [contacts-ds (get-data-source @contacts)] - [view {:style {:flex 1 - :backgroundColor color-white}} - [contact-list-toolbar navigator] - (when contacts-ds - [list-view {:dataSource contacts-ds - :enableEmptySections true - :renderRow (partial render-row navigator) - :style {:backgroundColor "white"}}])])))) diff --git a/src/syng_im/components/discovery/discovery.cljs b/src/syng_im/components/discovery/discovery.cljs deleted file mode 100644 index 7d0732e396..0000000000 --- a/src/syng_im/components/discovery/discovery.cljs +++ /dev/null @@ -1,83 +0,0 @@ -(ns syng-im.components.discovery.discovery - - (:require - [syng-im.utils.logging :as log] - [re-frame.core :refer [dispatch]] - [syng-im.models.discoveries :refer [save-discoveries]] - [syng-im.components.react :refer [android? - view - scroll-view - text - text-input]] - [reagent.core :as r] - [syng-im.components.toolbar :refer [toolbar]] - [syng-im.components.discovery.discovery-popular :refer [discovery-popular]] - [syng-im.components.discovery.discovery-recent :refer [discovery-recent]] - [syng-im.components.discovery.styles :as st] - [syng-im.persistence.realm :as realm])) - -(def search-input (atom {:search "x"})) - -(defn get-hashtags [status] - (let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))] - (if hashtags - hashtags - []))) - -(defn title-content [showSearch] - [view st/discovery-toolbar-content - (if showSearch - [text-input {:underlineColorAndroid "transparent" - :style st/discovery-search-input - :autoFocus true - :placeholder "Type your search tags here" - :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"]])]) - -(defn create-fake-discovery [] - (let [number (rand-int 999)] - (do - (save-discoveries [{:name (str "Name " number) - :status (str "Status This is some longer status to get the second line " number) - :whisper-id (str number) - :photo "" - :location "" - :tags ["tag1" "tag2" "tag3"] - :last-updated (new js/Date)}]) - (dispatch [:updated-discoveries])))) - -(defn discovery [{:keys [navigator]}] - (let [showSearch (r/atom false)] - (fn [] - [view {:style {:flex 1 - :backgroundColor "#eef2f5"}} - [toolbar {:style st/discovery-toolbar - :navigator navigator - :nav-action {:image {:source {:uri "icon_hamburger"} - :style {:width 16 - :height 12}} - :handler create-fake-discovery} - :title "Add Participants" - :custom-content [title-content @showSearch] - :action {:image {:source {:uri "icon_search"} - :style {:width 17 - :height 17}} - :handler (fn [] - (if @showSearch - (reset! showSearch false) - (reset! showSearch true)))}}] - [scroll-view {:style {}} - [view {:style st/section-spacing} - [text {:style st/discovery-subtitle} "Popular tags"]] - [discovery-popular navigator] - [view {:style st/section-spacing} - [text {:style st/discovery-subtitle} "Recent"]] - [discovery-recent]]]))) - (comment - (def page-width (aget (natal-shell.dimensions/get "window") "width")) - (def page-height (aget (natal-shell.dimensions/get "window") "height")) - ) diff --git a/src/syng_im/components/discovery/discovery_popular.cljs b/src/syng_im/components/discovery/discovery_popular.cljs deleted file mode 100644 index df33467899..0000000000 --- a/src/syng_im/components/discovery/discovery_popular.cljs +++ /dev/null @@ -1,23 +0,0 @@ -(ns syng-im.components.discovery.discovery-popular - (:require - [re-frame.core :refer [subscribe]] - [syng-im.utils.logging :as log] - [syng-im.components.react :refer [android? - text]] - [syng-im.components.carousel :refer [carousel]] - [syng-im.components.discovery.styles :as st] - [syng-im.components.discovery.discovery-popular-list :refer [discovery-popular-list]] - )) - -(defn page-width [] - (.-width (.get (.. js/React -Dimensions) "window"))) - -(defn discovery-popular [navigator] - (let [popular-tags (subscribe [:get-popular-tags 3])] - (log/debug "Got popular tags: " @popular-tags) - (if (> (count @popular-tags) 0) - [carousel {:pageStyle st/carousel-page-style - :sneak 20} - (for [tag @popular-tags] - (discovery-popular-list (.-name tag) (.-count tag) navigator))] - [text "None"]))) \ No newline at end of file diff --git a/src/syng_im/components/discovery/discovery_popular_list.cljs b/src/syng_im/components/discovery/discovery_popular_list.cljs deleted file mode 100644 index 0291a1bb82..0000000000 --- a/src/syng_im/components/discovery/discovery_popular_list.cljs +++ /dev/null @@ -1,44 +0,0 @@ -(ns syng-im.components.discovery.discovery-popular-list - (:require - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.utils.logging :as log] - [syng-im.components.react :refer [android? - view - list-view - touchable-highlight - text - image]] - [reagent.core :as r] - [syng-im.components.realm :refer [list-view]] - [syng-im.components.discovery.styles :as st] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.discovery.discovery-popular-list-item :refer [discovery-popular-list-item] ]) - ) - - -(defn render-row [row section-id row-id] - (let [elem (discovery-popular-list-item row)] - elem) -) - -(defn render-separator [sectionID, rowID, adjacentRowHighlighted] - (let [elem (r/as-element [view {:style st/row-separator - :key rowID}])] - elem)) - -(defn discovery-popular-list [tag count navigator] - (let [discoveries (subscribe [:get-discoveries-by-tag tag 3])] - [view {:style st/popular-list-container} - [view st/row - [view st/tag-name-container - [touchable-highlight {:onPress #(dispatch [:show-discovery-tag tag navigator :push])} - [text {:style st/tag-name} - (str " #" (name tag))]]] - [view {:style st/tag-count-container} - [text {:style st/tag-count} - count]]] - [list-view {:dataSource (to-realm-datasource @discoveries) - :enableEmptySections true - :renderRow render-row - :renderSeparator render-separator - :style st/popular-list}]])) diff --git a/src/syng_im/components/discovery/discovery_popular_list_item.cljs b/src/syng_im/components/discovery/discovery_popular_list_item.cljs deleted file mode 100644 index 2313d4ff76..0000000000 --- a/src/syng_im/components/discovery/discovery_popular_list_item.cljs +++ /dev/null @@ -1,23 +0,0 @@ -(ns syng-im.components.discovery.discovery-popular-list-item - (:require - [syng-im.utils.logging :as log] - [syng-im.components.react :refer [android? - view - text - image]] - [syng-im.components.discovery.styles :as st] - [reagent.core :as r]) - ) - -(defn discovery-popular-list-item [discovery] - (r/as-element [view {:style st/popular-list-item} - [view {:style st/popular-list-item-name-container} - [text {:style st/popular-list-item-name} (aget discovery "name")] - [text {:style st/popular-list-item-status - :numberOfLines 2} (aget discovery "status")] - ] - [view {:style st/popular-list-item-avatar-container} - [image {:style st/popular-list-item-avatar - :source {:uri "icon_avatar"}}] - ] - ])) \ No newline at end of file diff --git a/src/syng_im/components/discovery/discovery_recent.cljs b/src/syng_im/components/discovery/discovery_recent.cljs deleted file mode 100644 index 4b1a650322..0000000000 --- a/src/syng_im/components/discovery/discovery_recent.cljs +++ /dev/null @@ -1,35 +0,0 @@ -(ns syng-im.components.discovery.discovery-recent - (:require-macros - [natal-shell.data-source :refer [data-source clone-with-rows]] - - ) - (:require - [re-frame.core :refer [subscribe]] - [syng-im.components.react :refer [android? - view]] - [syng-im.components.realm :refer [list-view]] - [syng-im.utils.listview :refer [to-realm-datasource]] - [syng-im.components.discovery.styles :as st] - [syng-im.components.discovery.discovery-popular-list-item :refer [discovery-popular-list-item]] - [reagent.core :as r])) - - -(defn render-row [row section-id row-id] - (let [elem (discovery-popular-list-item row)] - elem) - ) - -(defn render-separator [sectionID, rowID, adjacentRowHighlighted] - (let [elem (r/as-element [view {:style st/row-separator - :key rowID}])] - elem)) - -(defn discovery-recent [] - (let [discoveries (subscribe [:get-discoveries]) - datasource (to-realm-datasource @discoveries)] - [list-view {:dataSource datasource - :enableEmptySections true - :renderRow render-row - :renderSeparator render-separator - :style st/recent-list}] - )) \ No newline at end of file diff --git a/src/syng_im/components/discovery/discovery_tag.cljs b/src/syng_im/components/discovery/discovery_tag.cljs deleted file mode 100644 index f8a7fa88a8..0000000000 --- a/src/syng_im/components/discovery/discovery_tag.cljs +++ /dev/null @@ -1,59 +0,0 @@ -(ns syng-im.components.discovery.discovery-tag - (:require - [re-frame.core :refer [subscribe]] - [syng-im.utils.logging :as log] - [syng-im.utils.listview :refer [to-realm-datasource - to-datasource]] - [syng-im.navigation :refer [nav-pop]] - [syng-im.components.react :refer [android? - view - text]] - [syng-im.components.realm :refer [list-view]] - [syng-im.components.toolbar :refer [toolbar]] - [reagent.core :as r] - [syng-im.components.discovery.discovery-popular-list-item :refer [discovery-popular-list-item]] - [syng-im.components.discovery.styles :as st])) - -(defn render-row [row section-id row-id] - (log/debug "discovery-tag-row: " row section-id row-id) - (if row - (let [elem (discovery-popular-list-item row)] - elem) - (r/as-element [text "null"]) - )) - -(defn render-separator [sectionID, rowID, adjacentRowHighlighted] - (let [elem (r/as-element [view {:style st/row-separator - :key rowID}])] - elem)) - -(defn title-content [tag] - [view {:style st/tag-title-container} - [text {:style st/tag-title} - (str " #" tag)]]) - -(defn discovery-tag [{:keys [tag navigator]}] - (let [tag (subscribe [:get-current-tag]) - discoveries (subscribe [:get-discoveries-by-tag @tag 0])] - (log/debug "Got discoveries: " @discoveries) - (fn [] - (let [items @discoveries - datasource (to-realm-datasource items)] - [view {:style st/discovery-tag-container} - [toolbar {:navigator navigator - :nav-action {:image {:source {:uri "icon_back"} - :style st/icon-back} - :handler (fn [] (nav-pop navigator))} - :title "Add Participants" - :content (title-content @tag) - :action {:image {:source {:uri "icon_search"} - :style st/icon-search} - :handler (fn [] - ())}}] - - [list-view {:dataSource datasource - :enableEmptySections true - :renderRow render-row - :renderSeparator render-separator - :style st/recent-list}] - ])))) \ No newline at end of file diff --git a/src/syng_im/components/discovery/handlers.cljs b/src/syng_im/components/discovery/handlers.cljs deleted file mode 100644 index 44d24c99d9..0000000000 --- a/src/syng_im/components/discovery/handlers.cljs +++ /dev/null @@ -1,44 +0,0 @@ -(ns syng-im.components.discovery.handlers - (:require [re-frame.core :refer [register-handler after dispatch]] - [syng-im.utils.logging :as log] - [syng-im.protocol.api :as api] - [syng-im.navigation :refer [nav-push - nav-replace - nav-pop]] - [syng-im.models.discoveries :refer [save-discoveries - set-current-tag - signal-discoveries-updated]])) - - -;; -- Discovery -------------------------------------------------------------- - -(register-handler :discovery-response-received - (fn [db [_ from payload]] - (let [{:keys [name status hashtags location]} payload - location (if location location "")] - (save-discoveries [{:name name - :status status - :whisper-id from - :photo "" - :location location - :tags hashtags - :last-updated (js/Date.)}]) - (signal-discoveries-updated db)))) - -(register-handler :updated-discoveries - (fn [db _] - (signal-discoveries-updated db))) - -(register-handler :broadcast-status - (fn [db [action status hashtags]] - (let [name (:name db)] - (log/debug "Status: " status ", Hashtags: " hashtags) - (api/broadcast-discover-status name status hashtags) - db))) - -(register-handler :show-discovery-tag - (fn [db [action tag navigator nav-type]] - (log/debug action "setting current tag: " tag) - (let [db (set-current-tag db tag)] - (dispatch [:navigate-to navigator {:view-id :discovery-tag} nav-type]) - db))) \ No newline at end of file diff --git a/src/syng_im/components/discovery/subs.cljs b/src/syng_im/components/discovery/subs.cljs deleted file mode 100644 index d1a8eb5399..0000000000 --- a/src/syng_im/components/discovery/subs.cljs +++ /dev/null @@ -1,48 +0,0 @@ -(ns syng-im.components.discovery.subs - (:require-macros [reagent.ratom :refer [reaction]]) - (:require [re-frame.core :refer [register-sub]] - [syng-im.db :as db] - [syng-im.utils.logging :as log] - [syng-im.models.discoveries :refer [discovery-list - current-tag - get-tag-popular - discoveries-by-tag - current-tag-updated? - discoveries-updated?]])) - - - -(register-sub :get-discoveries - (fn [db _] - (let [discoveries-updated (-> (discoveries-updated? @db) - (reaction))] - (reaction - (let [_ @discoveries-updated] - (discovery-list)))))) - -(register-sub :get-discoveries-by-tag - (fn [db [_ tag limit]] - (let [discoveries-updated (-> (discoveries-updated? @db) - (reaction))] - (log/debug "Getting discoveries for: " tag) - (reaction - (let [_ @discoveries-updated] - (discoveries-by-tag tag limit)))))) - -(register-sub :get-popular-tags - (fn [db [_ limit]] - (let [discoveries-updated (-> (discoveries-updated? @db) - (reaction))] - (log/debug "Getting tags limited: " limit) - (reaction - (let [_ @discoveries-updated] - (get-tag-popular limit)))))) - -(register-sub :get-current-tag - (fn [db _] - (let [current-tag-updated (-> (current-tag-updated? @db) - (reaction))] - (reaction - (let [_ @current-tag-updated] - (current-tag @db)))))) - diff --git a/src/syng_im/components/drawer_styles.cljs b/src/syng_im/components/drawer/styles.cljs similarity index 97% rename from src/syng_im/components/drawer_styles.cljs rename to src/syng_im/components/drawer/styles.cljs index b6ee3c0808..4e26767b26 100644 --- a/src/syng_im/components/drawer_styles.cljs +++ b/src/syng_im/components/drawer/styles.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.drawer-styles +(ns syng-im.components.drawer.styles (:require [syng-im.components.styles :refer [font color-light-blue-transparent color-white diff --git a/src/syng_im/components/drawer.cljs b/src/syng_im/components/drawer/view.cljs similarity index 92% rename from src/syng_im/components/drawer.cljs rename to src/syng_im/components/drawer/view.cljs index 362127f69d..3c96ce3395 100644 --- a/src/syng_im/components/drawer.cljs +++ b/src/syng_im/components/drawer/view.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.drawer +(ns syng-im.components.drawer.view (:require [clojure.string :as s] [re-frame.core :refer [subscribe dispatch dispatch-sync]] [reagent.core :as r] @@ -11,7 +11,7 @@ drawer-layout-android touchable-opacity]] [syng-im.resources :as res] - [syng-im.components.drawer-styles :as st])) + [syng-im.components.drawer.styles :as st])) (defonce drawer-atom (atom)) @@ -35,9 +35,9 @@ [text {:style st/menu-item-text} name]]) -(defn drawer-menu [navigator] - (let [username (subscribe [:username])] - (fn [navigator] +(defn drawer-menu [] + (let [username (subscribe [:get :username])] + (fn [] [view st/drawer-menu [view st/user-photo-container [user-photo {}]] @@ -72,10 +72,10 @@ [text {:style st/switch-users-text} "Switch users"]]]]))) -(defn drawer-view [{:keys [navigator]} items] +(defn drawer-view [items] [drawer-layout-android {:drawerWidth 260 :drawerPosition js/React.DrawerLayoutAndroid.positions.Left - :render-navigation-view #(r/as-element [drawer-menu navigator]) + :render-navigation-view #(r/as-element [drawer-menu]) :ref (fn [drawer] (reset! drawer-atom drawer))} items]) diff --git a/src/syng_im/components/profile.cljs b/src/syng_im/components/profile.cljs deleted file mode 100644 index 85b64b6475..0000000000 --- a/src/syng_im/components/profile.cljs +++ /dev/null @@ -1,109 +0,0 @@ -(ns syng-im.components.profile - (:require [clojure.string :as s] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.components.react :refer [android? - view - text - text-input - image - icon - scroll-view - touchable-highlight - touchable-opacity]] - [syng-im.resources :as res] - [syng-im.components.profile-styles :as st])) - -(defn user-photo [{:keys [photo-path]}] - [image {:source (if (s/blank? photo-path) - res/user-no-photo - {:uri photo-path}) - :style st/user-photo}]) - -(defn user-online [{:keys [online]}] - (when online - [view st/user-online-container - [view st/user-online-dot-left] - [view st/user-online-dot-right]])) - -(defn profile-property-view [{:keys [name value]}] - [view st/profile-property-view-container - [view st/profile-property-view-sub-container - [text {:style st/profile-property-view-label} - name] - [text {:style st/profile-property-view-value} - value]]]) - -(defn message-user [identity] - (when identity - (dispatch [:show-chat identity nil :push]))) - -(defn profile [] - (let [contact (subscribe [:contact])] - (fn [] - [scroll-view {:style st/profile} - [touchable-highlight {:style st/back-btn-touchable - :on-press #(dispatch [:navigate-back])} - [view st/back-btn-container - [icon :back st/back-btn-icon]]] - [view st/status-block - [view st/user-photo-container - [user-photo {}] - [user-online {:online true}]] - [text {:style st/user-name} - (:name @contact)] - [text {:style st/status} - "!not implemented"] - [view st/btns-container - [touchable-highlight {:onPress #(message-user (:whisper-identity @contact))} - [view st/message-btn - [text {:style st/message-btn-text} - "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" - :value (:name @contact)}] - [profile-property-view {:name "Phone number" - :value (:phone-number @contact)}] - [profile-property-view {:name "Email" - :value "!not implemented"}] - [view st/report-user-container - [touchable-opacity {} - [text {:style st/report-user-text} - "REPORT USER"]]]]]))) - -(defn my-profile [] - (let [username (subscribe [:username]) - phone-number (subscribe [:phone-number]) - email (subscribe [:email]) - status (subscribe [:status])] - (fn [] - [scroll-view {:style st/profile} - [touchable-highlight {:style st/back-btn-touchable - :on-press #(dispatch [:navigate-back])} - [view st/back-btn-container - [icon :back st/back-btn-icon]]] - [touchable-highlight {:style st/actions-btn-touchable - :on-press (fn [] - ;; TODO not implemented - )} - [view st/actions-btn-container - [icon :dots st/actions-btn-icon]]] - [view st/status-block - [view st/user-photo-container - [user-photo {}] - [user-online {:online true}]] - [text {:style st/user-name} - @username] - [text {:style st/status} - @status]] - [view st/profile-properties-container - [profile-property-view {:name "Username" - :value @username}] - [profile-property-view {:name "Phone number" - :value @phone-number}] - [profile-property-view {:name "Email" - :value @email}]]]))) diff --git a/src/syng_im/components/react.cljs b/src/syng_im/components/react.cljs index 87ea94c177..58c99aa6e4 100644 --- a/src/syng_im/components/react.cljs +++ b/src/syng_im/components/react.cljs @@ -15,7 +15,9 @@ (merge {:underlay-color :transparent} props) content]) (def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React))) -(def list-view (r/adapt-react-class (.-ListView js/React))) +(def list-view-class (r/adapt-react-class (.-ListView js/React))) +(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))) @@ -45,42 +47,3 @@ (r/as-element component)) (def dismiss-keyboard! (js/require "dismissKeyboard")) - -(comment - (.-width (.get (.. js/React -Dimensions) "window")) - ) - - -;; (do -;; (def activity-indicator-ios (r/adapt-react-class (.-ActivityIndicatorIOS js/React))) -;; (def animated-image (r/adapt-react-class (.-Animated.Image js/React))) -;; (def animated-text (r/adapt-react-class (.-Animated.Text js/React))) -;; (def animated-view (r/adapt-react-class (.-Animated.View js/React))) -;; (def date-picker-ios (r/adapt-react-class (.-DatePickerIOS js/React))) -;; (def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid js/React))) -;; (def image (r/adapt-react-class (.-Image js/React))) -;; (def list-view (r/adapt-react-class (.-ListView js/React))) -;; (def map-view (r/adapt-react-class (.-MapView js/React))) -;; (def modal (r/adapt-react-class (.-Modal js/React))) -;; (def navigator (r/adapt-react-class (.-Navigator js/React))) -;; (def navigator-ios (r/adapt-react-class (.-NavigatorIOS js/React))) -;; (def picker-ios (r/adapt-react-class (.-PickerIOS js/React))) -;; (def progress-bar-android (r/adapt-react-class (.-ProgressBarAndroid js/React))) -;; (def progress-view-ios (r/adapt-react-class (.-ProgressViewIOS js/React))) -;; (def pull-to-refresh-view-android (r/adapt-react-class (.-PullToRefreshViewAndroid js/React))) -;; (def scroll-view (r/adapt-react-class (.-ScrollView js/React))) -;; (def segmented-control-ios (r/adapt-react-class (.-SegmentedControlIOS js/React))) -;; (def slider-ios (r/adapt-react-class (.-SliderIOS js/React))) -;; (def switch (r/adapt-react-class (.-Switch js/React))) -;; (def tab-bar-ios (r/adapt-react-class (.-TabBarIOS js/React))) -;; (def tab-bar-ios-item (r/adapt-react-class (.-TabBarIOS.Item js/React))) -;; (def text (r/adapt-react-class (.-Text js/React))) -;; (def text-input (r/adapt-react-class (.-TextInput js/React))) -;; (def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React))) -;; (def touchable-highlight (r/adapt-react-class (.-TouchableHighlight js/React))) -;; (def touchable-native-feedback (r/adapt-react-class (.-TouchableNativeFeedback js/React))) -;; (def touchable-opacity (r/adapt-react-class (.-TouchableOpacity js/React))) -;; (def touchable-without-feedback (r/adapt-react-class (.-TouchableWithoutFeedback js/React))) -;; (def view (r/adapt-react-class (.-View js/React))) -;; (def view-pager-android (r/adapt-react-class (.-ViewPagerAndroid js/React))) -;; (def web-view (r/adapt-react-class (.-WebView js/React)))) diff --git a/src/syng_im/components/realm.cljs b/src/syng_im/components/realm.cljs index 524c2f6909..f9ea17d84e 100644 --- a/src/syng_im/components/realm.cljs +++ b/src/syng_im/components/realm.cljs @@ -3,16 +3,7 @@ (set! js/window.RealmReactNative (js/require "realm/react-native")) -(def list-view (r/adapt-react-class (.-ListView js/RealmReactNative))) +(def list-view-class (r/adapt-react-class (.-ListView js/RealmReactNative))) -(comment - - - ;(set! js/wat (js/require "realm.react-native.ListView")) - ;(.-Results js/Realm) - ; - ;(r/realm) - ; - ;(require '[syng-im.persistence.realm :as r]) - - ) +(defn list-view [props] + [list-view-class (merge {:enableEmptySections true} props)]) diff --git a/src/syng_im/components/styles.cljs b/src/syng_im/components/styles.cljs index 326f223cda..786555bcf1 100644 --- a/src/syng_im/components/styles.cljs +++ b/src/syng_im/components/styles.cljs @@ -10,7 +10,7 @@ (def color-black "#000000de") (def color-purple "#a187d5") (def color-gray "#838c93de") -(def color-white "white") +(def color-white :white) (def color-light-blue "#bbc4cb") (def color-light-blue-transparent "#bbc4cb32") (def color-dark-mint "#5fc48d") diff --git a/src/syng_im/components/toolbar.cljs b/src/syng_im/components/toolbar.cljs index f66ddf5735..72ef406a0e 100644 --- a/src/syng_im/components/toolbar.cljs +++ b/src/syng_im/components/toolbar.cljs @@ -1,8 +1,8 @@ (ns syng-im.components.toolbar - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.resources :as res] + (:require [re-frame.core :refer [subscribe dispatch]] [syng-im.components.react :refer [view text-input + icon text image touchable-highlight]] @@ -14,10 +14,9 @@ text2-color toolbar-background1]] [syng-im.components.realm :refer [list-view]] - [syng-im.utils.listview :refer [to-realm-datasource]] [reagent.core :as r])) -(defn toolbar [{:keys [navigator title nav-action hide-nav? action custom-action +(defn toolbar [{:keys [title nav-action hide-nav? action custom-action background-color custom-content style]}] (let [style (merge {:flexDirection "row" :backgroundColor (or background-color toolbar-background1) @@ -54,6 +53,7 @@ [touchable-highlight {:on-press (:handler action)} [view {:width 56 :height 56 + :alignItems "center" :justifyContent "center"} [image (:image action)]]])])) diff --git a/src/syng_im/contacts/handlers.cljs b/src/syng_im/contacts/handlers.cljs new file mode 100644 index 0000000000..ac58a379ba --- /dev/null +++ b/src/syng_im/contacts/handlers.cljs @@ -0,0 +1,101 @@ +(ns syng-im.contacts.handlers + (:require [re-frame.core :refer [register-handler after dispatch]] + [syng-im.models.contacts :as contacts] + [syng-im.utils.crypt :refer [encrypt]] + [clojure.string :as s] + [syng-im.utils.utils :refer [http-post]] + [syng-im.utils.phone-number :refer [format-phone-number]] + [syng-im.utils.handlers :as u])) + +(defn save-contact + [_ [_ contact]] + (contacts/save-contacts [contact])) + +(register-handler :add-contact + (-> (fn [db [_ {:keys [whisper-identity] :as contact}]] + (update db :contacts assoc whisper-identity contact)) + ((after save-contact)))) + +(defn load-contacts! [db _] + (let [contacts (->> (contacts/get-contacts) + (map (fn [{:keys [whisper-identity] :as contact}] + [whisper-identity contact])) + (into {}))] + (assoc db :contacts contacts))) + +(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")) + +(defn contact-name [contact] + (->> contact + ((juxt :givenName :middleName :familyName)) + (remove s/blank?) + (s/join " "))) + +(defn normalize-phone-contacts [contacts] + (let [contacts' (js->clj contacts :keywordize-keys true)] + (map (fn [{:keys [thumbnailPath phoneNumbers] :as contact}] + {:name (contact-name contact) + :photo-path thumbnailPath + :phone-numbers phoneNumbers}) contacts'))) + +(defn fetch-contacts-from-phone! + [_ _] + (.getAll react-native-contacts + (fn [error contacts] + (if error + (dispatch [:error-on-fetching-loading error]) + (let [contacts' (normalize-phone-contacts contacts)] + (dispatch [:get-contacts-identities contacts'])))))) + +(register-handler :sync-contacts + (u/side-effect! fetch-contacts-from-phone!)) + +(defn get-contacts-by-hash [contacts] + (->> contacts + (mapcat (fn [{:keys [phone-numbers] :as contact}] + (map (fn [{:keys [number]}] + (let [number' (format-phone-number number)] + [(encrypt number') + (-> contact + (assoc :phone-number number') + (dissoc :phone-numbers))])) + phone-numbers))) + (into {}))) + +(defn add-identity [contacts-by-hash contacts] + (map (fn [{:keys [phone-number-hash whisper-identity]}] + (let [contact (contacts-by-hash phone-number-hash)] + (assoc contact :whisper-identity whisper-identity))) + (js->clj contacts))) + +(defn request-stored-contacts [contacts] + (let [contacts-by-hash (get-contacts-by-hash contacts) + data (keys contacts-by-hash)] + (http-post "get-contacts" {:phone-number-hashes data} + (fn [{:keys [contacts]}] + (let [contacts' (add-identity contacts-by-hash contacts)] + (dispatch [:add-contacts contacts'])))))) + +(defn get-identities-by-contacts! [_ [_ contacts]] + (request-stored-contacts contacts)) + +(register-handler :get-contacts-identities + (u/side-effect! get-identities-by-contacts!)) + +(defn save-contacts! [{:keys [new-contacts]} _] + (contacts/save-contacts new-contacts)) + +(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)] + (-> db + (update :contacts concat new-contacts') + (assoc :new-contacts new-contacts')))) + +(register-handler :add-contacts + (after save-contacts!) + add-new-contacts) diff --git a/src/syng_im/contacts/screen.cljs b/src/syng_im/contacts/screen.cljs new file mode 100644 index 0000000000..3e16b78380 --- /dev/null +++ b/src/syng_im/contacts/screen.cljs @@ -0,0 +1,35 @@ +(ns syng-im.contacts.screen + (:require-macros [syng-im.utils.views :refer [defview]]) + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.components.react :refer [view text + image + touchable-highlight + list-view + list-item]] + [syng-im.contacts.views.contact :refer [contact-view]] + [syng-im.components.styles :refer [toolbar-background2]] + [syng-im.components.toolbar :refer [toolbar]] + [syng-im.contacts.styles :as st] + [syng-im.utils.listview :as lw])) + +(defn render-row [row _ _] + (list-item [contact-view row])) + +(defn contact-list-toolbar [] + [toolbar {:title "Contacts" + :background-color toolbar-background2 + :action {:image {:source {:uri :icon_search} + :style st/search-icon} + :handler (fn [])}}]) + +(defview contact-list [] + [contacts [:get-contacts]] + [view st/contacts-list-container + [contact-list-toolbar] + ;; todo what if there is no contacts, should we show some information + ;; about this? + (when contacts + [list-view {:dataSource (lw/to-datasource contacts) + :enableEmptySections true + :renderRow render-row + :style st/contacts-list}])]) diff --git a/src/syng_im/contacts/styles.cljs b/src/syng_im/contacts/styles.cljs new file mode 100644 index 0000000000..819c5582eb --- /dev/null +++ b/src/syng_im/contacts/styles.cljs @@ -0,0 +1,69 @@ +(ns syng-im.contacts.styles + (:require [syng-im.components.styles :refer [font + title-font + text1-color + color-white + online-color]])) + +(def search-icon + {:width 17 + :height 17}) + +(def contacts-list-container + {:flex 1 + :backgroundColor :white}) + +(def contacts-list + {:backgroundColor :white}) + +(def contact-photo-container + {:borderRadius 50}) + +(def photo-image + {:borderRadius 50 + :width 40 + :height 40}) + +(def online-container + {:position :absolute + :top 24 + :left 24 + :width 20 + :height 20 + :borderRadius 50 + :backgroundColor online-color + :borderWidth 2 + :borderColor color-white}) + +(def online-dot + {:position :absolute + :top 6 + :width 4 + :height 4 + :borderRadius 50 + :backgroundColor color-white}) + +(def online-dot-left + (assoc online-dot :left 3)) + +(def online-dot-right + (assoc online-dot :left 9)) + +(def contact-container + {:flexDirection :row + :height 56}) + +(def photo-container + {:marginTop 8 + :marginLeft 16 + :width 44 + :height 44}) + +(def name-container + {:justifyContent :center}) + +(def name-text + {:marginLeft 16 + :fontSize 16 + :fontFamily font + :color text1-color}) diff --git a/src/syng_im/contacts/subs.cljs b/src/syng_im/contacts/subs.cljs new file mode 100644 index 0000000000..d889f12b60 --- /dev/null +++ b/src/syng_im/contacts/subs.cljs @@ -0,0 +1,39 @@ +(ns syng-im.contacts.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]])) + +(register-sub :get-contacts + (fn [db _] + (let [contacts (reaction (:contacts @db))] + (reaction (vals @contacts))))) + +(register-sub :all-contacts + (fn [db _] + (let [contacts (reaction (:contacts @db))] + (reaction (sort-by :name (vals @contacts)))))) + +(defn contacts-by-current-chat [fn db] + (let [current-chat-id (:current-chat-id @db) + chat (reaction (get-in @db [:chats current-chat-id])) + contacts (reaction (:contacts @db))] + (reaction + (when @chat + (let [current-participants (->> @chat + :contacts + (map :identity) + set)] + (fn #(current-participants (:whisper-identity %)) + (vals @contacts))))))) + +(register-sub :contact + (fn [db _] + (let [identity (:contact-identity @db)] + (reaction (get-in @db [:contacts identity]))))) + +(register-sub :all-new-contacts + (fn [db _] + (contacts-by-current-chat remove db))) + +(register-sub :current-chat-contacts + (fn [db _] + (contacts-by-current-chat filter db))) diff --git a/src/syng_im/contacts/views/contact.cljs b/src/syng_im/contacts/views/contact.cljs new file mode 100644 index 0000000000..861a4c3cfa --- /dev/null +++ b/src/syng_im/contacts/views/contact.cljs @@ -0,0 +1,16 @@ +(ns syng-im.contacts.views.contact + (:require-macros [syng-im.utils.views :refer [defview]]) + (:require [syng-im.components.react :refer [view touchable-highlight]] + [re-frame.core :refer [dispatch subscribe]] + [syng-im.contacts.views.contact-inner :refer [contact-inner-view]])) + +(defn on-press [chat whisper-identity] + (if chat + #(dispatch [:navigate-to :chat whisper-identity]) + #(dispatch [:start-chat whisper-identity]))) + +(defview contact-view [{:keys [whisper-identity] :as contact}] + [chat [:get-chat whisper-identity]] + [touchable-highlight + {:onPress (on-press chat whisper-identity)} + [view {} [contact-inner-view contact]]]) diff --git a/src/syng_im/contacts/views/contact_inner.cljs b/src/syng_im/contacts/views/contact_inner.cljs new file mode 100644 index 0000000000..d96ed3cd35 --- /dev/null +++ b/src/syng_im/contacts/views/contact_inner.cljs @@ -0,0 +1,30 @@ +(ns syng-im.contacts.views.contact-inner + (:require [clojure.string :as s] + [syng-im.components.react :refer [view image text]] + [syng-im.resources :as res] + [syng-im.contacts.styles :as st])) + +(defn contact-photo [{:keys [photo-path]}] + [view st/contact-photo-container + [image {:source (if (s/blank? photo-path) + res/user-no-photo + {:uri photo-path}) + :style st/photo-image}]]) + +(defn contact-online [{:keys [online]}] + (when online + [view st/online-container + [view st/online-dot-left] + [view st/online-dot-right]])) + +(defn contact-inner-view [{:keys [name photo-path online]}] + [view st/contact-container + [view st/photo-container + [contact-photo {:photo-path photo-path}] + [contact-online {:online online}]] + [view st/name-container + [text {:style st/name-text} + (if (pos? (count name)) + name + ;; todo is this correct behaviour? + "Noname")]]]) diff --git a/src/syng_im/db.cljs b/src/syng_im/db.cljs index 70ec6a6e3f..4720ad0c62 100644 --- a/src/syng_im/db.cljs +++ b/src/syng_im/db.cljs @@ -10,15 +10,16 @@ (def app-db {:identity-password "replace-me-with-user-entered-password" :identity "me" :contacts [] + :contacts-ids #{} :current-chat-id "console" - :chat {:command nil - :last-message nil} + :chat {:command nil + :last-message nil} :chats {} :chats-updated-signal 0 :show-actions false :new-group #{} :new-participants #{} - :signed-up false + :signed-up true :view-id default-view :navigation-stack (list default-view) ;; TODO fix hardcoded values @@ -29,12 +30,6 @@ :current-tag nil}) (def protocol-initialized-path [:protocol-initialized]) -(def identity-password-path [:identity-password]) -(def contact-identity-path [:contact-identity]) -(def current-chat-id-path [:current-chat-id]) -(def updated-chats-signal-path [:chats-updated-signal]) -(defn updated-chat-signal-path [chat-id] - [:chats chat-id :chat-updated-signal]) (defn chat-name-path [chat-id] [:chats chat-id :name]) (defn chat-color-path [chat-id] @@ -53,13 +48,5 @@ [:chats chat-id :command-requests]) (defn chat-command-request-path [chat-id msg-id] [:chats chat-id :command-requests msg-id]) -(def show-actions-path [:show-actions]) (def group-settings-selected-member-path [:group-settings-selected-member]) (def group-settings-show-color-picker [:group-settings-show-color-picker]) -(def new-group-path [:new-group]) -(def new-participants-path [:new-participants]) -(def updated-discoveries-signal-path [:discovery-updated-signal]) -(defn updated-discovery-signal-path [whisper-id] - [:discoveries whisper-id :discovery-updated-signal]) -(def current-tag-path [:current-tag]) -(def updated-current-tag-signal-path [:current-tag-updated-signal]) diff --git a/src/syng_im/discovery/handlers.cljs b/src/syng_im/discovery/handlers.cljs new file mode 100644 index 0000000000..2efd7543ed --- /dev/null +++ b/src/syng_im/discovery/handlers.cljs @@ -0,0 +1,76 @@ +(ns syng-im.discovery.handlers + (:require [re-frame.core :refer [register-handler after dispatch enrich]] + [syng-im.protocol.api :as api] + [syng-im.navigation.handlers :as nav] + [syng-im.discovery.model :as discoveries] + [syng-im.utils.handlers :as u])) + +(defmethod nav/preload-data! :discovery + [{:keys [discoveries] :as db} _] + (if-not (seq discoveries) + (-> db + (assoc :tags (discoveries/all-tags)) + ;; todo add limit + ;; todo hash-map with whisper-id as key and sorted by last-update + ;; may be more efficient here + (assoc :discoveries (discoveries/discovery-list))) + db)) + +(register-handler :discovery-response-received + (u/side-effect! + (fn [_ [_ from payload]] + (let [{:keys [name status hashtags location]} payload + location (or location "") + discovery [{:name name + :status status + :whisper-id from + :photo "" + :location location + :tags (map #(hash-map :name %) hashtags) + :last-updated (js/Date.)}]] + (dispatch [:add-discovery discovery]))))) + +(register-handler :broadcast-status + (u/side-effect! + (fn [{:keys [name]} [_ status hashtags]] + (api/broadcast-discover-status name status hashtags)))) + +(register-handler :show-discovery-tag + (fn [db [_ tag]] + (dispatch [:navigate-to :discovery-tag]) + (assoc db :current-tag tag))) + +;; todo remove this +(register-handler :create-fake-discovery! + (u/side-effect! + (fn [_ _] + (let [number (rand-int 999) + discovery {:name (str "Name " number) + :status (str "Status This is some longer status to get the second line " number) + :whisper-id (str number) + :photo "" + :location "" + :tags [{:name "tag1"} + {:name "tag2"} + {:name "tag3"}] + :last-updated (new js/Date)}] + (dispatch [:add-discovery discovery]))))) + +(defn add-discovery + [db [_ discovery]] + (-> db + (assoc :new-discovery discovery) + (update :discoveries conj discovery))) + +(defn save-discovery! + [{:keys [new-discovery]} _] + (discoveries/save-discoveries [new-discovery])) + +(defn reload-tags! + [db _] + (assoc db :tags (discoveries/all-tags))) + +(register-handler :add-discovery + (-> add-discovery + ((after save-discovery!)) + ((enrich reload-tags!)))) diff --git a/src/syng_im/discovery/model.cljs b/src/syng_im/discovery/model.cljs new file mode 100644 index 0000000000..40d92407b2 --- /dev/null +++ b/src/syng_im/discovery/model.cljs @@ -0,0 +1,89 @@ +(ns syng-im.discovery.model + ;syng-im.models.discoveries + (:require [syng-im.utils.logging :as log] + [syng-im.persistence.realm :as realm] + [syng-im.persistence.realm :as r])) + +(defn get-tag [tag] + (log/debug "Getting tag: " tag) + (-> (r/get-by-field :tag :name tag) + (r/single-cljs))) + +(defn decrease-tag-counter [tag] + (let [tag (:name tag) + tag-object (get-tag tag)] + (if tag-object + (let [counter (dec (:count tag-object))] + (if (zero? counter) + (realm/delete tag-object) + (realm/create :tag {:name tag + :count counter} + true)))))) + +(defn increase-tag-counter [tag] + (let [tag (:name tag) + tag-object (get-tag tag)] + (if tag-object + (realm/create :tag {:name tag + :count (inc (:count tag-object))} + true)))) + +(defn decrease-tags-counter [tags] + (doseq [tag tags] + (decrease-tag-counter tag))) + +(defn increase-tags-counter [tags] + (doseq [tag tags] + (increase-tag-counter tag))) + +(defn get-tags [whisper-id] + (:tags (-> (r/get-by-field :discoveries :whisper-id whisper-id) + (r/single-cljs)))) + +(defn- create-discovery [{:keys [tags] :as discovery}] + (log/debug "Creating discovery: " discovery tags) + (realm/create :discoveries discovery true) + (increase-tags-counter tags)) + +(defn- update-discovery [{:keys [whisper-id tags] :as discovery}] + (let [old-tags (get-tags whisper-id) + tags (map :name tags)] + (decrease-tags-counter old-tags) + (realm/create :discoveries discovery true) + (increase-tags-counter tags))) + +(defn- discovery-exist? [discoveries discovery] + (some #(= (:whisper-id discovery) (:whisper-id %)) discoveries)) + +(defn discovery-list [] + (->> (-> (r/get-all :discoveries) + (r/sorted :last-updated :desc) + r/collection->map) + (map #(update % :tags vals)))) + +(defn- add-discoveries [discoveries] + (realm/write (fn [] + (let [db-discoveries (discovery-list)] + (mapv (fn [discovery] + (if-not (discovery-exist? db-discoveries + discovery) + (create-discovery discovery) + (update-discovery discovery))) + discoveries))))) + +(defn save-discoveries [discoveries] + (add-discoveries discoveries)) + +(defn discoveries-by-tag [tag limit] + (let [discoveries (-> (r/get-by-filter :discoveries (str "tags.name = '" tag "'")) + (r/sorted :last-updated :desc))] + (log/debug "Discoveries by tag: " tag) + (if (pos? limit) + (r/page discoveries 0 limit) + discoveries))) + +(defn all-tags [] + (-> (r/get-all :tag) + (r/sorted :count :desc) + r/collection->map)) + diff --git a/src/syng_im/discovery/screen.cljs b/src/syng_im/discovery/screen.cljs new file mode 100644 index 0000000000..8f51f391ac --- /dev/null +++ b/src/syng_im/discovery/screen.cljs @@ -0,0 +1,52 @@ +(ns syng-im.discovery.screen + (:require-macros [syng-im.utils.views :refer [defview]]) + (:require + [re-frame.core :refer [dispatch subscribe]] + [syng-im.components.react :refer [view + scroll-view + text + text-input]] + [syng-im.components.toolbar :refer [toolbar]] + [syng-im.discovery.views.popular :refer [popular]] + [syng-im.discovery.views.recent :refer [discovery-recent]] + [syng-im.discovery.styles :as st])) + +(defn get-hashtags [status] + (let [hashtags (map #(subs % 1) (re-seq #"#[^ !?,;:.]+" status))] + (or hashtags []))) + +(defn title-content [show-search] + [view st/discovery-toolbar-content + (if show-search + [text-input {:style st/discovery-search-input + :autoFocus true + :placeholder "Type your search tags here" + :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"]])]) + +(defn toogle-search [current-value] + (dispatch [:set ::show-search (not current-value)])) + +(defview discovery [] + [show-search [:get ::show-search]] + [view st/discovery-container + [toolbar + {:style st/discovery-toolbar + :nav-action {:image {:source {:uri :icon_hamburger} + :style st/hamburger-icon} + :handler #(dispatch [:create-fake-discovery!])} + :custom-content [title-content show-search] + :action {:image {:source {:uri :icon_search} + :style st/search-icon} + :handler #(toogle-search show-search)}}] + [scroll-view {:style {}} + [view st/section-spacing + [text {:style st/discovery-subtitle} "Popular tags"]] + [popular] + [view st/section-spacing + [text {:style st/discovery-subtitle} "Recent"]] + [discovery-recent]]]) diff --git a/src/syng_im/components/discovery/styles.cljs b/src/syng_im/discovery/styles.cljs similarity index 86% rename from src/syng_im/components/discovery/styles.cljs rename to src/syng_im/discovery/styles.cljs index 0044f86c94..11a76ab13c 100644 --- a/src/syng_im/components/discovery/styles.cljs +++ b/src/syng_im/discovery/styles.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.discovery.styles +(ns syng-im.discovery.styles (:require [syng-im.components.styles :refer [font title-font color-white @@ -17,7 +17,7 @@ :borderBottomColor "#eff2f3"}) (def row - {:flexDirection "row"}) + {:flexDirection :row}) (def column {:flexDirection "column"}) @@ -39,8 +39,8 @@ (def discovery-title {:color "#000000de" - :alignSelf "center" - :textAlign "center" + :alignSelf :center + :textAlign :center :fontFamily "sans-serif" :fontSize 16}) @@ -76,8 +76,8 @@ :fontSize 14 :paddingRight 5 :paddingBottom 2 - :alignItems "center" - :justifyContent "center"}) + :alignItems :center + :justifyContent :center}) (def tag-name-container {:flexDirection "column" @@ -91,8 +91,8 @@ :fontSize 12 :paddingRight 5 :paddingBottom 2 - :alignItems "center" - :justifyContent "center"}) + :alignItems :center + :justifyContent :center}) (def tag-count-container {:flex 0.2 @@ -103,18 +103,18 @@ (def popular-list-container {:flex 1 - :backgroundColor "white" + :backgroundColor :white :paddingLeft 10 :paddingTop 16}) (def popular-list - {:backgroundColor "white" + {:backgroundColor :white :paddingTop 13}) ;; discover_popular_list_item.cjls (def popular-list-item - {:flexDirection "row" + {:flexDirection :row :paddingTop 10 :paddingBottom 10}) @@ -137,7 +137,7 @@ (def popular-list-item-avatar-container {:flex 0.2 :flexDirection "column" - :alignItems "center" + :alignItems :center :paddingTop 5}) (def popular-list-item-avatar @@ -149,7 +149,7 @@ ;; discovery_recent (def recent-list - {:backgroundColor "white" + {:backgroundColor :white :paddingLeft 15}) ;; discovery_tag @@ -178,3 +178,15 @@ (def icon-search {:width 17 :height 17}) + +(def discovery-container + {:flex 1 + :backgroundColor :#eef2f5}) + +(def hamburger-icon + {:width 16 + :height 12}) + +(def search-icon + {:width 17 + :height 17}) diff --git a/src/syng_im/discovery/subs.cljs b/src/syng_im/discovery/subs.cljs new file mode 100644 index 0000000000..053094706b --- /dev/null +++ b/src/syng_im/discovery/subs.cljs @@ -0,0 +1,20 @@ +(ns syng-im.discovery.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]])) + +(register-sub :get-discoveries-by-tag + (fn [db [_ tag limit]] + (let [discoveries (reaction (:discoveries @db)) + tag' (or tag (:current-tag @db)) + filter-tag (filter #(some #{tag'} (map :name (:tags %)))) + xform (if limit + (comp filter-tag (take limit)) + filter-tag)] + (->> @discoveries + (into [] xform) + (reaction))))) + +(register-sub :get-popular-tags + (fn [db [_ limit]] + (reaction (take limit (:tags @db))))) + diff --git a/src/syng_im/discovery/tag.cljs b/src/syng_im/discovery/tag.cljs new file mode 100644 index 0000000000..455adce8fc --- /dev/null +++ b/src/syng_im/discovery/tag.cljs @@ -0,0 +1,42 @@ +(ns syng-im.discovery.tag + (:require + [re-frame.core :refer [subscribe dispatch]] + [syng-im.utils.logging :as log] + [syng-im.utils.listview :refer [to-datasource]] + [syng-im.components.react :refer [view text list-view list-item]] + [syng-im.components.toolbar :refer [toolbar]] + [syng-im.discovery.views.popular-list-item :refer [popular-list-item]] + [syng-im.discovery.styles :as st])) + +(defn render-row [row _ _] + (list-item [popular-list-item row])) + +(defn render-separator [_ row-id _] + (list-item [view {:style st/row-separator + :key row-id}])) + +(defn title-content [tag] + [view st/tag-title-container + [text {:style st/tag-title} (str " #" tag)]]) + +(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)] + [view st/discovery-tag-container + [toolbar {:nav-action {:image {:source {:uri :icon_back} + :style st/icon-back} + :handler #(dispatch [:navigate-back])} + :title "Add Participants" + :content (title-content @tag) + :action {:image {:source {:uri :icon_search} + :style st/icon-search} + :handler (fn [])}}] + + [list-view {:dataSource datasource + :renderRow render-row + :renderSeparator render-separator + :style st/recent-list}]])))) diff --git a/src/syng_im/discovery/views/popular.cljs b/src/syng_im/discovery/views/popular.cljs new file mode 100644 index 0000000000..8d95e8d094 --- /dev/null +++ b/src/syng_im/discovery/views/popular.cljs @@ -0,0 +1,22 @@ +(ns syng-im.discovery.views.popular + (:require-macros [syng-im.utils.views :refer [defview]]) + (:require + [re-frame.core :refer [subscribe]] + [syng-im.utils.logging :as log] + [syng-im.components.react :refer [android? + text]] + [syng-im.components.carousel.carousel :refer [carousel]] + [syng-im.discovery.styles :as st] + [syng-im.discovery.views.popular-list :refer [discovery-popular-list]])) + +(defn page-width [] + (.-width (.get (.. js/React -Dimensions) "window"))) + +(defview popular [] + [popular-tags [:get-popular-tags 3]] + (if (pos? (count popular-tags)) + [carousel {:pageStyle st/carousel-page-style + :sneak 20} + (for [{:keys [name count]} popular-tags] + [discovery-popular-list name count])] + [text "None"])) diff --git a/src/syng_im/discovery/views/popular_list.cljs b/src/syng_im/discovery/views/popular_list.cljs new file mode 100644 index 0000000000..78426570f6 --- /dev/null +++ b/src/syng_im/discovery/views/popular_list.cljs @@ -0,0 +1,34 @@ +(ns syng-im.discovery.views.popular-list + (:require-macros [syng-im.utils.views :refer [defview]]) + (:require + [re-frame.core :refer [subscribe dispatch]] + [syng-im.components.react :refer [view + list-view + list-item + touchable-highlight + text]] + [syng-im.discovery.styles :as st] + [syng-im.utils.listview :refer [to-datasource]] + [syng-im.discovery.views.popular-list-item :refer [popular-list-item]])) + +(defn render-row [row _ _] + (list-item [popular-list-item row])) + +(defn render-separator [_ row-id _] + (list-item [view {:style st/row-separator + :key row-id}])) + +(defview discovery-popular-list [tag count] + [discoveries [:get-discoveries-by-tag tag 3]] + [view st/popular-list-container + [view st/row + [view st/tag-name-container + [touchable-highlight {:onPress #(dispatch [:show-discovery-tag tag])} + [text {:style st/tag-name} (str " #" (name tag))]]] + [view st/tag-count-container + [text {:style st/tag-count} count]]] + [list-view {:dataSource (to-datasource discoveries) + :enableEmptySections true + :renderRow render-row + :renderSeparator render-separator + :style st/popular-list}]]) diff --git a/src/syng_im/discovery/views/popular_list_item.cljs b/src/syng_im/discovery/views/popular_list_item.cljs new file mode 100644 index 0000000000..6812cf4d1c --- /dev/null +++ b/src/syng_im/discovery/views/popular_list_item.cljs @@ -0,0 +1,15 @@ +(ns syng-im.discovery.views.popular-list-item + (:require [syng-im.components.react :refer [view text image]] + [syng-im.discovery.styles :as st] + [reagent.core :as r])) + +(defn popular-list-item + [{:keys [name status]}] + [view st/popular-list-item + [view st/popular-list-item-name-container + [text {:style st/popular-list-item-name} name] + [text {:style st/popular-list-item-status + :numberOfLines 2} status]] + [view st/popular-list-item-avatar-container + [image {:style st/popular-list-item-avatar + :source {:uri :icon_avatar}}]]]) diff --git a/src/syng_im/discovery/views/recent.cljs b/src/syng_im/discovery/views/recent.cljs new file mode 100644 index 0000000000..e690890b2b --- /dev/null +++ b/src/syng_im/discovery/views/recent.cljs @@ -0,0 +1,23 @@ +(ns syng-im.discovery.views.recent + (:require-macros [syng-im.utils.views :refer [defview]]) + (:require + [re-frame.core :refer [subscribe]] + [syng-im.components.react :refer [view list-view list-item]] + [syng-im.utils.listview :refer [to-datasource]] + [syng-im.discovery.styles :as st] + [syng-im.discovery.views.popular-list-item + :refer [popular-list-item]])) + +(defn render-row [row _ _] + (list-item [popular-list-item row])) + +(defn render-separator [_ row-id _] + (list-item [view {:style st/row-separator + :key row-id}])) + +(defview discovery-recent [] + [discoveries [:get :discoveries]] + [list-view {:dataSource (to-datasource discoveries) + :renderRow render-row + :renderSeparator render-separator + :style st/recent-list}]) diff --git a/src/syng_im/handlers.cljs b/src/syng_im/handlers.cljs index 396cf9839c..2da34d7660 100644 --- a/src/syng_im/handlers.cljs +++ b/src/syng_im/handlers.cljs @@ -2,6 +2,7 @@ (:require [re-frame.core :refer [register-handler after dispatch debug enrich]] [schema.core :as s :include-macros true] + [syng-im.db :refer [app-db schema]] [syng-im.persistence.simple-kv-store :as kv] [syng-im.protocol.state.storage :as storage] [syng-im.db :as db :refer [app-db schema]] @@ -9,31 +10,16 @@ [syng-im.protocol.protocol-handler :refer [make-handler]] [syng-im.models.protocol :refer [update-identity set-initialized]] - [syng-im.models.user-data :as user-data] [syng-im.models.contacts :as contacts] [syng-im.models.messages :refer [save-message update-message! message-by-id get-messages clear-history]] - [syng-im.models.commands :as commands :refer [set-chat-command - set-response-chat-command - set-chat-command-content - set-chat-command-request - stage-command - unstage-command - set-commands]] + [syng-im.models.commands :refer [set-commands]] [syng-im.handlers.server :as server] - [syng-im.handlers.contacts :as contacts-service] - [syng-im.handlers.suggestions :refer [get-command - handle-command - get-command-handler - load-commands - apply-staged-commands - check-suggestion - switch-command-suggestions]] + [syng-im.chat.suggestions :refer [load-commands]] [syng-im.handlers.sign-up :as sign-up-service] - [syng-im.components.discovery.handlers :as discovery] [syng-im.models.chats :refer [chat-exists? create-chat set-group-chat-name @@ -44,17 +30,7 @@ set-chat-active delete-chat re-join-group-chat - chat-by-id2] :as chats] - [syng-im.models.chat :refer [signal-chat-updated - set-current-chat-id - current-chat-id - update-new-group-selection - update-new-participants-selection - clear-new-group - clear-new-participants - new-group-selection - set-chat-input-text - new-participants-selection]] + chat-by-id2]] [syng-im.utils.logging :as log] [syng-im.protocol.api :as api] [syng-im.constants :refer [text-content-type @@ -64,8 +40,10 @@ nav-pop]] [syng-im.utils.crypt :refer [gen-random-bytes]] [syng-im.utils.random :as random] - [clojure.string :as str] - [syng-im.components.react :as r])) + syng-im.chat.handlers + [syng-im.navigation.handlers :as nav] + syng-im.discovery.handlers + syng-im.contacts.handlers)) ;; -- Middleware ------------------------------------------------------------ ;; @@ -83,10 +61,19 @@ ;; -- Common -------------------------------------------------------------- +(register-handler :set + (debug + (fn [db [_ k v]] + (assoc db k v)))) + +(defn preload-data! + [{:keys [view-id] :as db} _] + (nav/preload-data! db [nil view-id])) + (register-handler :initialize-db (fn [_ _] (assoc app-db - :signed-up (storage/get kv/kv-store :signed-up)))) + :signed-up (storage/get kv/kv-store :signed-up)))) (register-handler :set-loading (fn [db [_ value]] @@ -124,11 +111,6 @@ (log/debug action commands) (set-commands db commands))) -(register-handler :set-show-actions - (fn [db [action show-actions]] - (log/debug action) - (assoc-in db db/show-actions-path show-actions))) - ;; -- Protocol -------------------------------------------------------------- (register-handler :initialize-protocol @@ -142,55 +124,6 @@ (update-identity identity) (set-initialized true)))) -(defn gen-messages [n] - (mapv (fn [_] - (let [id (random-uuid)] - {:msg-id id - :content (str id - "ooops sdfg dsfg" - "s dfg\ndsfg dfg\ndsfgdsfgdsfg") - :content-type text-content-type - :outgoing false - :from "console" - :to "me"})) (range n))) - -(defn store-message! - [{:keys [new-message]} [_ {chat-id :from}]] - (save-message chat-id new-message)) - -(defn add-message-to-db - [db chat-id message] - (let [messages [:chats chat-id :messages]] - (update-in db messages conj message))) - -(defn check-author-direction - [db chat-id {:keys [from outgoing] :as message}] - (let [previous-message (first (get-in db [:chats chat-id :messages]))] - (merge message - {:same-author (if previous-message - (= (:from previous-message) from) - true) - :same-direction (if previous-message - (= (:outgoing previous-message) outgoing) - true)}))) - -(defn receive-message - [db [_ {chat-id :from :as message}]] - (let [message' (check-author-direction db chat-id message)] - (-> db - (add-message-to-db chat-id message') - (assoc :new-message message')))) - -(register-handler :received-msg - (-> receive-message - ((after store-message!)))) - -(register-handler :group-received-msg - (fn [db [action {chat-id :group-id :as msg}]] - (log/debug action "msg" msg) - (save-message chat-id msg) - (signal-chat-updated db chat-id))) - (defn system-message [msg-id content] {:from "system" :msg-id msg-id @@ -248,154 +181,43 @@ (register-handler :group-chat-invite-acked (fn [db [action from group-id ack-msg-id]] (log/debug action from group-id ack-msg-id) - (joined-chat-msg group-id from ack-msg-id) - (signal-chat-updated db group-id))) + (joined-chat-msg group-id from ack-msg-id))) (register-handler :participant-removed-from-group (fn [db [action from group-id identity msg-id]] (log/debug action msg-id from group-id identity) (chat-remove-participants group-id [identity]) - (participant-removed-from-group-msg group-id identity from msg-id) - (signal-chat-updated db group-id))) + (participant-removed-from-group-msg group-id identity from msg-id))) (register-handler :you-removed-from-group (fn [db [action from group-id msg-id]] (log/debug action msg-id from group-id) (you-removed-from-group-msg group-id from msg-id) - (set-chat-active group-id false) - (signal-chat-updated db group-id))) + (set-chat-active group-id false))) (register-handler :participant-left-group (fn [db [action from group-id msg-id]] (log/debug action msg-id from group-id) (if (= (api/my-identity) from) db - (do (participant-left-group-msg group-id from msg-id) - (signal-chat-updated db group-id))))) + (participant-left-group-msg group-id from msg-id)))) (register-handler :participant-invited-to-group (fn [db [action from group-id identity msg-id]] (log/debug action msg-id from group-id identity) - (participant-invited-to-group-msg group-id identity from msg-id) - (signal-chat-updated db group-id))) + (participant-invited-to-group-msg group-id identity from msg-id))) (register-handler :acked-msg (fn [db [action from msg-id]] (log/debug action from msg-id) (update-message! {:msg-id msg-id - :delivery-status :delivered}) - (signal-chat-updated db from))) + :delivery-status :delivered}))) (register-handler :msg-delivery-failed (fn [db [action msg-id]] (log/debug action msg-id) (update-message! {:msg-id msg-id - :delivery-status :failed}) - (let [{:keys [chat-id]} (message-by-id msg-id)] - (signal-chat-updated db chat-id)))) - -(defn console? [s] - (= "console" s)) - -(def not-console? - (complement console?)) - -(defn prepare-message - [{:keys [identity current-chat-id] :as db} _] - (let [text (get-in db [:chats current-chat-id :input-text]) - {:keys [command]} (check-suggestion db (str text " ")) - message (check-author-direction - db current-chat-id - {:msg-id (random/id) - :chat-id current-chat-id - :content text - :to current-chat-id - :from identity - :content-type text-content-type - :outgoing true})] - (if command - (set-chat-command db command) - (assoc db :new-message (when-not (str/blank? text) message))))) - -(defn prepare-command [identity chat-id staged-command] - (let [command-key (get-in staged-command [:command :command]) - content {:command (name command-key) - :content (:content staged-command)}] - {:msg-id (random/id) - :from identity - :to chat-id - :content content - :content-type content-type-command - :outgoing true - :handler (:handler staged-command)})) - -(defn prepare-staged-commans - [{:keys [current-chat-id identity] :as db} _] - (let [staged-commands (get-in db [:chats current-chat-id :staged-commands])] - (->> staged-commands - (map #(prepare-command identity current-chat-id %)) - ;todo this is wrong :( - (map #(check-author-direction db current-chat-id %)) - (assoc db :new-commands)))) - -(defn add-message - [{:keys [new-message current-chat-id] :as db}] - (if new-message - (add-message-to-db db current-chat-id new-message) - db)) - -(defn add-commands - [{:keys [new-commands current-chat-id] :as db}] - (reduce - #(add-message-to-db %1 current-chat-id %2) - db - new-commands)) - -(defn clear-input - [{:keys [current-chat-id new-message] :as db} _] - (if new-message - (assoc-in db [:chats current-chat-id :input-text] nil) - db)) - -(defn clear-staged-commands - [{:keys [current-chat-id] :as db} _] - (assoc-in db [:chats current-chat-id :staged-commands] [])) - -(defn send-message! - [{:keys [new-message current-chat-id]} _] - (when (and new-message (not-console? current-chat-id)) - (api/send-user-msg {:to current-chat-id - :content (:content new-message)}))) - -(defn save-message-to-realm! - [{:keys [new-message current-chat-id]} _] - (when new-message - (save-message current-chat-id new-message))) - -(defn save-commands-to-realm! - [{:keys [new-commands current-chat-id]} _] - (doseq [new-command new-commands] - (save-message current-chat-id (dissoc new-command :handler)))) - -(defn handle-commands - [{:keys [new-commands]}] - (doseq [{{content :content} :content - handler :handler} new-commands] - (when handler - (handler content)))) - -(register-handler :send-chat-msg - (-> prepare-message - ((enrich prepare-staged-commans)) - ((enrich add-message)) - ((enrich add-commands)) - ((enrich clear-input)) - ((enrich clear-staged-commands)) - ((after (fn [_ _] (r/dismiss-keyboard!)))) - ((after send-message!)) - ((after save-message-to-realm!)) - ((after save-commands-to-realm!)) - ((after handle-commands)))) + :delivery-status :failed}))) (register-handler :clear-history (fn [db [action]] @@ -407,207 +229,70 @@ (register-handler :leave-group-chat (fn [db [action]] (log/debug action) - (let [chat-id (current-chat-id db)] + (let [chat-id (:current-chat-id db)] (api/leave-group-chat chat-id) (set-chat-active chat-id false) (left-chat-msg chat-id) (delete-chat chat-id) - (dispatch [:navigate-back]) - (signal-chat-updated db chat-id)))) - -(register-handler :send-group-chat-msg - (fn [db [action chat-id text]] - (log/debug action "chat-id" chat-id "text" text) - (let [{msg-id :msg-id - {from :from} :msg} (api/send-group-user-msg {:group-id chat-id - :content text}) - msg {:msg-id msg-id - :from from - :to nil - :content text - :content-type text-content-type - :outgoing true}] - (save-message chat-id msg) - (signal-chat-updated db chat-id)))) + (dispatch [:navigate-back])))) ;; -- User data -------------------------------------------------------------- - -(register-handler :set-user-phone-number - (fn [db [_ value]] - (assoc db :user-phone-number value))) - (register-handler :load-user-phone-number (fn [db [_]] - (user-data/load-phone-number) - db)) - -;; -- Sign up -------------------------------------------------------------- - -(register-handler :sign-up - (-> (fn [db [_ phone-number]] - (assoc db :user-phone-number phone-number)) - ((after (fn [& _] (sign-up-service/on-sign-up-response)))))) - -(register-handler :sign-up-confirm - (fn [db [_ confirmation-code]] - (sign-up-service/on-send-code-response confirmation-code) - db)) - -(register-handler :sync-contacts - (fn [db [_ handler]] - (contacts-service/sync-contacts handler) - db)) - -;; -- Contacts -------------------------------------------------------------- - -(register-handler :load-syng-contacts - (fn [db [_ value]] - (contacts/load-syng-contacts db))) - -(register-handler :show-profile - (fn [db [action identity]] - (log/debug action) - (let [db (contacts/set-contact-identity db identity)] - (dispatch [:navigate-to :profile]) - db))) - -(register-handler :show-my-profile - (fn [db [action]] - (log/debug action) - (dispatch [:navigate-to :my-profile]) - db)) + ;; todo fetch phone number from db + (assoc db :user-phone-number "123"))) ;; -- Chats -------------------------------------------------------------- - -(register-handler :show-chat - (fn [db [action chat-id navigator nav-type]] - (log/debug action "chat-id" chat-id) - (let [db (-> db - (create-chat chat-id [chat-id] false) - (set-current-chat-id chat-id))] - ;; (dispatch [:navigate-to navigator {:view-id :chat} nav-type]) - (dispatch [:navigate-to :chat]) - db))) - -(register-handler :init-console-chat - (fn [db [_]] - (sign-up-service/init db))) - -(register-handler :set-signed-up - (fn [db [_ signed-up]] - (sign-up-service/set-signed-up db signed-up))) - -;; -- Chat -------------------------------------------------------------- - -(defn update-text [db [_ text]] - (set-chat-input-text db text)) - -(defn update-command [db [_ text]] - (let [{:keys [command]} (check-suggestion db text)] - (set-chat-command db command))) - -(register-handler :set-chat-input-text - ((enrich update-command) update-text)) - -(register-handler :switch-command-suggestions - (fn [db [_]] - (switch-command-suggestions db))) - -(register-handler :set-chat-command - (fn [db [_ command-key]] - ;; todo what is going on there?! - (set-chat-command db command-key))) - -(register-handler :stage-command - (fn [{:keys [current-chat-id] :as db} _] - (let [db (set-chat-input-text db nil) - {:keys [command content]} - (get-in db [:chats current-chat-id :command-input]) - command-info {:command command - :content content - :handler (:handler command)}] - (stage-command db command-info)))) - -(register-handler :unstage-command - (fn [db [_ staged-command]] - (let [] - (unstage-command db staged-command)))) - -(register-handler :set-response-chat-command - (fn [db [_ to-msg-id command-key]] - (set-response-chat-command db to-msg-id command-key))) - -(register-handler :set-chat-command-content - (fn [db [_ content]] - (set-chat-command-content db content))) - -(register-handler :set-chat-command-request - (fn [db [_ msg-id handler]] - (set-chat-command-request db msg-id handler))) - -(register-handler :show-contacts - (fn [db [action navigator]] - (nav-push navigator {:view-id :contact-list}) - db)) +(defn update-new-participants-selection [db identity add?] + (update db :new-participants (fn [new-participants] + (if add? + (conj new-participants identity) + (disj new-participants identity))))) (register-handler :select-new-participant (fn [db [action identity add?]] (log/debug action identity add?) (update-new-participants-selection db identity add?))) -(register-handler :show-remove-participants +(register-handler :remove-selected-participants (fn [db [action]] (log/debug action) - (dispatch [:navigate-to :remove-participants]) - (clear-new-participants db))) - -(register-handler :remove-selected-participants - (fn [db [action navigator]] - (log/debug action) - (let [identities (vec (new-participants-selection db)) - chat-id (current-chat-id db)] + (let [identities (vec (:new-participants db)) + chat-id (:current-chat-id db)] (chat-remove-participants chat-id identities) (dispatch [:navigate-back]) (doseq [ident identities] (api/group-remove-participant chat-id ident) - (removed-participant-msg chat-id ident)) - (signal-chat-updated db chat-id)))) - -(register-handler :show-add-participants - (fn [db [action]] - (log/debug action) - (dispatch [:navigate-to :add-participants]) - (clear-new-participants db))) + (removed-participant-msg chat-id ident))))) (register-handler :add-new-participants (fn [db [action navigator]] (log/debug action) - (let [identities (vec (new-participants-selection db)) - chat-id (current-chat-id db)] + (let [identities (vec (:new-participants db)) + chat-id (:current-chat-id db)] (chat-add-participants chat-id identities) (dispatch [:navigate-back]) (doseq [ident identities] (api/group-add-participant chat-id ident)) db))) -(register-handler :show-group-new - (fn [db [action]] - (log/debug action) - (dispatch [:navigate-to :new-group]) - (clear-new-group db))) +(defn update-new-group-selection [db identity add?] + (update-in db :new-group (fn [new-group] + (if add? + (conj new-group identity) + (disj new-group identity))))) (register-handler :select-for-new-group - (fn [db [action identity add?]] - (log/debug action identity add?) + (fn [db [_ identity add?]] (update-new-group-selection db identity add?))) (register-handler :create-new-group - (fn [db [action group-name navigator]] + (fn [db [action group-name]] (log/debug action) - (let [identities (vec (new-group-selection db)) + (let [identities (vec (:new-group db)) group-id (api/start-group-chat identities group-name) db (create-chat db group-id identities true group-name)] - (dispatch [:show-chat group-id navigator :replace]) + (dispatch [:show-chat group-id :replace]) db))) (register-handler :show-group-settings @@ -653,77 +338,3 @@ (if (chat-exists? group-id) (re-join-group-chat db group-id identities group-name) (create-chat db group-id identities true group-name)))) - -(register-handler :navigate-to - (fn [db [_ view-id]] - (-> db - (assoc :view-id view-id) - (update :navigation-stack conj view-id)))) - -(register-handler :navigate-back - (fn [{:keys [navigation-stack] :as db} _] - (log/debug :navigate-back) - (if (>= 1 (count navigation-stack)) - db - (let [[view-id :as navigation-stack'] (pop navigation-stack)] - (-> db - (assoc :view-id view-id) - (assoc :navigation-stack navigation-stack')))))) - -(register-handler :load-more-messages - (fn [db _] - db - ;; TODO implement - #_(let [chat-id (get-in db [:chat :current-chat-id]) - messages [:chats chat-id :messages] - new-messages (gen-messages 10)] - (update-in db messages concat new-messages)))) - -(defn load-messages! - [db _] - db - (->> (current-chat-id db) - get-messages - (assoc db :messages))) - -(defn init-chat - [{:keys [messages] :as db} _] - (let [id (current-chat-id db)] - (assoc-in db [:chats id :messages] messages))) - -(register-handler :init-chat - (-> load-messages! - ((enrich init-chat)) - debug)) - -(defn initialize-chats - [{:keys [loaded-chats] :as db} _] - (let [chats (->> loaded-chats - (map (fn [{:keys [chat-id] :as chat}] - [chat-id chat])) - (into {}))] - (-> db - (assoc :chats chats) - (dissoc :loaded-chats)))) - -(defn load-chats! - [db _] - (assoc db :loaded-chats (chats/chats-list))) - -(register-handler :initialize-chats - ((enrich initialize-chats) load-chats!)) - -(defn safe-trim [s] - (when (string? s) - (str/trim s))) - -(register-handler :cancel-command - (fn [{:keys [current-chat-id] :as db} _] - (-> db - (assoc-in [:chats current-chat-id :command-input] {}) - (update-in [:chats current-chat-id :input-text] safe-trim)))) - -(register-handler :save-password - (fn [db [_ password]] - (sign-up-service/save-password password) - (assoc db :password-saved true))) diff --git a/src/syng_im/handlers/contacts.cljs b/src/syng_im/handlers/contacts.cljs deleted file mode 100644 index 53b0146a49..0000000000 --- a/src/syng_im/handlers/contacts.cljs +++ /dev/null @@ -1,64 +0,0 @@ -(ns syng-im.handlers.contacts - (:require-macros [cljs.core.async.macros :refer [go]]) - (:require [clojure.string :as cstr] - [cljs.core.async :as async :refer [chan put! clj (:contacts data)))) - -(defn- get-contacts-by-hash [contacts] - (let [numbers-info (reduce (fn [numbers contact] - (into numbers - (map (fn [c] - {:number (format-phone-number (:number c)) - :contact contact}) - (:phone-numbers contact)))) - '() - contacts)] - (reduce (fn [m number-info] - (let [number (:number number-info) - hash (encrypt number)] - (assoc m hash number-info))) - {} - numbers-info))) - -(defn- request-syng-contacts [contacts] - (let [contacts-by-hash (get-contacts-by-hash contacts) - data (keys contacts-by-hash) - ch (chan)] - (http-post "get-contacts" {:phone-number-hashes data} - (fn [data] - (put! ch - (to-syng-contacts contacts-by-hash data)))) - ch)) - -(defn sync-contacts [handler] - (go - (let [result ( db (assoc-in [:chats "console"] console-chat) (assoc :new-chat console-chat) - (set-current-chat-id "console") + (assoc :current-chat-id "console") (intro)))))) diff --git a/src/syng_im/ios/core.cljs b/src/syng_im/ios/core.cljs index 02147cce96..c84d1c2404 100644 --- a/src/syng_im/ios/core.cljs +++ b/src/syng_im/ios/core.cljs @@ -20,13 +20,13 @@ (defn app-root [] (let [greeting (subscribe [:get-greeting])] (fn [] - [view {:style {:flex-direction "column" :margin 40 :align-items "center"}} - [text {:style {:font-size 30 :font-weight "100" :margin-bottom 20 :text-align "center"}} @greeting] + [view {:style {:flex-direction "column" :margin 40 :align-items :center}} + [text {:style {:font-size 30 :font-weight "100" :margin-bottom 20 :text-align :center}} @greeting] [image {:source logo-img :style {:width 80 :height 80 :margin-bottom 30}}] [touchable-highlight {:style {:background-color "#999" :padding 10 :border-radius 5} :on-press #(alert "HELLO!")} - [text {:style {:color "white" :text-align "center" :font-weight "bold"}} "press me"]]]))) + [text {:style {:color :white :text-align :center :font-weight "bold"}} "press me"]]]))) (defn init [] (dispatch-sync [:initialize-db]) diff --git a/src/syng_im/models/chat.cljs b/src/syng_im/models/chat.cljs deleted file mode 100644 index 09bf425768..0000000000 --- a/src/syng_im/models/chat.cljs +++ /dev/null @@ -1,58 +0,0 @@ -(ns syng-im.models.chat - (:require [syng-im.db :as db])) - -(defn set-current-chat-id [db chat-id] - (assoc-in db db/current-chat-id-path chat-id)) - -(defn current-chat-id [db] - (get-in db db/current-chat-id-path)) - -(defn current-chat [db] - (get-in db [:chats (current-chat-id db)])) - -(defn signal-chat-updated [db chat-id] - (update-in db (db/updated-chat-signal-path chat-id) (fn [current] - (if current - (inc current) - 0)))) - -(defn chat-updated? [db chat-id] - (get-in db (db/updated-chat-signal-path chat-id))) - -(defn update-new-group-selection [db identity add?] - (update-in db db/new-group-path (fn [new-group] - (if add? - (conj new-group identity) - (disj new-group identity))))) - -(defn update-new-participants-selection [db identity add?] - (update-in db db/new-participants-path (fn [new-participants] - (if add? - (conj new-participants identity) - (disj new-participants identity))))) - -(defn new-group-selection [db] - (get-in db db/new-group-path)) - -(defn clear-new-group [db] - (assoc-in db db/new-group-path #{})) - -(defn new-participants-selection [db] - (get-in db db/new-participants-path)) - -(defn clear-new-participants [db] - (assoc-in db db/new-participants-path #{})) - -(defn set-chat-input-text [db text] - (assoc-in db (db/chat-input-text-path (current-chat-id db)) text)) - -(defn get-chat-input-text [db] - (get-in db (db/chat-input-text-path (current-chat-id db)))) - -(comment - - (swap! re-frame.db/app-db (fn [db] - (signal-chat-updated db "0x0479a5ed1f38cadfad1db6cd56c4b659b0ebe052bbe9efa950f6660058519fa4ca6be2dda66afa80de96ab00eb97a2605d5267a1e8f4c2a166ab551f6826608cdd"))) - - (current-chat-id @re-frame.db/app-db) - ) diff --git a/src/syng_im/models/chats.cljs b/src/syng_im/models/chats.cljs index 481322ba55..c7cc356e2e 100644 --- a/src/syng_im/models/chats.cljs +++ b/src/syng_im/models/chats.cljs @@ -4,7 +4,6 @@ [syng-im.persistence.realm :as r] [syng-im.utils.random :as random :refer [timestamp]] [clojure.string :refer [join blank?]] - [syng-im.db :as db] [syng-im.utils.logging :as log] [syng-im.constants :refer [content-type-status]] [syng-im.models.messages :refer [save-message]] @@ -13,15 +12,6 @@ current-chat signal-chat-updated]])) -(defn signal-chats-updated [db] - (update-in db db/updated-chats-signal-path (fn [current] - (if current - (inc current) - 0)))) - -(defn chats-updated? [db] - (get-in db db/updated-chats-signal-path)) - (defn chat-name-from-contacts [identities] (let [chat-name (->> identities (map (fn [identity] @@ -56,8 +46,6 @@ ([{:keys [last-msg-id] :as chat}] (let [chat (assoc chat :last-msg-id (or last-msg-id ""))] (r/write #(r/create :chats chat)))) - ([db chat-id identities group-chat?] - (create-chat db chat-id identities group-chat? nil)) ([db chat-id identities group-chat? chat-name] (if (chat-exists? chat-id) db @@ -76,7 +64,7 @@ :contacts contacts :last-msg-id ""})))) (add-status-message chat-id) - (signal-chats-updated db))))) + db)))) (defn set-group-chat-name [db name] (let [chat-id (current-chat-id db)] @@ -115,8 +103,7 @@ :is-active true :name group-name :contacts contacts} true)))) - (-> (signal-chats-updated db) - (signal-chat-updated group-id))) + db) (defn normalize-contacts [chats] @@ -158,7 +145,7 @@ chat (r/single (r/get-by-field :chats :chat-id chat-id))] (-> (aget chat "contacts") (r/filtered query) - (.forEach (fn [object index collection] + (.forEach (fn [object _ _] (aset object "is-in-chat" false)))))))) (defn chat-remove-member [db identity] @@ -177,7 +164,7 @@ (defn active-group-chats [] (let [results (r/filtered (r/get-all :chats) "group-chat = true && is-active = true")] - (js->clj (.map results (fn [object index collection] + (js->clj (.map results (fn [object _ _] (aget object "chat-id")))))) diff --git a/src/syng_im/models/commands.cljs b/src/syng_im/models/commands.cljs index ad6ccaccb3..c35492bd0a 100644 --- a/src/syng_im/models/commands.cljs +++ b/src/syng_im/models/commands.cljs @@ -1,13 +1,9 @@ (ns syng-im.models.commands (:require [clojure.string :refer [join split]] [clojure.walk :refer [stringify-keys keywordize-keys]] - [cljs.core.async :as async :refer [chan put! !]] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [re-frame.core :refer [subscribe dispatch]] [syng-im.db :as db] - [syng-im.models.chat :refer [current-chat-id]] - [syng-im.components.styles :refer [color-blue - color-dark-mint]] - [syng-im.utils.utils :refer [log toast]])) + [syng-im.components.styles :refer [color-blue color-dark-mint]])) ;; todo delete (def commands [{:command :money @@ -79,52 +75,56 @@ (defn find-command [commands command-key] (first (filter #(= command-key (:command %)) commands))) -(defn get-chat-command-content [db] - (get-in db (db/chat-command-content-path (current-chat-id db)))) +(defn get-chat-command-content + [{:keys [current-chat-id] :as db}] + (get-in db (db/chat-command-content-path current-chat-id))) -(defn set-chat-command-content [db content] - (assoc-in db - [:chats (get-in db db/current-chat-id-path) :command-input :content] - content)) +(defn set-chat-command-content + [{:keys [current-chat-id] :as db} content] + (assoc-in db [:chats current-chat-id :command-input :content] content)) -(defn get-chat-command [db] - (get-in db (db/chat-command-path (current-chat-id db)))) +(defn get-chat-command + [{:keys [current-chat-id] :as db}] + (get-in db (db/chat-command-path current-chat-id))) -(defn set-response-chat-command [db msg-id command-key] - (let [chat-id (current-chat-id db)] - (-> db - (assoc-in [:chats chat-id :command-input :content] nil) - (assoc-in [:chats chat-id :command-input :command] - (get-command db command-key)) - (assoc-in [:chats chat-id :command-input :to-msg-id] msg-id)))) +(defn set-response-chat-command + [{:keys [current-chat-id] :as db} msg-id command-key] + (update-in db [:chats current-chat-id :command-input] merge + {:content nil + :command (get-command db command-key) + :to-msg-id msg-id})) (defn set-chat-command [db command-key] (set-response-chat-command db nil command-key)) -(defn get-chat-command-to-msg-id [db] - (get-in db (db/chat-command-to-msg-id-path (current-chat-id db)))) +(defn get-chat-command-to-msg-id + [{:keys [current-chat-id] :as db}] + (get-in db (db/chat-command-to-msg-id-path current-chat-id))) -(defn stage-command [db command-info] - (update-in db (db/chat-staged-commands-path (current-chat-id db)) - (fn [staged-commands] - (if staged-commands - (conj staged-commands command-info) - [command-info])))) +(defn stage-command + [{:keys [current-chat-id] :as db} command-info] + (update-in db (db/chat-staged-commands-path current-chat-id) + #(if % + (conj % command-info) + [command-info]))) (defn unstage-command [db staged-command] - (update-in db (db/chat-staged-commands-path (current-chat-id db)) + (update-in db (db/chat-staged-commands-path (:current-chat-id db)) (fn [staged-commands] (filterv #(not= % staged-command) staged-commands)))) -(defn clear-staged-commands [db] - (assoc-in db (db/chat-staged-commands-path (current-chat-id db)) [])) +(defn clear-staged-commands + [{:keys [current-chat-id] :as db}] + (assoc-in db (db/chat-staged-commands-path current-chat-id) [])) -(defn get-chat-command-request [db] - (get-in db (db/chat-command-request-path (current-chat-id db) +(defn get-chat-command-request + [{:keys [current-chat-id] :as db}] + (get-in db (db/chat-command-request-path current-chat-id (get-chat-command-to-msg-id db)))) -(defn set-chat-command-request [db msg-id handler] - (update-in db (db/chat-command-requests-path (current-chat-id db)) +(defn set-chat-command-request + [{:keys [current-chat-id] :as db} msg-id handler] + (update-in db (db/chat-command-requests-path current-chat-id) #(assoc % msg-id handler))) (defn parse-command-msg-content [commands content] diff --git a/src/syng_im/models/contacts.cljs b/src/syng_im/models/contacts.cljs index 30fe492d9b..f47986ebca 100644 --- a/src/syng_im/models/contacts.cljs +++ b/src/syng_im/models/contacts.cljs @@ -1,92 +1,21 @@ (ns syng-im.models.contacts - (:require [cljs.core.async :as async :refer [chan put! !]] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.db :as db] - [syng-im.utils.utils :refer [log toast]] - [syng-im.persistence.realm :as realm] - [syng-im.persistence.realm :as r] + (:require [syng-im.persistence.realm :as r] [syng-im.persistence.realm-queries :refer [include-query - exclude-query]] - [clojure.string :as s])) + exclude-query]])) -;; TODO see https://github.com/rt2zz/react-native-contacts/issues/45 -(def fake-phone-contacts? false) -(def fake-contacts? false) +(defn get-contacts [] + (-> (r/get-all :contacts) + (r/sorted :name :asc) + r/collection->map)) -(def react-native-contacts (js/require "react-native-contacts")) +(defn create-contact [{:keys [name photo-path] :as contact}] + (->> {:name (or name "") + :photo-path (or photo-path "")} + (merge contact) + (r/create :contacts))) -(defn- generate-contact [n] - {:name (str "Contact " n) - :photo-path "" - :phone-numbers [{:label "mobile" :number (apply str (repeat 7 n))}] - :delivery-status (if (< (rand) 0.5) :delivered :seen) - :datetime "15:30" - :new-messages-count (rand-int 3) - :online (< (rand) 0.5)}) - -(defn- generate-contacts [n] - (map generate-contact (range 1 (inc n)))) - -(defn load-phone-contacts [] - (let [ch (chan)] - (if fake-phone-contacts? - (put! ch {:error nil, :contacts (generate-contacts 10)}) - (.getAll react-native-contacts - (fn [error raw-contacts] - (put! ch - {:error error - :contacts - (when (not error) - (log raw-contacts) - (map (fn [contact] - (merge contact - (generate-contact 1) - {:name (:givenName contact) - :photo-path (:thumbnailPath contact) - :phone-numbers (:phoneNumbers contact)})) - (js->clj raw-contacts :keywordize-keys true)))})))) - ch)) - -(defn- get-contacts [] - (if fake-contacts? - [{:phone-number "123" - :whisper-identity "abc" - :name "fake" - :photo-path ""}] - (realm/get-list :contacts))) - -(defn load-syng-contacts [db] - (let [contacts (map (fn [contact] - (merge contact - {:delivery-status (if (< (rand) 0.5) :delivered :seen) - :datetime "15:30" - :new-messages-count (rand-int 3) - :online (< (rand) 0.5)})) - (get-contacts))] - (assoc db :contacts contacts))) - -(defn- create-contact [{:keys [phone-number whisper-identity name photo-path]}] - (realm/create :contacts - {:phone-number phone-number - :whisper-identity whisper-identity - :name (or name "") - :photo-path (or photo-path "")})) - -(defn- contact-exist? [contacts contact] - (some #(= (:phone-number contact) (:phone-number %)) contacts)) - -(defn- add-contacts [contacts] - (realm/write (fn [] - (let [db-contacts (get-contacts)] - (dorun (map (fn [contact] - (if (not (contact-exist? db-contacts contact)) - (create-contact contact) - ;; TODO else override? - )) - contacts)))))) - -(defn save-syng-contacts [syng-contacts] - (add-contacts syng-contacts)) +(defn save-contacts [contacts] + (r/write #(mapv create-contact contacts))) ;;;;;;;;;;;;;;;;;;;;---------------------------------------------- @@ -111,45 +40,4 @@ (r/sorted :name :asc))))) (defn contact-by-identity [identity] - (if (= identity "console") - {:phone-number "" - :whisper-identity "console" - :name "Console" - :photo-path ""} - (r/single-cljs (r/get-by-field :contacts :whisper-identity identity)))) - -;;;;;;;;;;;;;;;;;;;;---------------------------------------------- - -(defn set-contact-identity [db contact-id] - (assoc-in db db/contact-identity-path contact-id)) - -(defn contact-identity [db] - (get-in db db/contact-identity-path)) - -(comment - - (r/write #(create-contact {:phone-number "0543072333" - :whisper-identity "0x04e43e861a6dd99ad9eee7bd58af89dcaa430188ebec8698de7b7bad54573324fff4ac5cb9bb277af317efd7abfc917b91bf48cc41e40bf70062fd79400016a1f9" - :name "Splinter" - :photo-path ""})) - - (r/write #(create-contact {:phone-number "0544828649" - :whisper-identity "0x0487954e7fa746d8cf787403c2c491aadad540b9bb1f0f7b8184792e91c33b6a394079295f5777ec6d4af9ad5ba24794b3ff1ec8be9ff6a708c85a163733192665" - :name "Exodius" - :photo-path ""})) - - (r/write #(create-contact {:phone-number "0522222222" - :whisper-identity "0x0407c278af94e0b4599645023f5bec03cbdb3973bd0ae33b94c6a5885d9d20e82ff3f3c3584a637ba016af40bac2f711fd6028045756f561e36e4b07d0c2b4e623" - :name "Mr. Eagle" - :photo-path ""})) - - (r/write #(create-contact {:phone-number "0533333333" - :whisper-identity "0x04512f852558ea09d09419019f3f443ec03ff2c1913c48e567723d70e5abf239ed87fb62486b90b85e12de5d327501c1993c9905a69f2ca7e1bfbaab12dd033313" - :name "Mr. PiggyBear" - :photo-path ""})) - - (contacts-list) - - (:new-group @re-frame.db/app-db) - - ) + (r/single-cljs (r/get-by-field :contacts :whisper-identity identity))) diff --git a/src/syng_im/models/discoveries.cljs b/src/syng_im/models/discoveries.cljs deleted file mode 100644 index 5a568a11c9..0000000000 --- a/src/syng_im/models/discoveries.cljs +++ /dev/null @@ -1,124 +0,0 @@ -(ns syng-im.models.discoveries - (:require [cljs.core.async :as async :refer [chan put! !]] - [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.utils.logging :as log] - [syng-im.persistence.realm :as realm] - [syng-im.persistence.realm :as r] - [syng-im.db :as db])) - -(defn signal-discoveries-updated [db] - (update-in db db/updated-discoveries-signal-path (fn [current] - (if current - (inc current) - 0)))) - -(defn discoveries-updated? [db] - (get-in db db/updated-discoveries-signal-path)) - -(defn current-tag-updated? [db] - (get-in db db/updated-current-tag-signal-path)) - - - -(defn current-tag [db] - (get-in db db/current-tag-path)) - -(defn set-current-tag [db tag] - (assoc-in db db/current-tag-path tag)) - -(defn get-tag [tag] - (log/debug "Getting tag: " tag) - (-> (r/get-by-field :tag :name tag) - (r/single-cljs))) - -(defn decrease-tag-counter [tag] - (let [tag (:name tag) - tag-object (get-tag tag)] - (if tag-object - (let [counter (dec (:count tag-object))] - (if (= counter 0) - (realm/delete tag-object) - (realm/create :tag {:name tag - :count counter} - true)))))) - -(defn increase-tag-counter [tag] - (let [tag (:name tag) - tag-object (get-tag tag)] - (if tag-object - (realm/create :tag {:name tag - :count (inc (:count tag-object))} - true)))) - -(defn decrease-tags-counter [tags] - (doseq [tag tags] - (decrease-tag-counter tag))) - -(defn increase-tags-counter [tags] - (doseq [tag tags] - (increase-tag-counter tag))) - -(defn get-tags [whisper-id] - (:tags (-> (r/get-by-field :discoveries :whisper-id whisper-id) - (r/single-cljs)))) - -(defn- create-discovery [{:keys [name status whisper-id photo location tags last-updated]}] - (let [tags (mapv (fn [tag] {:name tag}) tags) - discovery {:name name - :status status - :whisper-id whisper-id - :photo photo - :location location - :tags tags - :last-updated last-updated}] - (log/debug "Creating discovery: " discovery tags) - (realm/create :discoveries discovery true) - (increase-tags-counter tags))) - -(defn- update-discovery [{:keys [name status whisper-id photo location tags last-updated]}] - (let [old-tags (get-tags whisper-id) - tags (mapv (fn [tag] {:name tag}) tags) - discovery {:name name - :status status - :whisper-id whisper-id - :photo photo - :location location - :tags tags - :last-updated last-updated}] - (decrease-tags-counter old-tags) - (realm/create :discoveries discovery true) - (increase-tags-counter tags))) - -(defn- discovery-exist? [discoveries discovery] - (some #(= (:whisper-id discovery) (:whisper-id %)) discoveries)) - -(defn discovery-list [] - (-> (r/get-all :discoveries) - (r/sorted :last-updated :desc))) - -(defn- add-discoveries [discoveries] - (realm/write (fn [] - (let [db-discoveries (.slice (discovery-list) 0)] - (dorun (map (fn [discovery] - (if (not (discovery-exist? db-discoveries discovery)) - (create-discovery discovery) - (update-discovery discovery) - )) - discoveries)))))) - -(defn save-discoveries [discoveries] - (add-discoveries discoveries)) - -(defn discoveries-by-tag [tag limit] - (let [discoveries (-> (r/get-by-filter :discoveries (str "tags.name = '" tag "'")) - (r/sorted :last-updated :desc))] - (log/debug "Discoveries by tag: " tag) - (if (pos? limit) - (r/page discoveries 0 limit) - discoveries))) - -(defn get-tag-popular [limit] - (-> (r/get-all :tag) - (r/sorted :count :desc) - (r/page 0 limit))) - diff --git a/src/syng_im/models/messages.cljs b/src/syng_im/models/messages.cljs index 87517068c6..f34a9fc58e 100644 --- a/src/syng_im/models/messages.cljs +++ b/src/syng_im/models/messages.cljs @@ -17,10 +17,6 @@ [s] (keywordize-keys (apply hash-map (split s #"[;=]")))) -(defn select-chat-last-message [chat] - (when-let [last-msg-id (:last-msg-id chat)] - (r/single-cljs (r/get-by-field :msgs :msg-id last-msg-id)))) - (defn save-message [chat-id {:keys [from to msg-id content content-type outgoing same-author same-direction] @@ -57,9 +53,6 @@ (update message :content str-to-map) message))))) -(defn message-by-id [msg-id] - (r/single-cljs (r/get-by-field :msgs :msg-id msg-id))) - (defn update-message! [{:keys [msg-id] :as msg}] (log/debug "update-message!" msg) (r/write diff --git a/src/syng_im/models/protocol.cljs b/src/syng_im/models/protocol.cljs index 301876a0d7..aff7283d97 100644 --- a/src/syng_im/models/protocol.cljs +++ b/src/syng_im/models/protocol.cljs @@ -6,25 +6,19 @@ [syng-im.utils.types :refer [to-edn-string]] [re-frame.db :refer [app-db]] [syng-im.db :as db] - [syng-im.persistence.simple-kv-store :as kv] - [syng-im.utils.logging :as log])) + [syng-im.persistence.simple-kv-store :as kv])) (defn set-initialized [db initialized?] (assoc-in db db/protocol-initialized-path initialized?)) (defn update-identity [db identity] - (let [password (get-in db db/identity-password-path) + (let [password (:identity-password db) encrypted (password-encrypt password (to-edn-string identity))] (s/put kv/kv-store :identity encrypted) (assoc db :user-identity identity))) (defn stored-identity [db] (let [encrypted (s/get kv/kv-store :identity) - password (get-in db db/identity-password-path)] + password (:identity-password db)] (when encrypted (read-string (password-decrypt password encrypted))))) - -(comment - - (stored-identity @re-frame.db/app-db) - ) diff --git a/src/syng_im/models/user_data.cljs b/src/syng_im/models/user_data.cljs deleted file mode 100644 index 463a731b5a..0000000000 --- a/src/syng_im/models/user_data.cljs +++ /dev/null @@ -1,17 +0,0 @@ -(ns syng-im.models.user-data - (:require-macros - [natal-shell.async-storage :refer [get-item set-item]]) - (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] - [syng-im.utils.utils :refer [log on-error toast]])) - -(defn save-phone-number [phone-number] - (set-item "user-phone-number" phone-number) - (dispatch [:set-user-phone-number phone-number])) - -(defn load-phone-number [] - (get-item "user-phone-number" - (fn [error value] - (if error - (on-error error) - (dispatch [:set-user-phone-number (when value - (str value))]))))) diff --git a/src/syng_im/navigation/handlers.cljs b/src/syng_im/navigation/handlers.cljs new file mode 100644 index 0000000000..217bd88136 --- /dev/null +++ b/src/syng_im/navigation/handlers.cljs @@ -0,0 +1,84 @@ +(ns syng-im.navigation.handlers + (:require [re-frame.core :refer [register-handler dispatch debug enrich]])) + +(defn push-view [db view-id] + (-> db + (update :navigation-stack conj view-id) + (assoc :view-id view-id))) + +(defn replace-top-element [stack view-id] + (let [stack' (if (pos? (count stack)) + (pop stack) + stack)] + (conj stack' view-id))) + +(defn replace-view [db view-id] + (-> db + (update :navigation-stack replace-top-element view-id) + (assoc :view-id view-id))) + +(defmulti preload-data! (fn [_ [_ view-id]] view-id)) +(defmethod preload-data! :default [db _] db) + +(register-handler :navigate-to + (enrich preload-data!) + (fn [db [_ view-id]] + (push-view db view-id))) + +(register-handler :navigation-replace + (fn [db [_ view-id]] + (replace-view db view-id))) + +(register-handler :navigate-back + (fn [{:keys [navigation-stack] :as db} _] + (if (>= 1 (count navigation-stack)) + db + (let [[view-id :as navigation-stack'] (pop navigation-stack)] + (-> db + (assoc :view-id view-id) + (assoc :navigation-stack navigation-stack')))))) + +(register-handler :show-group-new + (debug + (fn [db _] + (-> db + (push-view :new-group) + (assoc :new-group #{}))))) + +(register-handler :show-chat + (fn [db [_ chat-id nav-type]] + (let [update-view-id-fn (if (= :replace nav-type) replace-view push-view)] + (-> db + (update-view-id-fn :chat) + (assoc :current-chat-id chat-id))))) + +(register-handler :show-contacts + (fn [db _] + (push-view db :contact-list))) + +(defn clear-new-participants [db] + (assoc-in db :new-participants #{})) + +(register-handler :show-remove-participants + (fn [db _] + (-> db + (push-view :remove-participants) + clear-new-participants))) + +(register-handler :show-add-participants + (fn [db _] + (-> db + (push-view :add-participants) + clear-new-participants))) + +(register-handler :show-profile + (debug + (fn [db [_ identity]] + (let [db (assoc db :contact-identity identity)] + (dispatch [:navigate-to :profile]) + db)))) + +(register-handler :show-my-profile + (fn [db _] + (dispatch [:navigate-to :my-profile]) + db)) diff --git a/src/syng_im/navigation/subs.cljs b/src/syng_im/navigation/subs.cljs new file mode 100644 index 0000000000..d7b2dc4a0a --- /dev/null +++ b/src/syng_im/navigation/subs.cljs @@ -0,0 +1,11 @@ +(ns syng-im.navigation.subs + (:require-macros [reagent.ratom :refer [reaction]]) + (:require [re-frame.core :refer [register-sub]])) + +(register-sub :view-id + (fn [db _] + (reaction (@db :view-id)))) + +(register-sub :navigation-stack + (fn [db _] + (reaction (:navigation-stack @db)))) diff --git a/src/syng_im/new_group/screen.cljs b/src/syng_im/new_group/screen.cljs new file mode 100644 index 0000000000..5e7843c86b --- /dev/null +++ b/src/syng_im/new_group/screen.cljs @@ -0,0 +1,58 @@ +(ns syng-im.new-group.screen + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.resources :as res] + [syng-im.components.react :refer [view + text-input + text + image + icon + touchable-highlight + list-view + list-item]] + [syng-im.components.styles :refer [color-purple]] + [syng-im.components.toolbar :refer [toolbar]] + [syng-im.utils.listview :refer [to-datasource]] + [syng-im.new-group.views.contact :refer [new-group-contact]] + [syng-im.new-group.styles :as st])) + + +(defn new-group-toolbar [] + (let [group-name (subscribe [:get ::group-name])] + (fn [] + [toolbar + {:title "New group chat" + :action {:image {:source res/v ;; {:uri "icon_search"} + :style st/toolbar-icon} + :handler #(dispatch [:create-new-group @group-name])}}]))) + +(defn group-name-input [] + (let [group-name (subscribe [:get ::group-name])] + (fn [] + [text-input + {:underlineColorAndroid color-purple + :style st/group-name-input + :autoFocus true + :placeholder "Group Name" + :onChangeText #(dispatch [:set ::group-name %]) + :onSubmitEditing #(dispatch [:set ::group-name nil])} + @group-name]))) + +(defn new-group [] + (let [contacts (subscribe [:all-contacts])] + (fn [] + (let [contacts-ds (to-datasource @contacts)] + [view st/new-group-container + [new-group-toolbar] + [view st/chat-name-container + [text {:style st/chat-name-text} "Chat name"] + [group-name-input] + [text {:style st/members-text} "Members"] + [touchable-highlight {:on-press (fn [])} + [view st/add-container + [icon :add_gray st/add-icon] + [text {:style st/add-text} "Add members"]]] + [list-view + {:dataSource contacts-ds + :renderRow (fn [row _ _] + (list-item [new-group-contact row])) + :style st/contacts-list}]]])))) diff --git a/src/syng_im/new_group/styles.cljs b/src/syng_im/new_group/styles.cljs new file mode 100644 index 0000000000..695c616ea9 --- /dev/null +++ b/src/syng_im/new_group/styles.cljs @@ -0,0 +1,67 @@ +(ns syng-im.new-group.styles + (:require [syng-im.components.styles :refer [font + title-font + color-white + color-purple + text1-color + text2-color + toolbar-background1]])) + +(def toolbar-icon + {:width 20 + :height 18}) + +(def new-group-container + {:flex 1 + :flexDirection :column + :backgroundColor color-white}) + +(def chat-name-container + {:marginHorizontal 16}) + +(def chat-name-text + {:marginTop 24 + :marginBottom 16 + :color text2-color + :fontFamily font + :fontSize 14 + :lineHeight 20}) + +(def group-name-input + {:marginLeft -4 + :fontSize 14 + :fontFamily font + :color text1-color}) + +(def members-text + {:marginTop 24 + :marginBottom 16 + :color text2-color + :fontFamily font + :fontSize 14 + :lineHeight 20}) + +(def add-container + {:flexDirection :row + :marginBottom 16}) + +(def add-icon + {:marginVertical 19 + :marginHorizontal 3 + :width 17 + :height 17}) + +(def add-text + {:marginTop 18 + :marginLeft 32 + :color text2-color + :fontFamily font + :fontSize 14 + :lineHeight 20}) + +(def contacts-list + {:backgroundColor :white}) + +(def contact-container + {:flexDirection :row + :height 56}) diff --git a/src/syng_im/new_group/views/contact.cljs b/src/syng_im/new_group/views/contact.cljs new file mode 100644 index 0000000000..2a12eb2bc5 --- /dev/null +++ b/src/syng_im/new_group/views/contact.cljs @@ -0,0 +1,19 @@ +(ns syng-im.new-group.views.contact + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.components.react :refer [view]] + [syng-im.contacts.views.contact-inner :refer [contact-inner-view]] + [syng-im.components.item-checkbox :refer [item-checkbox]] + [reagent.core :as r] + [syng-im.new-group.styles :as st])) + +(defn new-group-contact [{:keys [whisper-identity] :as contact}] + (let [checked (r/atom false)] + (fn [] + [view st/contact-container + [item-checkbox + {:onToggle (fn [checked?] + (reset! checked checked?) + (dispatch [:select-for-new-group whisper-identity checked?])) + :checked @checked + :size 30}] + [contact-inner-view contact]]))) diff --git a/src/syng_im/participants/styles.cljs b/src/syng_im/participants/styles.cljs new file mode 100644 index 0000000000..482436f30a --- /dev/null +++ b/src/syng_im/participants/styles.cljs @@ -0,0 +1,24 @@ +(ns syng-im.participants.styles) + +(def participants-container + {:flex 1 + :backgroundColor :white}) + +(def participants-list + {:backgroundColor :white}) + +(def new-participant-image + {:width 20 + :height 18}) + +(def remove-participants-image + {:width 22 + :height 30}) + +(def participant-container + {:flexDirection :row + :marginTop 5 + :marginBottom 5 + :paddingLeft 15 + :paddingRight 15 + :height 75}) diff --git a/src/syng_im/participants/views/contact.cljs b/src/syng_im/participants/views/contact.cljs new file mode 100644 index 0000000000..f193f24deb --- /dev/null +++ b/src/syng_im/participants/views/contact.cljs @@ -0,0 +1,19 @@ +(ns syng-im.participants.views.contact + (:require [re-frame.core :refer [subscribe dispatch dispatch-sync]] + [syng-im.components.react :refer [view]] + [syng-im.contacts.views.contact-inner :refer [contact-inner-view]] + [syng-im.components.item-checkbox :refer [item-checkbox]] + [reagent.core :as r] + [syng-im.participants.styles :as st])) + +(defn participant-contact [{:keys [whisper-identity] :as contact}] + ;; todo must be moved to handlers + (let [checked (r/atom false)] + (fn [{:keys [whisper-identity] :as contact}] + [view st/participant-container + [item-checkbox {:onToggle (fn [checked?] + (reset! checked checked?) + (dispatch [:select-new-participant whisper-identity checked?])) + :checked @checked + :size 30}] + [contact-inner-view contact]]))) diff --git a/src/syng_im/participants/views/create.cljs b/src/syng_im/participants/views/create.cljs new file mode 100644 index 0000000000..20a84dfbfa --- /dev/null +++ b/src/syng_im/participants/views/create.cljs @@ -0,0 +1,30 @@ +(ns syng-im.participants.views.create + (:require [re-frame.core :refer [subscribe dispatch]] + [syng-im.resources :as res] + [syng-im.components.react :refer [view list-view list-item]] + [syng-im.components.toolbar :refer [toolbar]] + [syng-im.utils.listview :refer [to-datasource]] + [syng-im.participants.views.contact :refer [participant-contact]] + [reagent.core :as r] + [syng-im.participants.styles :as st])) + +(defn new-participants-toolbar [] + [toolbar + {:title "Add Participants" + :action {:image {:source res/v ;; {:uri "icon_search"} + :style st/new-participant-image} + :handler #(dispatch [:add-new-participants])}}]) + +(defn new-participants-row + [row _ _] + (list-item [participant-contact row])) + +(defn new-participants [] + (let [contacts (subscribe [:all-new-contacts])] + (fn [] + (let [contacts-ds (to-datasource @contacts)] + [view st/participants-container + [new-participants-toolbar] + [list-view {:dataSource contacts-ds + :renderRow new-participants-row + :style st/participants-list}]])))) diff --git a/src/syng_im/participants/views/remove.cljs b/src/syng_im/participants/views/remove.cljs new file mode 100644 index 0000000000..40ff3528f7 --- /dev/null +++ b/src/syng_im/participants/views/remove.cljs @@ -0,0 +1,33 @@ +(ns syng-im.participants.views.remove + (:require [re-frame.core :refer [subscribe dispatch]] + [syng-im.resources :as res] + [syng-im.components.react :refer [view text-input text image + touchable-highlight list-view + list-item]] + [syng-im.components.toolbar :refer [toolbar]] + [syng-im.utils.listview :refer [to-datasource]] + [syng-im.participants.views.contact + :refer [participant-contact]] + [reagent.core :as r] + [syng-im.participants.styles :as st])) + +(defn remove-participants-toolbar [] + [toolbar + {:title "Remove Participants" + :action {:handler #(dispatch [:remove-selected-participants]) + :image {:source res/trash-icon ;; {:uri "icon_search"} + :style st/remove-participants-image}}}]) + +(defn remove-participants-row + [row _ _] + (r/as-element [participant-contact row])) + +(defn remove-participants [] + (let [contacts (subscribe [:current-chat-contacts])] + (fn [] + (let [contacts-ds (to-datasource @contacts)] + [view st/participants-container + [remove-participants-toolbar] + [list-view {:dataSource contacts-ds + :renderRow remove-participants-row + :style st/participants-list}]])))) diff --git a/src/syng_im/persistence/realm.cljs b/src/syng_im/persistence/realm.cljs index dcc7cd7043..af5050cf78 100644 --- a/src/syng_im/persistence/realm.cljs +++ b/src/syng_im/persistence/realm.cljs @@ -46,28 +46,28 @@ :color {:type "string" :default "#a187d5"} :group-chat {:type "bool" - :indexed true} - :is-active "bool" - :timestamp "int" - :contacts {:type "list" - :objectType "chat-contact"} + :indexed true} + :is-active "bool" + :timestamp "int" + :contacts {:type "list" + :objectType "chat-contact"} :last-msg-id "string"}} - {:name :tag - :primaryKey :name - :properties {:name "string" - :count {:type "int" - :optional true - :default 0}}} - {:name :discoveries - :primaryKey :whisper-id - :properties {:name "string" - :status "string" - :whisper-id "string" - :photo "string" - :location "string" - :tags {:type "list" - :objectType "tag"} - :last-updated "date"}}]}) + {:name :tag + :primaryKey :name + :properties {:name "string" + :count {:type "int" + :optional true + :default 0}}} + {:name :discoveries + :primaryKey :whisper-id + :properties {:name "string" + :status "string" + :whisper-id "string" + :photo "string" + :location "string" + :tags {:type "list" + :objectType "tag"} + :last-updated "date"}}]}) (def realm (js/Realm. (clj->js opts))) diff --git a/src/syng_im/profile/screen.cljs b/src/syng_im/profile/screen.cljs new file mode 100644 index 0000000000..9d075c4999 --- /dev/null +++ b/src/syng_im/profile/screen.cljs @@ -0,0 +1,98 @@ +(ns syng-im.profile.screen + (:require-macros [syng-im.utils.views :refer [defview]]) + (:require [clojure.string :as s] + [re-frame.core :refer [subscribe dispatch]] + [syng-im.components.react :refer [view + text + image + icon + scroll-view + touchable-highlight + touchable-opacity]] + [syng-im.resources :as res] + [syng-im.profile.styles :as st])) + +(defn user-photo [{:keys [photo-path]}] + [image {:source (if (s/blank? photo-path) + res/user-no-photo + {:uri photo-path}) + :style st/user-photo}]) + +(defn user-online [{:keys [online]}] + (when online + [view st/user-online-container + [view st/user-online-dot-left] + [view st/user-online-dot-right]])) + +(defn profile-property-view [{:keys [name value]}] + [view st/profile-property-view-container + [view st/profile-property-view-sub-container + [text {:style st/profile-property-view-label} name] + [text {:style st/profile-property-view-value} value]]]) + +(defn message-user [identity] + (when identity + (dispatch [:show-chat identity :push]))) + +(defview profile [] + [{:keys [name whisper-identity phone-number]} [:contact]] + [scroll-view {:style st/profile} + [touchable-highlight {:style st/back-btn-touchable + :on-press #(dispatch [:navigate-back])} + [view st/back-btn-container + [icon :back st/back-btn-icon]]] + [view st/status-block + [view st/user-photo-container + [user-photo {}] + [user-online {:online true}]] + [text {:style st/user-name} name] + [text {:style st/status} "!not implemented"] + [view st/btns-container + [touchable-highlight {:onPress #(message-user whisper-identity)} + [view st/message-btn + [text {:style st/message-btn-text} "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" + :value name}] + [profile-property-view {:name "Phone number" + :value phone-number}] + [profile-property-view {:name "Email" + :value "!not implemented"}] + [view st/report-user-container + [touchable-opacity {} + [text {:style st/report-user-text} "REPORT USER"]]]]]) + +(defview my-profile [] + [username [:get :username] + phone-number [:get :phone-number] + email [:get :email] + status [:get :status]] + [scroll-view {:style st/profile} + [touchable-highlight {:style st/back-btn-touchable + :on-press #(dispatch [:navigate-back])} + [view st/back-btn-container + [icon :back st/back-btn-icon]]] + [touchable-highlight {:style st/actions-btn-touchable + :on-press (fn [] + ;; TODO not implemented + )} + [view st/actions-btn-container + [icon :dots st/actions-btn-icon]]] + [view st/status-block + [view st/user-photo-container + [user-photo {}] + [user-online {:online true}]] + [text {:style st/user-name} username] + [text {:style st/status} status]] + [view st/profile-properties-container + [profile-property-view {:name "Username" + :value username}] + [profile-property-view {:name "Phone number" + :value phone-number}] + [profile-property-view {:name "Email" + :value email}]]]) diff --git a/src/syng_im/components/profile_styles.cljs b/src/syng_im/profile/styles.cljs similarity index 98% rename from src/syng_im/components/profile_styles.cljs rename to src/syng_im/profile/styles.cljs index 347b89c282..545016ca4d 100644 --- a/src/syng_im/components/profile_styles.cljs +++ b/src/syng_im/profile/styles.cljs @@ -1,4 +1,4 @@ -(ns syng-im.components.profile-styles +(ns syng-im.profile.styles (:require [syng-im.components.styles :refer [font color-light-blue-transparent color-white diff --git a/src/syng_im/subs.cljs b/src/syng_im/subs.cljs index 3f8cf88a12..17adff37ec 100644 --- a/src/syng_im/subs.cljs +++ b/src/syng_im/subs.cljs @@ -1,188 +1,36 @@ (ns syng-im.subs (:require-macros [reagent.ratom :refer [reaction]]) (:require [re-frame.core :refer [register-sub]] - [syng-im.db :as db] - [syng-im.components.discovery.subs :as discovery] - [syng-im.models.chat :refer [current-chat-id + [syng-im.models.chats :refer [chats-list chat-by-id]] + [syng-im.models.contacts :refer [get-contacts current-chat - chat-updated?]] - [syng-im.models.chats :refer [chats-list - chats-updated? - chat-by-id]] - [syng-im.models.messages :refer [get-messages]] - [syng-im.models.contacts :refer [contacts-list contacts-list-exclude contacts-list-include - contact-identity contact-by-identity]] - [syng-im.models.commands :refer [get-commands - get-chat-command - get-chat-command-content - get-chat-command-request - parse-command-request]] - [syng-im.handlers.suggestions :refer [get-suggestions - typing-command?]] - [syng-im.handlers.content-suggestions :refer [get-content-suggestions]])) - -;; -- Chat -------------------------------------------------------------- - -(register-sub :get-chat-messages - (fn [db _] - (let [chat-id (current-chat-id @db)] - (reaction (get-in @db [:chats chat-id :messages]))))) - -(register-sub :get-current-chat-id - (fn [db _] - (reaction (current-chat-id @db)))) - -(register-sub :get-suggestions - (fn [db _] - (let [input-text (->> (current-chat-id @db) - db/chat-input-text-path - (get-in @db) - (reaction))] - (reaction (get-suggestions @db @input-text))))) - -(register-sub :typing-command? - (fn [db _] - (reaction (typing-command? @db)))) - -(register-sub :get-content-suggestions - (fn [db _] - (let [command (reaction (get-chat-command @db)) - text (reaction (get-chat-command-content @db))] - (reaction (get-content-suggestions @db @command @text))))) - -(register-sub :get-commands - (fn [db _] - (reaction (get-commands @db)))) - -(register-sub :get-chat-input-text - (fn [db _] - (reaction (get-in @db (db/chat-input-text-path (current-chat-id @db)))))) - -(register-sub :get-chat-staged-commands - (fn [db _] - (reaction (get-in @db (db/chat-staged-commands-path (current-chat-id @db)))))) - -(register-sub :get-chat-command - (fn [db _] - (reaction (get-chat-command @db)))) - -(register-sub :get-chat-command-content - (fn [db _] - (reaction (get-chat-command-content @db)))) - -(register-sub :chat-command-request - (fn [db _] - (reaction (get-chat-command-request @db)))) + syng-im.chat.subs + syng-im.navigation.subs + syng-im.discovery.subs + syng-im.contacts.subs)) ;; -- Chats list -------------------------------------------------------------- -(register-sub :get-chats - (fn [db _] - (let [chats-updated (reaction (chats-updated? @db))] - (reaction - (let [_ @chats-updated] - (chats-list)))))) - -(register-sub :get-current-chat - (fn [db _] - (let [current-chat-id (current-chat-id @db)] - (reaction (get-in @db [:chats current-chat-id]))))) - +(register-sub :get + (fn [db [_ k]] + (reaction (k @db)))) (register-sub :get-current-chat-name (fn [db _] (let [current-chat-id (current-chat-id @db)] (reaction (get-in @db (db/chat-name-path current-chat-id)))))) - (register-sub :get-current-chat-color (fn [db _] (let [current-chat-id (current-chat-id @db)] (reaction (get-in @db (db/chat-color-path current-chat-id)))))) ;; -- User data -------------------------------------------------------------- - -(register-sub :username - (fn [db _] - (reaction - (get @db :username)))) - -(register-sub :phone-number - (fn [db _] - (reaction - (get @db :phone-number)))) - -(register-sub :email - (fn [db _] - (reaction - (get @db :email)))) - -(register-sub :status - (fn [db _] - (reaction - (get @db :status)))) - -(register-sub - :get-user-identity - (fn [db _] - (reaction - (get @db :user-identity)))) - -(register-sub - :get-loading - (fn [db _] - (reaction - (get @db :loading)))) - (register-sub :signed-up (fn [db _] - (reaction - (get @db :signed-up)))) - -(register-sub - :show-actions - (fn [db _] - (reaction (get-in @db db/show-actions-path)))) - -(register-sub - :get-contacts - (fn [db _] - (reaction - (get @db :contacts)))) - -(register-sub :all-contacts - (fn [db _] - (reaction - (contacts-list)))) - -(register-sub :contact - (fn [db _] - (let [identity (reaction (get-in @db db/contact-identity-path))] - (reaction (contact-by-identity @identity))))) - -(register-sub :all-new-contacts - (fn [db _] - (let [current-chat-id (reaction (current-chat-id @db)) - chat (reaction (when-let [chat-id @current-chat-id] - (chat-by-id chat-id)))] - (reaction - (when @chat - (let [current-participants (->> @chat - :contacts - (map :identity))] - (contacts-list-exclude current-participants))))))) - -(register-sub :current-chat-contacts - (fn [db _] - (let [chat (reaction (current-chat @db))] - (reaction - (when @chat - (let [current-participants (->> @chat - :contacts - (map :identity))] - (contacts-list-include current-participants))))))) + (reaction (:signed-up @db)))) (register-sub :selected-group-chat-member (fn [db [_]] @@ -194,28 +42,5 @@ (fn [db [_]] (reaction (get-in @db db/group-settings-show-color-picker)))) -(register-sub :view-id - (fn [db _] - (reaction (@db :view-id)))) - -(register-sub :chat - (fn [db [_ k]] - (-> @db - (get-in [:chats (current-chat-id @db) k]) - (reaction)))) - -(register-sub :navigation-stack - (fn [db _] - (reaction (:navigation-stack @db)))) - (register-sub :db (fn [db _] (reaction @db))) - -(register-sub :chat-properties - (fn [db [_ properties]] - (->> properties - (map (fn [k] - [k (-> @db - (get-in [:chats (:current-chat-id @db) k]) - (reaction))])) - (into {})))) diff --git a/src/syng_im/utils/handlers.cljs b/src/syng_im/utils/handlers.cljs new file mode 100644 index 0000000000..154022cad1 --- /dev/null +++ b/src/syng_im/utils/handlers.cljs @@ -0,0 +1,8 @@ +(ns syng-im.utils.handlers) + +(defn side-effect! + "Middleware for handlers that will not affect db." + [handler] + (fn [db params] + (handler db params) + db)) diff --git a/src/syng_im/utils/listview.cljs b/src/syng_im/utils/listview.cljs index d52b674394..ab7031662e 100644 --- a/src/syng_im/utils/listview.cljs +++ b/src/syng_im/utils/listview.cljs @@ -1,19 +1,10 @@ (ns syng-im.utils.listview - (:require-macros [natal-shell.data-source :refer [data-source clone-with-rows]]) + (:require-macros [natal-shell.data-source :refer [data-source]]) (:require [syng-im.components.realm])) -(defn to-datasource [items] - (clone-with-rows (data-source {:rowHasChanged not=}) items)) - -(defn to-realm-datasource [items] - (-> (cljs.core/clj->js {:rowHasChanged not=}) - (js/RealmReactNative.ListView.DataSource.) - (clone-with-rows items))) - - -(defn clone-with-rows2 [ds rows] +(defn clone-with-rows [ds rows] (.cloneWithRows ds (reduce (fn [ac el] (.push ac el) ac) (clj->js []) rows))) -(defn to-datasource2 [items] - (clone-with-rows2 (data-source {:rowHasChanged not=}) items)) +(defn to-datasource [items] + (clone-with-rows (data-source {:rowHasChanged not=}) items)) diff --git a/src/syng_im/utils/phone_number.cljs b/src/syng_im/utils/phone_number.cljs index 0d5a960193..aaf610a3cc 100644 --- a/src/syng_im/utils/phone_number.cljs +++ b/src/syng_im/utils/phone_number.cljs @@ -5,6 +5,7 @@ (def country-code (subs locale 3 5)) (set! js/PhoneNumber (js/require "awesome-phonenumber")) +;; todo check wrong numbers, .getNumber returns empty string (defn format-phone-number [number] (str (.getNumber (js/PhoneNumber. number country-code "international")))) diff --git a/src/syng_im/utils/views.clj b/src/syng_im/utils/views.clj new file mode 100644 index 0000000000..3eb1487e76 --- /dev/null +++ b/src/syng_im/utils/views.clj @@ -0,0 +1,27 @@ +(ns syng-im.utils.views) + +(defn prepare-subs [subs] + (let [pairs (map (fn [[form sub]] + {:form form + :sub sub + :sym (gensym)}) + (partition 2 subs))] + [(mapcat (fn [{:keys [sym sub]}] + [sym `(re-frame.core/subscribe ~sub)]) + pairs) + (mapcat (fn [{:keys [sym form]}] + [form `(deref ~sym)]) + pairs)])) + +(defmacro defview + [n params & rest] + (let [[subs body] (if (= 1 (count rest)) + [nil (first rest)] + rest) + [subs-bindings vars-bindings] (prepare-subs subs)] + `(defn ~n ~params + (let [~@subs-bindings] + (fn ~params + (let [~@vars-bindings] + ~body)))))) +