Merge branch 'develop' into structure

Conflicts:
	src/syng_im/android/core.cljs
	src/syng_im/chat/screen.cljs
	src/syng_im/chat/styles/suggestions.cljs
	src/syng_im/chat/suggestions.cljs
	src/syng_im/chat/views/command.cljs
	src/syng_im/chat/views/phone.cljs
	src/syng_im/chat/views/suggestions.cljs
	src/syng_im/components/chat/input/money.cljs
	src/syng_im/components/chats/chats_list.cljs
	src/syng_im/components/contact_list/contact.cljs
	src/syng_im/components/contact_list/contact_list.cljs
	src/syng_im/components/discovery/discovery.cljs
	src/syng_im/components/toolbar.cljs
	src/syng_im/db.cljs
	src/syng_im/discovery/styles.cljs
	src/syng_im/handlers.cljs
	src/syng_im/models/chat.cljs
	src/syng_im/models/contacts.cljs
	src/syng_im/subs.cljs
This commit is contained in:
Roman Volosovskyi 2016-05-16 12:31:08 +03:00
commit 473604d859
50 changed files with 948 additions and 186 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 775 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 960 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

View File

@ -2,7 +2,6 @@
(: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]
@ -15,12 +14,10 @@
[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.components.profile :refer [profile my-profile]]
[syng-im.utils.utils :refer [toast]]
[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
@ -44,7 +41,9 @@
:chat-list [chats-list]
:new-group [new-group]
:contact-list [contact-list]
:chat [chat]))))
:chat [chat]
:profile [profile]
:my-profile [my-profile]))))
(defn init []
(dispatch-sync [:initialize-db])

View File

@ -340,3 +340,7 @@
((after save-chat!))
((after open-chat!))
debug))
(register-handler :switch-command-suggestions
(fn [db [_]]
(suggestions/switch-command-suggestions db)))

View File

@ -13,9 +13,11 @@
[syng-im.resources :as res]
[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.chat.views.message :refer [chat-message]]
[syng-im.chat.views.new-message :refer [chat-message-new]]))
(defn contacts-by-identity [contacts]
(->> contacts
(map (fn [{:keys [identity] :as contact}]
@ -76,24 +78,43 @@
[view nil]]
items])
(defn action-view [{:keys [icon-style handler title subtitle]
(defn action-view [{:keys [icon-style custom-icon handler title subtitle]
icon-name :icon}]
[touchable-highlight {:on-press (fn []
(dispatch [:set-show-actions false])
(handler))}
(when handler
(handler)))}
[view st/action-icon-row
[view st/action-icon-view
[icon icon-name icon-style]]
(or custom-icon
[icon icon-name icon-style])]
[view st/action-view
[text {:style st/action-title} title]
(when-let [subtitle subtitle]
[text {:style st/action-subtitle}
subtitle])]]])
(defn menu-item-contact-photo [{:keys [photo-path]}]
[image {:source (if (s/blank? photo-path)
res/user-no-photo
{:uri photo-path})
:style st/menu-item-profile-contact-photo}])
(defn menu-item-contact-online [{:keys [online]}]
(when online
[view st/menu-item-profile-online-view
[view st/menu-item-profile-online-dot-left]
[view st/menu-item-profile-online-dot-right]]))
(defn menu-item-icon-profile []
[view st/icon-view
[menu-item-contact-photo {}]
[menu-item-contact-online {:online true}]])
(defn actions-list-view []
(let [{:keys [group-chat active]}
(subscribe [:chat-properties [:group-chat :active]])]
(when-let [actions (when (and @group-chat @active)
(let [{:keys [group-chat chat-id]}
(subscribe [:chat-properties [:group-chat :chat-id]])]
(when-let [actions (if @group-chat
[{:title "Add Contact to chat"
:icon :menu_group
:icon-style {:width 25
@ -113,6 +134,32 @@
{:title "Settings"
:subtitle "Not implemented"
:icon :settings
:icon-style {:width 20
:height 13}
:handler (fn [])}]
[{:title "Profile"
:custom-icon [menu-item-icon-profile]
:icon :menu_group
:icon-style {:width 25
:height 19}
:handler #(dispatch [:show-profile @chat-id])}
{:title "Search chat"
:subtitle "!not implemented"
:icon :search_gray_copy
:icon-style {:width 17
:height 17}
:handler nil #_#(dispatch
[:show-remove-participants navigator])}
{:title "Notifications and sounds"
:subtitle "!not implemented"
:icon :muted
:icon-style {:width 18
:height 21}
:handler nil #_#(dispatch [:leave-group-chat
navigator])}
{:title "Settings"
:subtitle "!not implemented"
:icon :settings
:icon-style {:width 20
:height 13}
:handler (fn [])}])]
@ -126,40 +173,48 @@
[overlay {:on-click-outside #(dispatch [:set-show-actions false])}
[actions-list-view]])
(defn toolbar []
(defn toolbar-content []
(let [{:keys [group-chat name contacts]}
(subscribe [:chat-properties [:group-chat :name :contacts]])
show-actions (subscribe [:show-actions])]
(fn []
[view st/toolbar-view
(when (not @show-actions)
[touchable-highlight {:on-press #(dispatch [:navigate-back])}
[view st/icon-view
[icon :back st/back-icon]]])
[view (st/chat-name-view @show-actions)
[text {:style st/chat-name-text}
(or @name "Chat name")]
(if @group-chat
[view {:flexDirection :row}
[icon :group st/group-icon]
[text {:style st/members}
(let [cnt (count @contacts)]
(str cnt
(if (< 1 cnt)
" members"
" member")
", " cnt " active"))]]
[text {:style st/last-activity} "Active a minute ago"])]
(if @show-actions
[touchable-highlight
{:on-press #(dispatch [:set-show-actions false])}
[view st/icon-view
[icon :up st/up-icon]]]
[touchable-highlight
{:on-press #(dispatch [:set-show-actions true])}
[view st/icon-view
[chat-photo {}]
[contact-online {:online true}]]])])))
[view (st/chat-name-view @show-actions)
[text {:style st/chat-name-text}
(or @name "Chat name")]
(if @group-chat
[view {:flexDirection :row}
[icon :group st/group-icon]
[text {:style st/members}
(let [cnt (count @contacts)]
(str cnt
(if (< 1 cnt)
" members"
" member")
", " cnt " active"))]]
[text {:style st/last-activity} "Active a minute ago"])])))
(defn toolbar-action []
(let [show-actions (subscribe [:show-actions])]
(fn []
(if @show-actions
[touchable-highlight
{:on-press #(dispatch [:set-show-actions false])}
[view st/icon-view
[icon :up st/up-icon]]]
[touchable-highlight
{:on-press #(dispatch [:set-show-actions true])}
[view st/icon-view
[chat-photo {}]
[contact-online {:online true}]]]))))
(defn chat-toolbar []
(let [{:keys [group-chat name contacts]}
(subscribe [:chat-properties [:group-chat :name :contacts]])
show-actions (subscribe [:show-actions])]
(fn []
[toolbar {:hide-nav? @show-actions
:custom-content [toolbar-content]
:custom-action [toolbar-action]}])))
(defview messages-view [group-chat]
[messages [:chat :messages]
@ -176,7 +231,7 @@
group-chat [:chat :group-chat]
show-actions-atom [:show-actions]]
[view st/chat-view
[toolbar]
[chat-toolbar]
[messages-view group-chat]
(when group-chat [typing-all])
(when is-active [chat-message-new])

View File

@ -6,7 +6,6 @@
text2-color
chat-background
color-black]]))
(def money-input
{:flex 1
:marginLeft 8
@ -15,6 +14,9 @@
:fontFamily font
:color :black})
(def command-input-and-suggestions-container
{:flexDirection :column})
(def command-input-container
{:flexDirection :row
:height 56

View File

@ -12,17 +12,22 @@
:height 56
:backgroundColor color-white})
(def switch-commands-touchable
{:width 56
:height 56
:alignItems :center
:justifyContent :center})
(def list-icon
{:marginTop 22
:marginRight 6
:marginBottom 6
:marginLeft 21
:width 13
:height 12})
{:width 13
:height 12})
(def close-icon
{:width 12
:height 12})
(def message-input
{:flex 1
:marginLeft 16
:marginTop -2
:padding 0
:fontSize 14

View File

@ -167,3 +167,34 @@
(def overlay-highlight
{:flex 1})
;;----- Menu item Profile ----------------
(def menu-item-profile-contact-photo
{:marginTop 13
:marginLeft 16
:borderRadius 50
:width 24
:height 24})
(def menu-item-profile-online-view
{:position :absolute
:top 26
:left 29
:width 15
:height 15
:borderRadius 50
:backgroundColor online-color
:borderWidth 2
:borderColor color-white})
(def menu-item-profile-online-dot
{:position :absolute
:top 4
:width 3
:height 3
:borderRadius 50
:backgroundColor color-white})
(def menu-item-profile-online-dot-left (merge menu-item-profile-online-dot {:left 1.7}))
(def menu-item-profile-online-dot-right (merge menu-item-profile-online-dot {:left 6.3}))

View File

@ -1,44 +1,69 @@
(ns syng-im.chat.styles.suggestions
(:require [syng-im.components.styles :refer [font color-white]]))
(:require [syng-im.components.styles :refer [font
color-light-blue-transparent
color-white
color-black
color-blue
color-blue-transparent
selected-message-color
online-color
separator-color
text1-color
text2-color
text3-color]]))
(def suggestion-item-container
{:flexDirection :row
:marginVertical 1
:marginHorizontal 0
:height 40
:backgroundColor color-white})
(def suggestion-height 88)
(def suggestion-container
{:flexDirection :column
:paddingLeft 16
:backgroundColor color-white})
(def suggestion-sub-container
{:height suggestion-height
:borderBottomWidth 1
:borderBottomColor separator-color})
(defn suggestion-background
[{:keys [color]}]
{:flexDirection :column
:position :absolute
:top 10
:left 60
{:alignSelf :flex-start
:marginTop 10
:height 24
:backgroundColor color
:borderRadius 10})
:borderRadius 50})
(def suggestion-text
{:marginTop -2
:marginHorizontal 10
:fontSize 14
{:marginTop 2.5
:marginHorizontal 12
:fontSize 12
:fontFamily font
:color color-white})
(def suggestion-description
{:flex 1
:position :absolute
:top 7
:left 190
:lineHeight 18
(def value-text
{:marginTop 6
:fontSize 14
:fontFamily font
:color :black})
:color text1-color})
(defn suggestions-container
[suggestions]
(def description-text
{:marginTop 2
:fontSize 12
:fontFamily font
:color text2-color})
(defn suggestions-container [suggestions-count]
{:flexDirection :row
:marginVertical 1
:marginHorizontal 0
:height (min 105 (* 42 (count suggestions)))
:height (min 168 (* suggestion-height suggestions-count))
:backgroundColor color-white
:borderRadius 5})
(def drag-down-touchable
{:height 22
:alignItems :center
:justifyContent :center})
(def drag-down-icon
{:width 16
:height 16})

View File

@ -3,8 +3,10 @@
(:require [re-frame.core :refer [register-sub]]
[syng-im.db :as db]
;todo handlers in subs?...
[syng-im.chat.suggestions :refer [get-suggestions]]
[syng-im.models.commands :as commands]))
[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]]
@ -80,3 +82,13 @@
(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)))))

View File

@ -61,3 +61,12 @@
[suggestion] (filter #(= suggestion-text' (:text %))
(get-commands db))]
suggestion)))
(defn typing-command? [db]
(-> db
(get-in [:chats (:current-chat-id db) :input-text])
suggestion?))
(defn switch-command-suggestions [db]
(let [text (if (typing-command? db) nil "!")]
(assoc-in db [:chats (:current-chat-id db) :input-text] text)))

