Merge pull request #58 from status-im/feature-discover
Feature discover
This commit is contained in:
commit
bda5ce32ab
|
@ -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 |
|
@ -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"]]
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)))
|
|
@ -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})
|
|
@ -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 []
|
||||
|
|
|
@ -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"))
|
||||
)
|
|
@ -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"])))
|
|
@ -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}]]))
|
|
@ -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}]
|
||||
]
|
||||
]))
|
|
@ -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}]
|
||||
))
|
|
@ -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}]
|
||||
]))))
|
|
@ -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)))
|
|
@ -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})
|
|
@ -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))))))
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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)]]]]))
|
||||
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
|
|
@ -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)))
|
||||
|
|
|
@ -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)))})
|
||||
|
|
|
@ -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"))
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue