Merge pull request #58 from status-im/feature-discover

Feature discover

Former-commit-id: bda5ce32ab
This commit is contained in:
Jarrad 2016-05-10 13:21:58 +02:00
commit 3c7c91b745
26 changed files with 953 additions and 43 deletions

View File

@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application
android:allowBackup="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

View File

@ -8,9 +8,9 @@
[reagent "0.5.1" :exclusions [cljsjs/react]]
[re-frame "0.6.0"]
[prismatic/schema "1.0.4"]
^{:voom {:repo "git@github.com:status-im/status-lib.git"
:branch "master"}}
[syng-im/protocol "0.1.1-20160430_080316-gf359cb7"]
^{:voom {:repo "https://github.com/status-im/status-lib.git"
:branch "feature-discover"}}
[syng-im/protocol "0.1.1-20160506_171115-ge2c95c1"]
[natal-shell "0.1.6"]]
:plugins [[lein-cljsbuild "1.1.1"]
[lein-figwheel "0.5.0-2"]]

View File

@ -8,6 +8,8 @@
[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]]
@ -37,6 +39,8 @@
view-id (subscribe [:view-id])]
(fn []
(case (if @signed-up @view-id :chat)
:discovery [discovery]
:discovery-tag [discovery-tag]
:add-participants [new-participants]
:remove-participants [remove-participants]
:chat-list [chats-list]

View File

@ -0,0 +1,178 @@
(ns syng-im.components.carousel
(:require [syng-im.components.react :refer [android?
view
scroll-view
touchable-without-feedback
text]]
[syng-im.components.carousel.styles :as st]
[syng-im.utils.logging :as log]))
(defn window-page-width []
(.-width (.get (.. js/React -Dimensions) "window")))
(def defaults {:gap 10
:sneak 10
:pageStyle {}
:scrollThreshold 20})
(defn get-active-page [data]
(get data :activePage 0))
(defn get-sneak [data]
(get data :sneak (:sneak defaults)))
(defn get-gap [data]
(get data :gap (:gap defaults)))
(defn compute-page-width
([gap sneak]
(compute-page-width (window-page-width) gap sneak))
([window-page-width gap sneak]
(- window-page-width (+ (* 2 gap) (* 2 sneak)))))
(defn get-page-width [data]
(get data :pageWidth (compute-page-width (get-gap data) (get-sneak data))))
(defn get-page-style [data]
(let [data-style (get data :pageStyle {})]
(merge (:pageStyle defaults) data-style)))
(defn get-scroll-threshold [data]
(get data :scrollThreshold (:scrollThreshold defaults)))
(defn apply-props [component props]
(let [sneak (get-sneak props)
page-width (get-page-width props)
style (get-page-style props)
gap (quot (- (- (window-page-width) (* 2 sneak)) page-width) 2)]
(reagent.core/set-state component {:sneak sneak
:pageWidth page-width
:pageStyle style
:gap gap})))
(defn scroll-to [component x y]
(.scrollTo (.-scrollView component) (clj->js {:y y
:x x})))
(defn get-current-position [event]
(.-x (.-contentOffset (.-nativeEvent event))))
(defn go-to-page [component page]
(let [props (reagent.core/props component)
state (reagent.core/state component)
page-width (get-page-width state)
gap (get-gap state)
page-position (* page (+ page-width gap))]
(log/debug "go-to-page: props-page-width=" page-width "; gap=" gap
"; page-position=" page-position)
(scroll-to component page-position 0)
(reagent.core/set-state component {:activePage page})
(when (:onPageChange props)
((:onPageChange props) page))))
(defn on-scroll-end [event component starting-position]
(let [props (reagent.core/props component)
state (reagent.core/state component)
scroll-threshold (get-scroll-threshold props)
current-page (get-active-page state)
current-position (get-current-position event)
direction (cond
(> current-position (+ starting-position scroll-threshold)) 1
(< current-position (- starting-position scroll-threshold)) -1
:else 0)
new-page (+ current-page direction)
]
(log/debug state "on-scroll-end: starting position=" starting-position
"; current-position=" current-position "; direction=" direction
"; current-page=" current-page "; new-page=" new-page)
(if (not= current-page new-page)
(go-to-page component new-page)
(scroll-to component starting-position 0))))
(defn component-will-mount [component new-args]
(let [props (reagent.core/props component)]
(log/debug "component-will-mount: new-args="new-args)
(apply-props component props)))
(defn component-did-mount [component]
(let [props (reagent.core/props component)
initial-page (.-initialPage props)]
(log/debug "component-did-mount: initial-page="initial-page)
(when (pos? initial-page)
(go-to-page component initial-page))))
(defn component-will-update [component new-argv]
(log/debug "component-will-update: "))
(defn component-did-update [component old-argv]
(log/debug "component-did-update"))
(defn component-will-receive-props [component new-argv]
(log/debug "component-will-receive-props: new-argv=" new-argv)
(apply-props component new-argv))
(defn get-event-width [event]
(.-width (.-layout (.-nativeEvent event))))
(defn on-layout-change [event component]
(let [state (reagent.core/state component)
page-width (compute-page-width (get-event-width event) (get-gap state) (get-sneak state))
state-page-width (get-page-width state)
active-page (get-active-page state)
gap (get-gap state)
page-position (* active-page (+ page-width gap))]
(log/debug "Layout changed: " " page-width=" page-width "; state-page-width=" state-page-width)
(if (not= page-width state-page-width)
(do
(reagent.core/set-state component {:pageWidth page-width})
(.setState component {:layout (.-layout (.-nativeEvent event))})
)
(scroll-to component page-position 0))))
(defn get-pages [component data children]
(let [page-width (get-page-width data)
page-style (get-page-style data)
gap (get-gap data)
margin (quot gap 2)]
(doall (map-indexed (fn [index child]
(let [page-index index
touchable-data {:key index
:onPress #(go-to-page component page-index)}]
[touchable-without-feedback touchable-data
[view {:style [(st/page page-width margin)
page-style]
:onLayout #(log/debug "view onLayout" %)}
child]])) children))))
(defn reagent-render [data children]
(let [starting-position (atom 0)
component (reagent.core/current-component)
state (reagent.core/state component)
sneak (get-sneak state)
gap (get-gap state)]
(log/debug "reagent-render: " data state)
[view {:style st/scroll-view-container}
[scroll-view {:contentContainerStyle (st/content-container sneak gap)
:automaticallyAdjustContentInsets false
:bounces false
:decelerationRate 0.9
:horizontal true
:onLayout #(on-layout-change % component)
:onScrollBeginDrag #(reset! starting-position (get-current-position %))
:onScrollEndDrag #(on-scroll-end % component @starting-position)
:showsHorizontalScrollIndicator false
:ref #(set! (.-scrollView component) %)}
(get-pages component state children)]]))
(defn carousel [data children]
(let [component-data {:component-did-mount component-did-mount
:component-will-mount component-will-mount
:component-will-receive-props component-will-receive-props
:component-will-update component-will-update
:component-did-update component-did-update
:display-name "carousel"
:reagent-render reagent-render}]
(log/debug "Creating carousel component: " data)
(reagent.core/create-class component-data)))

View File

@ -0,0 +1,24 @@
(ns syng-im.components.carousel.styles
(:require [syng-im.components.styles :refer [font
title-font
color-white
chat-background
online-color
selected-message-color
separator-color
text1-color
text2-color
toolbar-background1]]))
(def scroll-view-container
{:flex 1})
(defn content-container [sneak gap]
{:paddingLeft (+ sneak (quot gap 2))
:paddingRight (+ sneak (quot gap 2))})
(defn page [page-width margin]
{:width page-width
:justifyContent "center"
:marginLeft margin
:marginRight margin})

View File

@ -177,6 +177,7 @@
[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)}]))))
(defn chat []

View File

@ -0,0 +1,83 @@
(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.resources :as res]
[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]
(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"
: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"))
)

View File

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

View File

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

View File

@ -0,0 +1,24 @@
(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.resources :as res]
[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 res/user-no-photo}]
]
]))