View File

@ -1,12 +1,11 @@
(ns syng-im.chat.views.command
(:require [re-frame.core :refer [subscribe dispatch]]
[syng-im.components.react :refer [view
image
icon
text
text-input
touchable-highlight]]
[syng-im.resources :as res]
[syng-im.components.chat.content-suggestions :refer [content-suggestions-view]]
[syng-im.chat.styles.input :as st]))
(defn cancel-command-input []
@ -19,23 +18,31 @@
(dispatch [:stage-command])
(cancel-command-input))
(defn simple-command-input-view [command input-options]
(defn valid? [message validator]
(if validator
(validator message)
(pos? (count message))))
(defn simple-command-input-view [command input-options & {:keys [validator]}]
(let [message-atom (subscribe [:get-chat-command-content])]
(fn [command input-options]
(fn [command input-options & {:keys [validator]}]
(let [message @message-atom]
[view st/command-input-container
[view (st/command-text-container command)
[text {:style st/command-text} (:text command)]]
[text-input (merge {:style st/command-input
:autoFocus true
:onChangeText set-input-message
:onSubmitEditing send-command}
input-options)
message]
(if (pos? (count message))
[touchable-highlight {:on-press send-command}
[view st/send-container [icon :send st/send-icon]]]
[touchable-highlight {:on-press cancel-command-input}
[view st/cancel-container
[image {:source res/icon-close-gray
:style st/cancel-icon}]]])]))))
[view st/command-input-and-suggestions-container
[content-suggestions-view]
[view st/command-input-container
[view (st/command-text-container command)
[text {:style st/command-text} (:text command)]]
[text-input (merge {:style st/command-input
:autoFocus true
:onChangeText set-input-message
:onSubmitEditing (fn []
(when (valid? message validator)
(send-command)))}
input-options)
message]
(if (valid? message validator)
[touchable-highlight {:on-press send-command}
[view st/send-container [icon :send st/send-icon]]]
[touchable-highlight {:on-press cancel-command-input}
[view st/cancel-container
[icon :close-gray st/cancel-icon]]])]]))))

