improved contact request (#122)

This commit is contained in:
alwxndr 2016-08-24 10:29:40 +03:00
parent 0e9f91026d
commit b260e7ba6b
17 changed files with 267 additions and 164 deletions

4
.gitignore vendored
View File

@ -46,6 +46,10 @@ index.android.js
index.ios.js
target/
# Generated by lein voom
#
pom.xml
# Figwheel
#
figwheel_server.log

View File

@ -8,9 +8,8 @@
[reagent "0.5.1" :exclusions [cljsjs/react]]
[re-frame "0.7.0"]
[prismatic/schema "1.0.4"]
^{:voom {:repo "git@github.com:status-im/status-lib.git"
:branch "message-delivery"}}
[status-im/protocol "0.2.0-20160829_094621-g83381b2"]
^{:voom {:repo "git@github.com:status-im/status-lib.git" :branch "master"}}
[status-im/protocol "0.2.1-20160908_061908-geefb360"]
[natal-shell "0.3.0"]
[com.andrewmcveigh/cljs-time "0.4.0"]
[tailrecursion/cljs-priority-map "1.2.0"]

View File

@ -33,7 +33,8 @@
status-im.chat.handlers.receive-message
[cljs.core.async :as a]
status-im.chat.handlers.webview-bridge
status-im.chat.handlers.wallet-chat))
status-im.chat.handlers.wallet-chat
[status-im.utils.logging :as log]))
(register-handler :set-show-actions
(fn [db [_ show-actions]]
@ -291,10 +292,11 @@
(update :chats assoc chat-id new-chat)
(update :chats-ids conj chat-id)))
(defn save-chat!
(defn save-new-chat!
[{:keys [new-chat]} _]
(chats/create-chat new-chat))
(defn open-chat!
[_ [_ chat-id _ navigation-type]]
(dispatch [(or navigation-type :navigate-to) :chat chat-id]))
@ -302,20 +304,20 @@
(register-handler ::start-chat!
(-> prepare-chat
((enrich add-chat))
((after save-chat!))
((after save-new-chat!))
((after open-chat!))))
(register-handler :start-chat
(u/side-effect!
(fn [{:keys [chats]} [_ contcat-id options navigation-type]]
(if (chats contcat-id)
(dispatch [(or navigation-type :navigate-to) :chat contcat-id])
(dispatch [::start-chat! contcat-id options navigation-type])))))
(fn [{:keys [chats]} [_ contact-id options navigation-type]]
(if (chats contact-id)
(dispatch [(or navigation-type :navigate-to) :chat contact-id])
(dispatch [::start-chat! contact-id options navigation-type])))))
(register-handler :add-chat
(-> prepare-chat
((enrich add-chat))
((after save-chat!))))
((after save-new-chat!))))
(register-handler :switch-command-suggestions!
(u/side-effect!
@ -417,11 +419,16 @@
(fn [db [_ chat-id mode]]
(assoc-in db [:kb-mode chat-id] mode)))
(defn save-chat!
[_ [_ chat]]
(chats/create-chat chat))
(register-handler :update-chat!
(fn [db [_ chat-id new-chat-data]]
(-> (fn [db [_ {:keys [chat-id] :as chat}]]
(if (get-in db [:chats chat-id])
(update-in db [:chats chat-id] merge new-chat-data)
db)))
(update-in db [:chats chat-id] merge chat)
db))
((after save-chat!))))
(register-handler :check-autorun
(u/side-effect!

View File

@ -22,13 +22,13 @@
[status-im.chat.views.suggestions :refer [suggestion-container]]
[status-im.chat.views.response :refer [response-view]]
[status-im.chat.views.new-message :refer [chat-message-new]]
[status-im.chat.views.actions :refer [actions-view]]
[status-im.i18n :refer [label label-pluralize]]
[status-im.components.animation :as anim]
[reagent.core :as r]
[clojure.string :as str]
[cljs-time.core :as t]))
(defn contacts-by-identity [contacts]
(->> contacts
(map (fn [{:keys [identity] :as contact}]
@ -86,114 +86,6 @@
[view nil]]
items])
(defn action-view [{{:keys [icon-style
custom-icon
handler
title
subtitle]
icon-name :icon} :action
platform-specific :platform-specific}]
[touchable-highlight {:on-press (fn []
(dispatch [:set-show-actions false])
(when handler
(handler)))}
[view st/action-icon-row
[view st/action-icon-view
(or custom-icon
[icon icon-name icon-style])]
[view st/action-view
[text {:style st/action-title
:platform-specific platform-specific
:font :medium} title]
(when-let [subtitle subtitle]
[text {:style st/action-subtitle
:platform-specific platform-specific
:font :default}
subtitle])]]])
(defview menu-item-icon-profile []
[chat-id [:chat :chat-id]
group-chat [:chat :group-chat]
name [:chat :name]
color [:chat :color]]
;; TODO stub data ('online' property)
[chat-icon-view-menu-item chat-id group-chat name color true])
(defn members-text [members]
(truncate-str (str (s/join ", " (map #(:name %) members)) " " (label :t/and-you)) 35))
(defn actions-list-view [{styles :styles :as platform-specific}]
(let [{:keys [group-chat chat-id]} (subscribe [:chat-properties [:group-chat :chat-id]])
members (subscribe [:current-chat-contacts])
status-bar-height (get-in styles [:components :status-bar :default :height])]
(when-let [actions (if @group-chat
[{:title (label :t/members-title)
:subtitle (members-text @members)
:icon :menu_group
:icon-style {:width 25
:height 19}
;; TODO not implemented: action Members
:handler nil}
{:title (label :t/search-chat)
:subtitle (label :t/not-implemented)
:icon :search_gray_copy
:icon-style {:width 17
:height 17}
;; TODO not implemented: action Search chat
:handler nil}
{:title (label :t/notifications-title)
:subtitle (label :t/not-implemented)
;;:subtitle "Chat muted"
:icon :muted
:icon-style {:width 18
:height 21}
;; TODO not implemented: action Notifications
:handler nil}
{:title (label :t/settings)
:icon :settings
:icon-style {:width 20
:height 13}
:handler #(dispatch [:show-group-settings])}]
[{:title (label :t/profile)
:custom-icon [menu-item-icon-profile]
:icon :menu_group
:icon-style {:width 25
:height 19}
:handler #(dispatch [:show-profile @chat-id])}
{:title (label :t/search-chat)
:subtitle (label :t/not-implemented)
:icon :search_gray_copy
:icon-style {:width 17
:height 17}
;; TODO not implemented: action Search chat
:handler nil}
{:title (label :t/notifications-title)
:subtitle (label :t/not-implemented)
;;:subtitle "Notifications on"
:icon :muted
:icon-style {:width 18
:height 21}
;; TODO not implemented: action Notifications
:handler nil}
{:title (label :t/settings)
:subtitle (label :t/not-implemented)
:icon :settings
:icon-style {:width 20
:height 13}
;; TODO not implemented: action Settings
:handler nil}])]
[view (-> (st/actions-wrapper status-bar-height)
(merge (get-in styles [:components :actions-list-view])))
[view st/actions-separator]
[view st/actions-view
(for [action actions]
^{:key action} [action-view {:platform-specific platform-specific
:action action}])]])))
(defn actions-view [platform-specific]
[overlay {:on-click-outside #(dispatch [:set-show-actions false])}
[actions-list-view platform-specific]])
(defn online-text [contact chat-id]
(if contact
(let [last-online (get contact :last-online)
@ -215,10 +107,11 @@
[view (st/chat-name-view @show-actions)
[text {:style st/chat-name-text
:platform-specific platform-specific
:number-of-lines 1
:font :medium}
(if (str/blank? @name)
(label :t/user-anonymous)
(truncate-str (or @name (label :t/chat-name)) 30))]
(or @name (label :t/chat-name)))]
(if @group-chat
[view {:flexDirection :row}
[icon :group st/group-icon]

View File

@ -0,0 +1,145 @@
(ns status-im.chat.views.actions
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[clojure.string :as s]
[status-im.components.react :refer [view
animated-view
text
icon
touchable-highlight
list-view
list-item]]
[status-im.components.chat-icon.screen :refer [chat-icon-view-menu-item]]
[status-im.chat.styles.screen :as st]
[status-im.i18n :refer [label label-pluralize]]
[status-im.utils.logging :as log]))
(defview menu-item-icon-profile []
[chat-id [:chat :chat-id]
group-chat [:chat :group-chat]
name [:chat :name]
color [:chat :color]]
;; TODO stub data ('online' property)
[chat-icon-view-menu-item chat-id group-chat name color true])
(defn- members-text [members]
(str (s/join ", " (map #(:name %) members))
" "
(label :t/and-you)))
(defn item-members [members]
{:title (label :t/members-title)
:subtitle (members-text members)
:icon :menu_group
:icon-style {:width 25
:height 19}
;; TODO not implemented: action Members
:handler nil})
(defn item-user [chat-id]
{:title (label :t/profile)
:custom-icon [menu-item-icon-profile]
:icon :menu_group
:icon-style {:width 25
:height 19}
:handler #(dispatch [:show-profile chat-id])})
(defn item-add-to-contacts [contact]
{:title (label :t/add-to-contacts)
:icon :menu_group
:icon-style {:width 20
:height 17}
:handler #(dispatch [:add-pending-contact contact])})
(def item-search
{:title (label :t/search-chat)
:subtitle (label :t/not-implemented)
:icon :search_gray_copy
:icon-style {:width 17
:height 17}
;; TODO not implemented: action Search chat
:handler nil})
(def item-notifications
{:title (label :t/notifications-title)
:subtitle (label :t/not-implemented)
;;:subtitle "Chat muted"
:icon :muted
:icon-style {:width 18
:height 21}
;; TODO not implemented: action Notifications
:handler nil})
(def item-settings
{:title (label :t/settings)
:icon :settings
:icon-style {:width 20
:height 13}
:handler #(dispatch [:show-group-settings])})
(defn group-chat-items [members]
[(item-members members)
item-search
item-notifications
item-settings])
(defn user-chat-items [chat-id {:keys [pending] :as contact}]
[(item-user chat-id)
(if pending (item-add-to-contacts contact) nil)
item-search
item-notifications
item-settings])
(defn overlay [{:keys [on-click-outside]} items]
[view st/actions-overlay
[touchable-highlight {:on-press on-click-outside
:style st/overlay-highlight}
[view nil]]
items])
(defn action-view [{{:keys [icon-style
custom-icon
handler
title
subtitle]
icon-name :icon} :action
platform-specific :platform-specific}]
[touchable-highlight {:on-press (fn []
(dispatch [:set-show-actions false])
(when handler
(handler)))}
[view st/action-icon-row
[view st/action-icon-view
(or custom-icon
[icon icon-name icon-style])]
[view st/action-view
[text {:style st/action-title
:platform-specific platform-specific
:number-of-lines 1
:font :medium} title]
(when-let [subtitle subtitle]
[text {:style st/action-subtitle
:platform-specific platform-specific
:number-of-lines 1
:font :default}
subtitle])]]])
(defn actions-list-view [{styles :styles :as platform-specific}]
(let [{:keys [group-chat chat-id]} (subscribe [:chat-properties [:group-chat :chat-id]])
members (subscribe [:current-chat-contacts])
status-bar-height (get-in styles [:components :status-bar :default :height])]
(when-let [actions (if @group-chat
(group-chat-items @members)
(user-chat-items @chat-id (first @members)))]
[view (-> (st/actions-wrapper status-bar-height)
(merge (get-in styles [:components :actions-list-view])))
[view st/actions-separator]
[view st/actions-view
(for [action actions]
(if action
^{:key action} [action-view {:platform-specific platform-specific
:action action}]))]])))
(defn actions-view [platform-specific]
[overlay {:on-click-outside #(dispatch [:set-show-actions false])}
[actions-list-view platform-specific]])

View File

@ -19,9 +19,10 @@
[chat-icon-view-chat-list chat-id group-chat name color online]]
[view st/item-container
[view st/name-view
[text {:style st/name-text} (if (str/blank? name)
[text {:style st/name-text}
(if (str/blank? name)
(label :t/user-anonymous)
(truncate-str name 20))]
(truncate-str name 30))]
(when group-chat
[icon :group st/group-icon])
(when group-chat

View File

@ -16,12 +16,17 @@
(contacts/save-contacts [contact]))
(defn watch-contact
[_ [_ contact]]
(api/watch-user contact))
[_ [_ {:keys [whisper-identity]}]]
(api/watch-user whisper-identity))
(register-handler :watch-contact (u/side-effect! watch-contact))
(register-handler :update-contact
(defn send-contact-request
[{:keys [current-account-id accounts]} [_ contact]]
(let [account (get accounts current-account-id)]
(api/send-contact-request contact account)))
(register-handler :update-contact!
(-> (fn [db [_ {:keys [whisper-identity] :as contact}]]
(update-in db [:contacts whisper-identity] merge contact))
((after save-contact))))
@ -101,10 +106,18 @@
(defn save-contacts! [{:keys [new-contacts]} _]
(contacts/save-contacts new-contacts))
(defn update-pending-status [old-contacts {:keys [whisper-identity pending] :as contact}]
(let [{old-pending :pending
:as old-contact} (get old-contacts whisper-identity)]
(if old-contact
(assoc contact :pending (and old-pending pending))
(assoc contact :pending pending))))
(defn add-new-contacts
[{:keys [contacts] :as db} [_ new-contacts]]
(let [identities (set (map :whisper-identity contacts))
new-contacts' (->> new-contacts
(map #(update-pending-status contacts %))
(remove #(identities (:whisper-identity %)))
(map #(vector (:whisper-identity %) %))
(into {}))]
@ -125,13 +138,25 @@
(u/side-effect!
(fn [_ [_ {:keys [whisper-identity] :as contact}]]
(when-not (contacts/get-contact whisper-identity)
(dispatch [::new-contact contact])))))
(dispatch [::prepare-contact contact])))))
(register-handler ::new-contact
(register-handler ::prepare-contact
(-> add-new-contact
((after save-contact))
((after send-contact-request))
((after watch-contact))))
(register-handler ::update-pending-contact
(-> add-new-contact
((after save-contact))))
(register-handler :add-pending-contact
(u/side-effect!
(fn [_ [_ {:keys [whisper-identity] :as contact}]]
(let [contact (assoc contact :pending false)]
(api/send-discovery-keypair whisper-identity)
(dispatch [::update-pending-contact contact])))))
(defn set-contact-identity-from-qr
[db [_ _ contact-identity]]
(assoc db :new-contact-identity contact-identity))
@ -143,16 +168,30 @@
;; TODO: security issue: we should use `from` instead of `public-key` here, but for testing it is much easier to use `public-key`
(fn [db [_ from {{:keys [public-key last-updated name] :as account} :account}]]
(let [prev-last-updated (get-in db [:contacts public-key :last-updated])]
(if (< prev-last-updated last-updated)
(if (<= prev-last-updated last-updated)
(let [contact (-> (assoc account :whisper-identity public-key)
(dissoc :public-key))]
(dispatch [:update-contact contact])
(dispatch [:update-chat! public-key {:name name}])))))))
(dispatch [:update-contact! contact])
(dispatch [:update-chat! {:chat-id public-key
:name name}])))))))
(register-handler :contact-online-received
(u/side-effect!
(fn [db [_ from {last-online :at :as payload}]]
(let [prev-last-online (get-in db [:contacts from :last-online])]
(if (< prev-last-online last-online)
(dispatch [:update-contact {:whisper-identity from
(dispatch [:update-contact! {:whisper-identity from
:last-online last-online}]))))))
(register-handler :contact-request-received
(u/side-effect!
(fn [_ [_ {:keys [contact from]}]]
(let [contact (assoc contact :whisper-identity from
:pending true)]
(dispatch [:add-contacts [contact]])))))
(register-handler :contact-keypair-received
(u/side-effect!
(fn [_ [_ from]]
(api/stop-watching-user from)
(api/watch-user from))))

View File

@ -69,8 +69,8 @@
[text {:style st/show-all-text} (label :show-all)]]])])
(defn contact-list [{platform-specific :platform-specific}]
(let [contacts (subscribe [:get-contacts-with-limit contacts-limit])
contcats-count (subscribe [:contacts-count])
(let [contacts (subscribe [:get-added-contacts-with-limit contacts-limit])
contacts-count (subscribe [:added-contacts-count])
click-handler (subscribe [:get :contacts-click-handler])
show-toolbar-shadow? (r/atom false)]
(fn []
@ -80,20 +80,20 @@
(when @show-toolbar-shadow?
[linear-gradient {:style st/contact-group-header-gradient-bottom
:colors st/contact-group-header-gradient-bottom-colors}])]
(if (pos? @contcats-count)
(if (pos? @contacts-count)
[scroll-view {:style st/contact-groups
:onScroll (fn [e]
(let [offset (.. e -nativeEvent -contentOffset -y)]
(reset! show-toolbar-shadow? (<= st/contact-group-header-height offset))))}
[contact-group
@contacts
@contcats-count
@contacts-count
(label :t/contacs-group-dapps)
:dapps true
@click-handler]
[contact-group
@contacts
@contcats-count
@contacts-count
(label :t/contacs-group-people)
:people false
@click-handler]]

View File

@ -12,19 +12,21 @@
(sort-by :name #(compare (clojure.string/lower-case %1)
(clojure.string/lower-case %2)) (vals contacts)))
(register-sub :all-contacts
(register-sub :all-added-contacts
(fn [db _]
(let [contacts (reaction (:contacts @db))]
(reaction (sort-contacts @contacts)))))
(->> (remove #(:pending %) @contacts)
(sort-contacts)
(reaction)))))
(register-sub :get-contacts-with-limit
(register-sub :get-added-contacts-with-limit
(fn [_ [_ limit]]
(let [contacts (subscribe [:all-contacts])]
(let [contacts (subscribe [:all-added-contacts])]
(reaction (take limit @contacts)))))
(register-sub :contacts-count
(register-sub :added-contacts-count
(fn [_ _]
(let [contacts (subscribe [:all-contacts])]
(let [contacts (subscribe [:all-added-contacts])]
(reaction (count @contacts)))))
(defn get-contact-letter [contact]

View File

@ -16,7 +16,8 @@
[view st/contact-inner-container
[contact-photo contact]
[view st/info-container
[text {:style st/name-text}
[text {:style st/name-text
:number-of-lines 1}
(if (pos? (count (:name contact)))
name
;; todo is this correct behaviour?

View File

@ -42,7 +42,7 @@
(defn create-chat
([{:keys [last-message-id] :as chat}]
(let [chat (assoc chat :last-message-id (or last-message-id ""))]
(r/write :account #(r/create :account :chat chat))))
(r/write :account #(r/create :account :chat chat true))))
([db chat-id identities group-chat? chat-name]
(when-not (chat-exists? chat-id)
(let [chat-name (or chat-name

View File

@ -2,7 +2,8 @@
(:require [status-im.persistence.realm.core :as r]
[status-im.utils.identicon :refer [identicon]]
[status-im.persistence.realm-queries :refer [include-query
exclude-query]]))
exclude-query]]
[status-im.utils.logging :as log]))
(defn get-contacts []
(-> (r/get-all :account :contact)
@ -12,10 +13,14 @@
(defn get-contact [id]
(r/get-one-by-field :account :contact :whisper-identity id))
(defn create-contact [{:keys [whisper-identity] :as contact}]
(let [contact-from-db (r/get-one-by-field :account :contact
:whisper-identity whisper-identity)]
(r/create :account :contact contact (if contact-from-db true false))))
(defn create-contact [{:keys [whisper-identity pending] :as contact}]
(let [{pending-db :pending
:as contact-db} (r/get-one-by-field :account :contact
:whisper-identity whisper-identity)
contact (assoc contact :pending (boolean (if contact-db
(and pending-db pending)
pending)))]
(r/create :account :contact contact (if contact-db true false))))
(defn save-contacts [contacts]
(r/write :account #(mapv create-contact contacts)))

View File

@ -7,7 +7,8 @@
[clojure.walk :refer [stringify-keys keywordize-keys]]
[status-im.constants :as c]
[status-im.utils.types :refer [clj->json json->clj]]
[status-im.commands.utils :refer [generate-hiccup]]))
[status-im.commands.utils :refer [generate-hiccup]]
[status-im.utils.logging :as log]))
(defn get-pending-messages []
(let [collection (-> (r/get-by-fields :account :pending-message :or [[:status :sending]

View File

@ -49,7 +49,7 @@
[text {:style st/group-name-validation-message} (first validation-messages)])])
(defview new-group [{platform-specific :platform-specific}]
[contacts [:all-contacts]]
[contacts [:all-added-contacts]]
[view st/new-group-container
[new-group-toolbar platform-specific]
[view st/chat-name-container

View File

@ -24,7 +24,8 @@
:name {:type "string" :optional true}
:photo-path {:type "string" :optional true}
:last-updated {:type "int" :default 0}
:last-online {:type "int" :default 0}}}
:last-online {:type "int" :default 0}
:pending {:type "bool" :default false}}}
{:name :request
:properties {:message-id :string
:chat-id :string

View File

@ -21,6 +21,8 @@
(dispatch [:received-message (assoc payload :chat-id from
:from from
:to to)]))
:contact-request (let [{:keys [from payload]} event]
(dispatch [:contact-request-received (assoc payload :from from)]))
:message-delivered (let [{:keys [message-id from]} event]
(dispatch [:message-delivered from message-id]))
:message-seen (let [{:keys [message-id from]} event]
@ -29,6 +31,8 @@
(dispatch [:message-failed chat-id message-id]))
:message-sent (let [{:keys [message-id chat-id]} event]
(dispatch [:message-sent chat-id message-id]))
:user-discovery-keypair (let [{:keys [from]} event]
(dispatch [:contact-keypair-received from]))
:pending-message-upsert (let [{message :message} event]
(dispatch [:pending-message-upsert message]))
:pending-message-remove (let [{:keys [message-id]} event]

View File

@ -54,6 +54,7 @@
:phone-number "Phone number"
:email "Email"
:profile-no-status "No status"
:add-to-contacts "Add to contacts"
;;make_photo
:image-source-title "Profile image"