View File

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

View File

@ -0,0 +1,60 @@
(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.resources :as res]
[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}]
]))))

View File

@ -0,0 +1,44 @@
(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)))

View File

@ -0,0 +1,175 @@
(ns syng-im.components.discovery.styles
(:require [syng-im.components.styles :refer [font
title-font
color-white
chat-background
online-color
selected-message-color
separator-color
text1-color
text2-color
toolbar-background1]]))
;; common
(def row-separator
{:borderBottomWidth 1
:borderBottomColor "#eff2f3"})
(def row
{:flexDirection "row"})
(def column
{:flexDirection "column"})
;; discovery.cljs
(def discovery-search-input
{:flex 1
:marginLeft 18
:lineHeight 42
:fontSize 14
:fontFamily "Avenir-Roman"
:color "#9CBFC0"})
(def discovery-title
{:color "#000000de"
:alignSelf "center"
:textAlign "center"
:fontFamily "sans-serif"
:fontSize 16})
(def discovery-toolbar
{:backgroundColor "#eef2f5"
:elevation 0})
(def discovery-subtitle
{:color "#8f838c93"
:fontFamily "sans-serif-medium"
:fontSize 14})
(def section-spacing
{:paddingLeft 30
:paddingTop 15
:paddingBottom 15})
;; discovery_popular.cljs
(def carousel-page-style
{:borderRadius 1
:shadowColor "black"
:shadowRadius 1
:shadowOpacity 0.8
:elevation 2
:marginBottom 10})
;; discovery_populat_list.cljs
(def tag-name
{:color "#7099e6"
:fontFamily "sans-serif-medium"
:fontSize 14
:paddingRight 5
:paddingBottom 2
:alignItems "center"
:justifyContent "center"})
(def tag-name-container
{:flexDirection "column"
:backgroundColor "#eef2f5"
:borderRadius 5
:padding 4})
(def tag-count
{:color "#838c93"
:fontFamily "sans-serif"
:fontSize 12
:paddingRight 5
:paddingBottom 2
:alignItems "center"
:justifyContent "center"})
(def tag-count-container
{:flex 0.2
:flexDirection "column"
:alignItems "flex-end"
:paddingTop 10
:paddingRight 9})
(def popular-list-container
{:flex 1
:backgroundColor "white"
:paddingLeft 10
:paddingTop 16})
(def popular-list
{:backgroundColor "white"
:paddingTop 13})
;; discover_popular_list_item.cjls
(def popular-list-item
{:flexDirection "row"
:paddingTop 10
:paddingBottom 10})
(def popular-list-item-status
{:color "black"
:fontFamily "sans-serif"
:lineHeight 22
:fontSize 14})
(def popular-list-item-name
{:color "black"
:fontFamily "sans-serif-medium"
:fontSize 14
:lineHeight 24})
(def popular-list-item-name-container
{:flex 0.8
:flexDirection "column"})
(def popular-list-item-avatar-container
{:flex 0.2
:flexDirection "column"
:alignItems "center"
:paddingTop 5})
(def popular-list-item-avatar
{:resizeMode "contain"
:borderRadius 150
:width 40
:height 40})
;; discovery_recent
(def recent-list
{:backgroundColor "white"
:paddingLeft 15})
;; discovery_tag
(def discovery-tag-container
{:flex 1
:backgroundColor "#eef2f5"})
(def tag-title
{:color "#7099e6"
:fontFamily "sans-serif-medium"
:fontSize 14
:paddingRight 5
:paddingBottom 2})
(def tag-title-container
{:backgroundColor "#eef2f5"
:flexWrap :wrap
:borderRadius 5
:padding 4})
(def icon-back
{:width 8
:height 14})
(def icon-search
{:width 17
:height 17})