View File

@ -1,7 +1,9 @@
(ns syng-im.chat.views.phone
(:require
[syng-im.chat.views.command
:refer [simple-command-input-view]]))
:refer [simple-command-input-view]]
[syng-im.utils.phone-number :refer [valid-mobile-number?]]))
(defn phone-input-view [command]
[simple-command-input-view command {:keyboardType :phone-pad}])
[simple-command-input-view command {:keyboardType :phone-pad}
:validator valid-mobile-number?])

View File

@ -18,24 +18,39 @@
(dispatch [:send-group-chat-msg chat-id input-message])
(dispatch [:send-chat-msg]))))
(defn message-valid? [staged-commands message]
(or (and (pos? (count message))
(not= "!" message))
(pos? (count staged-commands))))
(defn try-send [chat staged-commands message]
(when (message-valid? staged-commands message)
(send chat message)))
(defn plain-message-input-view []
(let [chat (subscribe [:get-current-chat])
input-message-atom (subscribe [:get-chat-input-text])
staged-commands-atom (subscribe [:get-chat-staged-commands])]
staged-commands-atom (subscribe [:get-chat-staged-commands])
typing-command? (subscribe [:typing-command?])]
(fn []
(let [input-message @input-message-atom]
[view st/input-container
[suggestions-view]
[view st/input-view
[icon :list st/list-icon]
[touchable-highlight {:on-press #(dispatch [:switch-command-suggestions])
:style st/switch-commands-touchable}
[view nil
(if @typing-command?
[icon :close-gray st/close-icon]
[icon :list st/list-icon])]]
[text-input {:style st/message-input
:autoFocus (pos? (count @staged-commands-atom))
:onChangeText set-input-message
:onSubmitEditing #(send @chat input-message)}
:onSubmitEditing #(try-send @chat @staged-commands-atom
input-message)}
input-message]
[icon :smile st/smile-icon]
(when (or (pos? (count input-message))
(pos? (count @staged-commands-atom)))
(when (message-valid? @staged-commands-atom input-message)
[touchable-highlight {:on-press #(send @chat input-message)}
[view st/send-container
[icon :send st/send-icon]]])]]))))

View File

@ -2,6 +2,7 @@
(:require [re-frame.core :refer [subscribe dispatch]]
[syng-im.components.react :refer [view
text
icon
touchable-highlight
list-view
list-item]]
@ -17,10 +18,12 @@
:as suggestion}]
[touchable-highlight
{:onPress #(set-command-input (keyword command))}
[view st/suggestion-item-container
[view (st/suggestion-background suggestion)
[text {:style st/suggestion-text} label]]
[text {:style st/suggestion-description} description]]])
[view st/suggestion-container
[view st/suggestion-sub-container
[view (st/suggestion-background 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 row]))
@ -30,8 +33,14 @@
(fn []
(let [suggestions @suggestions-atom]
(when (seq suggestions)
[view (st/suggestions-container suggestions)
[list-view {:dataSource (to-datasource suggestions)
:enableEmptySections true
:renderRow render-row
:style {}}]])))))
[view
[touchable-highlight {:style st/drag-down-touchable
:onPress (fn []
;; TODO hide suggestions?
)}
[view
[icon :drag_down st/drag-down-icon]]]
[view (st/suggestions-container (count suggestions))
[list-view {:dataSource (to-datasource suggestions)
:enableEmptySections true
:renderRow render-row}]]])))))

View File

@ -0,0 +1,50 @@
(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}]]])))))

View File

@ -0,0 +1,54 @@
(ns syng-im.components.chat.content-suggestions-styles
(:require [syng-im.components.styles :refer [font
color-light-blue-transparent
color-white
color-black
color-blue
color-blue-transparent
selected-message-color
online-color
separator-color
text1-color
text2-color
text3-color]]))
(def suggestion-height 56)
(def suggestion-container
{:flexDirection :column
:paddingLeft 16
:backgroundColor color-white})
(def suggestion-sub-container
{:height suggestion-height
:borderBottomWidth 1
:borderBottomColor separator-color})
(def value-text
{:marginTop 9
:fontSize 14
:fontFamily font
:color text1-color})
(def description-text
{:marginTop 1.5
:fontSize 14
:fontFamily font
:color text2-color})
(defn suggestions-container [suggestions-count]
{:flexDirection :row
:marginVertical 1
:marginHorizontal 0
:height (min 150 (* suggestion-height suggestions-count))
:backgroundColor color-white
:borderRadius 5})
(def drag-down-touchable
{:height 22
:alignItems :center
:justifyContent :center})
(def drag-down-icon
{:width 16
:height 16})

View File

@ -0,0 +1,81 @@
(ns syng-im.components.drawer
(:require [clojure.string :as s]
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
[reagent.core :as r]
[syng-im.components.react :refer [android?
view
text
image
navigator
toolbar-android
drawer-layout-android
touchable-opacity]]
[syng-im.resources :as res]
[syng-im.components.drawer-styles :as st]))
(defonce drawer-atom (atom))
(defn open-drawer []
(.openDrawer @drawer-atom))
(defn close-drawer []
(.closeDrawer @drawer-atom))
(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 menu-item [{:keys [name handler]}]
[touchable-opacity {:style st/menu-item-touchable
:onPress (fn []
(close-drawer)
(handler))}
[text {:style st/menu-item-text}
name]])
(defn drawer-menu [navigator]
(let [username (subscribe [:get :username])]
(fn [navigator]
[view st/drawer-menu
[view st/user-photo-container
[user-photo {}]]
[view st/name-container
[text {:style st/name-text}
@username]]
[view st/menu-items-container
[menu-item {:name "Profile"
:handler (fn []
(dispatch [:show-my-profile]))}]
[menu-item {:name "Settings"
:handler (fn []
;; TODO not implemented
)}]
[menu-item {:name "Discovery"
:handler (fn []
(dispatch [:navigate-to :discovery]))}]
[menu-item {:name "Contacts"
:handler (fn []
(dispatch [:show-contacts navigator]))}]
[menu-item {:name "Invite friends"
:handler (fn []
;; TODO not implemented
)}]
[menu-item {:name "FAQ"
:handler (fn [])}]]
[view st/switch-users-container
[touchable-opacity {:onPress (fn []
(close-drawer)
;; TODO not implemented
)}
[text {:style st/switch-users-text}
"Switch users"]]]])))
(defn drawer-view [{:keys [navigator]} items]
[drawer-layout-android {:drawerWidth 260
:drawerPosition js/React.DrawerLayoutAndroid.positions.Left
:render-navigation-view #(r/as-element [drawer-menu navigator])
:ref (fn [drawer]
(reset! drawer-atom drawer))}
items])