View File

@ -0,0 +1,48 @@
(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))))))

View File

@ -16,6 +16,8 @@
content])
(def toolbar-android (r/adapt-react-class (.-ToolbarAndroid js/React)))
(def list-view (r/adapt-react-class (.-ListView js/React)))
(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)))
(defn text-input [props text]
[text-input-class (merge

View File

@ -18,37 +18,44 @@
[reagent.core :as r]
[syng-im.navigation :refer [nav-pop]]))
(defn toolbar [{:keys [navigator title nav-action action background-color]}]
[view {:style {:flexDirection "row"
:backgroundColor (or background-color toolbar-background1)
:height 56
:elevation 2}}
(if nav-action
[touchable-highlight {:on-press (:handler nav-action)}
(defn toolbar [{:keys [navigator title nav-action action background-color 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)}
[view {:width 56
:height 56
:alignItems "center"
:justifyContent "center"}
[image (:image nav-action)]]]
[touchable-highlight {:on-press #(nav-pop navigator)}
[view {:width 56
:height 56}
[image {:source {:uri "icon_back"}
:style {:marginTop 21
:marginLeft 23
:width 8
:height 14}}]]])
(if content
[view {:style {:flex 1
:alignItems "center"
:justifyContent "center"}}
content]
[view {:style {:flex 1
:alignItems "center"
:justifyContent "center"}}
[text {:style {: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 nav-action)]]]
[touchable-highlight {:on-press #(nav-pop navigator)}
[view {:width 56
:height 56}
[image {:source {:uri "icon_back"}
:style {:marginTop 21
:marginLeft 23
:width 8
:height 14}}]]])
[view {:style {:flex 1
:alignItems "center"
:justifyContent "center"}}
[text {:style {: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)]]]])
[image (:image action)]]]]))