View File

@ -0,0 +1,64 @@
(ns syng-im.components.drawer-styles
(:require [syng-im.components.styles :refer [font
color-light-blue-transparent
color-white
color-black
color-blue
color-blue-transparent
selected-message-color
online-color
separator-color
text1-color
text2-color
text3-color]]))
(def user-photo
{:borderRadius 50
:width 64
:height 64})
(def menu-item-touchable
{:height 48
:paddingLeft 16
:paddingTop 14})
(def menu-item-text
{:fontSize 14
:fontFamily font
:lineHeight 21
:color text1-color})
(def drawer-menu
{:flex 1
:backgroundColor color-white
:flexDirection :column})
(def user-photo-container
{:marginTop 40
:alignItems :center
:justifyContent :center})
(def name-container
{:marginTop 20
:alignItems :center})
(def name-text
{:marginTop -2.5
:color text1-color
:fontSize 16})
(def menu-items-container
{:flex 1
:marginTop 80
:alignItems :stretch
:flexDirection :column})
(def switch-users-container
{:paddingVertical 36
:alignItems :center})
(def switch-users-text
{:fontSize 14
:fontFamily font
:lineHeight 21
:color text3-color})

View File

@ -0,0 +1,109 @@
(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 [:get :username])
phone-number (subscribe [:get :phone-number])
email (subscribe [:get :email])
status (subscribe [:get :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}]]])))

View File

@ -0,0 +1,168 @@
(ns syng-im.components.profile-styles
(:require [syng-im.components.styles :refer [font
color-light-blue-transparent
color-white
color-black
color-blue
color-blue-transparent
selected-message-color
online-color
separator-color
text1-color
text2-color]]))
(def user-photo
{:borderRadius 50
:width 64
:height 64})
(def user-online-container
{:position :absolute
:top 44
:left 44
:width 24
:height 24
:borderRadius 50
:backgroundColor online-color
:borderWidth 2
:borderColor color-white})
(def user-online-dot
{:position :absolute
:top 8
:left 5
:width 4
:height 4
:borderRadius 50
:backgroundColor color-white})
(def user-online-dot-left
(assoc user-online-dot :left 5))
(def user-online-dot-right
(assoc user-online-dot :left 11))
(def profile-property-view-container
{:height 85
:paddingHorizontal 16})
(def profile-property-view-sub-container
{:borderBottomWidth 1
:borderBottomColor separator-color})
(def profile-property-view-label
{:marginTop 16
:fontSize 14
:fontFamily font
:color text2-color})
(def profile-property-view-value
{:marginTop 11
:height 40
:fontSize 16
:fontFamily font
:color text1-color})
(def profile
{:flex 1
:backgroundColor color-white
:flexDirection :column})
(def back-btn-touchable
{:position :absolute})
(def back-btn-container
{:width 56
:height 56})
(def back-btn-icon
{:marginTop 21
:marginLeft 23
:width 8
:height 14})
(def actions-btn-touchable
{:position :absolute
:right 0})
(def actions-btn-container
{:width 56
:height 56
:alignItems :center
:justifyContent :center})
(def actions-btn-icon
{:width 4
:height 16})
(def status-block
{:alignSelf :center
:alignItems :center
:width 249})
(def user-photo-container
{:marginTop 26})
(def user-name
{:marginTop 20
:fontSize 18
:fontFamily font
:color text1-color})
(def status
{:marginTop 10
:fontFamily font
:fontSize 14
:lineHeight 20
:textAlign :center
:color text2-color})
(def btns-container
{:marginTop 18
:flexDirection :row})
(def message-btn
{:height 40
:justifyContent :center
:backgroundColor color-blue
:paddingLeft 25
:paddingRight 25
:borderRadius 50})
(def message-btn-text
{:marginTop -2.5
:fontSize 14
:fontFamily font
:color color-white})
(def more-btn
{:marginLeft 10
:width 40
:height 40
:alignItems :center
:justifyContent :center
:backgroundColor color-blue-transparent
:padding 8
:borderRadius 50})
(def more-btn-image
{:width 4
:height 16})
(def profile-properties-container
{:marginTop 20
:alignItems :stretch
:flexDirection :column})
(def report-user-container
{:marginTop 50
:marginBottom 43
:alignItems :center})
(def report-user-text
{:fontSize 14
:fontFamily font
:lineHeight 21
:color text2-color
;; IOS:
:letterSpacing 0.5})

View File

@ -26,6 +26,8 @@
:placeholder "Type"}
props)
text])
(def drawer-layout-android (r/adapt-react-class (.-DrawerLayoutAndroid js/React)))
(def touchable-opacity (r/adapt-react-class (.-TouchableOpacity js/React)))
(defn icon [n style]

View File

@ -5,6 +5,7 @@
(def title-font "sans-serif-medium")
(def color-blue "#7099e6")
(def color-blue-transparent "#7099e632")
(def color-black "#000000de")
(def color-purple "#a187d5")
(def color-gray "#838c93de")
@ -16,8 +17,9 @@
(def text1-color color-black)
(def text2-color color-gray)
(def text3-color color-blue)
(def online-color color-blue)
(def new-messages-count-color "#7099e632")
(def new-messages-count-color color-blue-transparent)
(def chat-background color-light-gray)
(def selected-message-color "#E4E9ED")
(def separator-color "#0000001f")

View File

@ -12,45 +12,49 @@
color-purple
text1-color
text2-color
toolbar-background1]]))
toolbar-background1]]
[syng-im.components.realm :refer [list-view]]
[reagent.core :as r]))
(defn toolbar [{:keys [title nav-action action background-color content style]}]
(let [style (merge {:flexDirection :row
(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)
:height 56
:elevation 2} style)]
[view {:style style}
(if nav-action
[touchable-highlight {:on-press (:handler nav-action)}
(when (not hide-nav?)
(if nav-action
[touchable-highlight {:on-press (:handler nav-action)}
[view {:width 56
:height 56
:alignItems "center"
:justifyContent "center"}
[image (:image nav-action)]]]
[touchable-highlight {:on-press #(dispatch [:navigate-back])}
[view {:width 56
:height 56}
[image {:source {:uri "icon_back"}
:style {:marginTop 21
:marginLeft 23
:width 8
:height 14}}]]]))
(or custom-content
[view {:style {:flex 1
:alignItems "center"
:justifyContent "center"}}
[text {:style {:marginTop -2.5
:color text1-color
:fontSize 16
:fontFamily font}}
title]])
custom-action
(when action
[touchable-highlight {:on-press (:handler action)}
[view {:width 56
:height 56
:alignItems :center
:justifyContent :center}
[image (:image nav-action)]]]
[touchable-highlight {:on-press #(dispatch [:navigate-back])}
[view {:width 56
:height 56}
[icon :back {:marginTop 21
:marginLeft 23
:width 8
:height 14}]]])
(if content
[view {:flex 1
:alignItems :center
:justifyContent :center}
content]
[view {:flex 1
:alignItems :center
:justifyContent :center}
[text {:marginTop -2.5
:color text1-color
:fontSize 16
:fontFamily font}
title]])
[touchable-highlight {:on-press (:handler action)}
[view {:width 56
:height 56
:alignItems :center
:justifyContent :center}
[image (:image action)]]]]))
:alignItems "center"
:justifyContent "center"}
[image (:image action)]]])]))

View File

@ -6,3 +6,34 @@
(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)))

View File