View File

@ -4,7 +4,7 @@
;; schema of app-db
(def schema {:greeting s/Str})
(def default-view :chat-list)
(def default-view :discovery)
;; initial state of app-db
(def app-db {:greeting "Hello Clojure in iOS and Android!"
@ -21,8 +21,9 @@
:new-participants #{}
:signed-up false
:view-id default-view
:navigation-stack (list default-view)})
:navigation-stack (list default-view)
:name "My Name"
:current-tag nil})
(def protocol-initialized-path [:protocol-initialized])
(def identity-password-path [:identity-password])
@ -47,3 +48,8 @@
(def show-actions-path [:show-actions])
(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])

View File

@ -29,6 +29,7 @@
apply-staged-commands
check-suggestion]]
[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
chat-add-participants

View File

@ -0,0 +1,125 @@
(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.resources :as res]
[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)))

View File

@ -45,12 +45,27 @@
:name "string"
:group-chat {:type "bool"
:indexed true}
:is-active "bool"
:timestamp "int"
:contacts {:type "list"
:objectType "chat-contact"}
:last-msg-id "string"}}]})
: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"}}]})
(def realm (js/Realm. (clj->js opts)))
@ -84,6 +99,10 @@
value))]
query))
(defn get-by-filter [schema-name filter]
(-> (.objects realm (name schema-name))
(.filtered filter)))
(defn get-by-field [schema-name field value]
(let [q (to-query schema-name :eq field value)]
(.filtered (.objects realm (name schema-name)) q)))

View File

@ -40,4 +40,6 @@
(dispatch [:you-removed-from-group from group-id msg-id]))
:participant-left-group (let [{:keys [group-id from msg-id]} event]
(dispatch [:participant-left-group from group-id msg-id]))
:discover-response (let [{:keys [from payload]} event]
(dispatch [:discovery-response-received from payload]))
(log/info "Don't know how to handle" event-type)))})

View File

@ -16,5 +16,6 @@
(def add-icon (js/require "./images/add.png"))
(def trash-icon (js/require "./images/trash.png"))
(def leave-icon (js/require "./images/leave.png"))
(def icon-close-gray (js/require "./images/icon_close_gray.png"))
(def menu (js/require "./images/ic_menu_black_24dp_1x.png"))
(def search (js.require "./images/ic_search_black_24dp_1x.png"))

View File

@ -2,6 +2,7 @@
(: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
chat-updated?]]
[syng-im.models.chats :refer [chats-list
@ -75,6 +76,8 @@
(let [current-chat-id (current-chat-id @db)]
(reaction (get-in @db [:chats current-chat-id])))))
;; -- User data --------------------------------------------------------------
;; (register-sub