@ -4,7 +4,7 @@
;; schema of app-db
(def schema {:greeting s/Str})
(def default-view :discovery)
(def default-view :chat-list)
;; initial state of app-db
(def app-db {:identity-password "replace-me-with-user-entered-password"
@ -22,7 +22,11 @@
:signed-up true
:view-id default-view
:navigation-stack (list default-view)
:name "My Name"
;; TODO fix hardcoded values
:username "My Name"
:phone-number "3147984309"
:email "myemail@gmail.com"
:status "Hi, this is my status"
:current-tag nil})
(def protocol-initialized-path [:protocol-initialized])

View File

@ -24,6 +24,11 @@
;; discovery.cljs
(def discovery-toolbar-content
{:flex 1
:alignItems :center
:justifyContent :center})
(def discovery-search-input
{:flex 1
:marginLeft 18

View File

@ -3,6 +3,9 @@
[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]]
[syng-im.protocol.api :refer [init-protocol]]
[syng-im.protocol.protocol-handler :refer [make-handler]]
[syng-im.models.protocol :refer [update-identity
@ -11,12 +14,8 @@
[syng-im.models.messages :refer [save-message update-message!]]
[syng-im.models.commands :refer [set-commands]]
[syng-im.handlers.server :as server]
[syng-im.chat.suggestions :refer [get-command
handle-command
get-command-handler
load-commands
apply-staged-commands
check-suggestion]]
[syng-im.chat.suggestions :refer [load-commands]]
[syng-im.handlers.sign-up :as sign-up-service]
[syng-im.models.chats :refer [chat-exists?
create-chat
chat-add-participants
@ -64,8 +63,9 @@
(nav/preload-data! db [nil view-id]))
(register-handler :initialize-db
(enrich preload-data!)
(fn [_ _] app-db))
(fn [_ _]
(assoc app-db
:signed-up (storage/get kv/kv-store :signed-up))))
(register-handler :set-loading
(fn [db [_ value]]

View File

@ -0,0 +1,22 @@
(ns syng-im.handlers.content-suggestions
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[syng-im.db :as db]
[syng-im.utils.logging :as log]
[clojure.string :as s]))
(def suggestions
{:phone [{:value "89171111111"
:description "Number format 1"}
{:value "+79171111111"
:description "Number format 2"}
{:value "9171111111"
:description "Number format 3"}]})
(defn get-content-suggestions [db command text]
(or (when command
(when-let [command-suggestions ((:command command) suggestions)]
(filterv (fn [s]
(and (.startsWith (:value s) (or text ""))
(not= (:value s) text)))
command-suggestions)))
[]))

View File

@ -70,3 +70,14 @@
(-> db
(push-view :add-participants)
clear-new-participants)))
(register-handler :show-profile
(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))

View File

@ -4,7 +4,8 @@
[syng-im.models.chats :refer [chats-list chat-by-id]]
[syng-im.models.contacts :refer [get-contacts
contacts-list-exclude
contacts-list-include]]
contacts-list-include
contact-by-identity]]
syng-im.chat.subs
syng-im.navigation.subs
syng-im.discovery.subs
@ -17,37 +18,10 @@
(reaction (k @db))))
;; -- User data --------------------------------------------------------------
(register-sub
:signed-up
(fn [db _]
(reaction (:signed-up @db))))
(register-sub :all-contacts
(fn [db _]
(let [contacts (reaction (:contacts @db))]
(reaction (sort-by :name @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 :all-new-contacts
(fn [db _]
(contacts-by-current-chat remove db)))
(register-sub :current-chat-contacts
(fn [db _]
(contacts-by-current-chat filter db)))
(register-sub :db
(fn [db _] (reaction @db)))

View File

@ -8,3 +8,9 @@
;; todo check wrong numbers, .getNumber returns empty string
(defn format-phone-number [number]
(str (.getNumber (js/PhoneNumber. number country-code "international"))))
(defn valid-mobile-number? [number]
(when (string? number)
(let [number-obj (js/PhoneNumber. number country-code "international")]
(and (.isValid number-obj)
(.isMobile number-obj)))))