Contacts search (#448);

Processed messages cache (#552);
Fix for adding pending contacts manually (#554);
Lots of fixes for toolbar & contact item
This commit is contained in:
alwx 2016-12-05 13:50:47 +03:00
parent 6ab45b692f
commit 395aa9d807
42 changed files with 553 additions and 291 deletions

View File

@ -1,5 +1,6 @@
(ns status-im.accounts.handlers
(:require [status-im.data-store.accounts :as accounts-store]
[status-im.data-store.processed-messages :as processed-messages]
[re-frame.core :refer [register-handler after dispatch dispatch-sync debug]]
[taoensso.timbre :as log]
[status-im.protocol.core :as protocol]
@ -18,6 +19,7 @@
[status-im.utils.gfycat.core :refer [generate-gfy]]
[status-im.constants :refer [console-chat-id]]
[status-im.utils.scheduler :as s]
[status-im.protocol.message-cache :as cache]
[status-im.navigation.handlers :as nav]))
@ -149,6 +151,15 @@
(register-handler :console-create-account console-create-account)
(register-handler
:load-processed-messages
(u/side-effect!
(fn [_]
(let [now (time/now-ms)
messages (processed-messages/get-filtered (str "ttl > " now))]
(cache/init! messages)
(processed-messages/delete (str "ttl <=" now))))))
(defmethod nav/preload-data! :qr-code-view
[{:keys [current-account-id] :as db} [_ _ {:keys [contact qr-source amount?]}]]
(assoc db :qr-modal {:contact (or contact

View File

@ -9,6 +9,7 @@
get-dimensions]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.components.toolbar.styles :refer [toolbar-title-container
toolbar-title-text]]
[status-im.components.text-field.view :refer [text-field]]
@ -66,9 +67,7 @@
:style st/gradient-background}]
[status-bar {:type :transparent}]
[toolbar {:background-color :transparent
:nav-action {:image {:source {:uri :icon_back_white}
:style icon-back}
:handler #(dispatch [:navigate-back])}
:nav-action (act/back-white #(dispatch [:navigate-back]))
:custom-content [toolbar-title]
:actions [{:image {:style icon-search}
:handler #()}]}]

View File

@ -9,13 +9,12 @@
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.text-field.view :refer [text-field]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.components.toolbar.styles :refer [toolbar-gradient
toolbar-title-container
toolbar-title-text]]
[status-im.components.styles :refer [color-purple
color-white
icon-back
icon-search
button-input]]
[status-im.components.react :refer [linear-gradient]]
[status-im.i18n :refer [label]]
@ -78,12 +77,8 @@
[view st/screen-container
[status-bar {:type :transparent}]
[toolbar {:background-color :transparent
:nav-action {:image {:source {:uri :icon_back}
:style icon-back}
:handler #(dispatch [:navigate-back])}
:custom-content [toolbar-title]
:actions [{:image {:style icon-search}
:handler #()}]}]
:nav-action (act/back #(dispatch [:navigate-back]))
:custom-content [toolbar-title]}]
[linear-gradient {:locations [0 0.6 1]
:colors gradient-colors
:style toolbar-gradient}]

View File

@ -11,6 +11,7 @@
touchable-highlight]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.components.styles :refer [color-purple
color-white
icon-search
@ -61,9 +62,9 @@
:style st/gradient-background}
[status-bar {:type :transparent}]
[toolbar {:background-color :transparent
:nav-action {:image {:source (if show-back? {:uri :icon_back_white} nil)
:style icon-back}
:handler (if show-back? #(dispatch [:navigate-back]) nil)}
:nav-action (if show-back?
(act/back-white #(dispatch [:navigate-back]))
act/nothing)
:custom-content [toolbar-title]
:actions [{:image {:style icon-search}
:handler #()}]}]]

View File

@ -12,6 +12,7 @@
modal
splash-screen]]
[status-im.components.main-tabs :refer [main-tabs]]
[status-im.contacts.search-results :refer [contacts-search-results]]
[status-im.contacts.views.contact-list :refer [contact-list]]
[status-im.contacts.views.new-contact :refer [new-contact]]
[status-im.qr-scanner.screen :refer [qr-scanner]]
@ -95,6 +96,7 @@
:new-group new-group
:group-settings group-settings
:contact-list main-tabs
:contact-list-search-results contacts-search-results
:group-contacts contact-list
:new-contact new-contact
:qr-scanner qr-scanner

View File

@ -8,43 +8,36 @@
text
image
touchable-highlight]]
[status-im.utils.listview :refer [to-datasource]]
[status-im.chats-list.views.chat-list-item :refer [chat-list-item]]
[status-im.components.action-button :refer [action-button
action-button-item]]
[status-im.components.drawer.view :refer [open-drawer]]
[status-im.components.styles :refer [color-blue]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.styles :refer [toolbar-background1
toolbar-background2]]
[status-im.components.toolbar.view :refer [toolbar-with-search]]
[status-im.components.toolbar.actions :as act]
[status-im.components.icons.custom-icons :refer [ion-icon]]
[status-im.components.react :refer [linear-gradient]]
[status-im.i18n :refer [label]]
[status-im.chats-list.styles :as st]
[status-im.utils.platform :refer [platform-specific]]
[status-im.components.sync-state.offline :refer [offline-view]]
[status-im.utils.listview :refer [to-datasource]]
[status-im.chats-list.views.chat-list-item :refer [chat-list-item]]
[status-im.i18n :refer [label]]
[status-im.utils.platform :refer [platform-specific]]
[status-im.chats-list.styles :as st]
[status-im.components.tabs.styles :refer [tabs-height]]))
(defview chats-list-toolbar []
(defview toolbar-view []
[chats-scrolled? [:get :chats-scrolled?]]
(let [new-chat? (get-in platform-specific [:chats :new-chat-in-toolbar?])
actions (cond->> [{:image {:source {:uri :icon_search}
:style st/toolbar-icon}
:handler (fn [])}]
new-chat?
(into [{:image {:source {:uri :icon_add}
:style st/toolbar-icon}
:handler #(dispatch [:navigate-to :group-contacts :people])}]))]
[toolbar {:nav-action {:image {:source {:uri :icon_hamburger}
:style st/hamburger-icon}
:handler open-drawer}
:title (label :t/chats)
:style (get-in platform-specific [:component-styles :toolbar])
:background-color (if chats-scrolled?
toolbar-background1
toolbar-background2)
:actions actions}]))
actions (if new-chat?
[(act/add #(dispatch [:navigate-to :group-contacts :people]))])]
[toolbar-with-search
{:show-search? false
:search-key :chat-list
:title (label :t/chats)
:search-placeholder (label :t/search-for)
:nav-action (act/hamburger open-drawer)
:actions actions
:style (st/toolbar chats-scrolled?)}]))
(defn chats-action-button []
[view {:style (st/action-buttons-container false 0)
@ -75,7 +68,7 @@
(defview chats-list []
[chats [:get :chats]]
[view st/chats-container
[chats-list-toolbar]
[toolbar-view]
[list-view {:dataSource (to-datasource chats)
:renderRow (fn [[id :as row] _ _]
(list-item ^{:key id} [chat-list-item row]))

View File

@ -7,8 +7,16 @@
text2-color
new-messages-count-color]]
[status-im.components.tabs.styles :refer [tabs-height]]
[status-im.components.toolbar.styles :refer [toolbar-background1
toolbar-background2]]
[status-im.utils.platform :as p]))
(defn toolbar [chats-scrolled?]
(merge {:background-color (if chats-scrolled?
toolbar-background1
toolbar-background2)}
(get-in p/platform-specific [:component-styles :toolbar])))
(def gradient-top-bottom-shadow
["rgba(24, 52, 76, 0.165)"
"rgba(24, 52, 76, 0.03)"
@ -103,14 +111,6 @@
:color color-blue
:textAlign :center})
(def hamburger-icon
{:width 16
:height 12})
(def toolbar-icon
{:width 17
:height 17})
(def chats-container
{:flex 1})

View File

@ -107,8 +107,8 @@
:onScrollBeginDrag #(reset! dragging? true)
:on-momentum-scroll-end (on-scroll-end swiped? dragging?)})
[chats-list]
[discover (= @view-id :discover)]
[contact-list]]
[discover (= @view-id :discover)]
[contact-list (= @view-id :contact-list)]]
[tabs {:selected-view-id @view-id
:prev-view-id @prev-view-id
:tab-list tab-list}]

View File

@ -31,10 +31,6 @@
(def flex
{:flex 1})
(def hamburger-icon
{:width 16
:height 12})
(def icon-search
{:width 17
:height 17})

View File

@ -0,0 +1,31 @@
(ns status-im.components.toolbar.actions
(:require [status-im.components.toolbar.styles :as st]))
(def nothing
{:image {:source nil
:style st/action-search}})
(defn hamburger [handler]
{:image {:source {:uri :icon_hamburger}
:style st/action-hamburger}
:handler handler})
(defn add [handler]
{:image {:source {:uri :icon_add}
:style st/action-add}
:handler handler})
(defn search [handler]
{:image {:source {:uri :icon_search}
:style st/action-search}
:handler handler})
(defn back [handler]
{:image {:source {:uri :icon_back}
:style st/action-back}
:handler handler})
(defn back-white [handler]
{:image {:source {:uri :icon_back_white}
:style st/action-back}
:handler handler})

View File

@ -41,7 +41,7 @@
:justifyContent :center})
(def toolbar-title-text
{:margin-top -2.5
{:margin-top 0
:color text1-color
:font-size 16})
@ -57,4 +57,46 @@
:height toolbar-height
:margin-right toolbar-icon-spacing
:align-items :center
:justify-content :center})
:justify-content :center})
(def toolbar-with-search
{:background-color toolbar-background2
:elevation 0})
(def toolbar-with-search-content
{:flex 1
:align-items :center
:justify-content :center})
(def toolbar-search-input
{:flex 1
:align-self :stretch
:margin-left 18
:margin-top 2
:font-size 14
:color "#7099e6"})
(def toolbar-with-search-title
{:color "#000000de"
:align-self :center
:text-align :center
:font-size 16})
;; Specific actions
(def action-hamburger
{:width 16
:height 12})
(def action-add
{:width 17
:height 17})
(def action-search
{:width 17
:height 17})
(def action-back
{:width 8
:height 14})

View File

@ -3,10 +3,13 @@
[status-im.components.react :refer [view
icon
text
text-input
image
touchable-highlight]]
[status-im.components.sync-state.gradient :refer [sync-state-gradient-view]]
[status-im.components.styles :refer [icon-back]]
[status-im.components.styles :refer [icon-back
icon-search]]
[status-im.components.toolbar.actions :as act]
[status-im.components.toolbar.styles :as st]
[status-im.utils.platform :refer [platform-specific]]))
@ -48,3 +51,43 @@
custom-action)]]
[sync-state-gradient-view]]))
(defn- toolbar-search-submit [on-search-submit]
(let [text @(subscribe [:get-in [:toolbar-search :text]])]
(on-search-submit text)
(dispatch [:set-in [:toolbar-search :text] nil])))
(defn- toolbar-with-search-content [{:keys [show-search?
search-key
search-placeholder
title
on-search-submit]}]
[view st/toolbar-with-search-content
(if show-search?
[text-input
{:style st/toolbar-search-input
:auto-focus true
:placeholder search-placeholder
:on-change-text #(dispatch [:set-in [:toolbar-search :text] %])
:on-submit-editing #(toolbar-search-submit on-search-submit)}]
[view
[text {:style st/toolbar-with-search-title
:font :toolbar-title}
title]])])
(defn toolbar-with-search [{:keys [show-search?
search-key
nav-action
actions
style
on-search-submit]
:as opts}]
(let [toggle-search-fn #(dispatch [:set-in [:toolbar-search :show] %])
actions (if show-search?
[(act/search #(toolbar-search-submit on-search-submit))]
(into actions [(act/search #(toggle-search-fn search-key))]))]
[toolbar {:style (merge st/toolbar-with-search style)
:nav-action (if show-search?
(act/back #(toggle-search-fn nil))
nav-action)
:custom-content [toolbar-with-search-content opts]
:actions actions}]))

View File

@ -11,6 +11,7 @@
[status-im.utils.utils :refer [require]]
[status-im.navigation.handlers :as nav]
[status-im.utils.random :as random]
[status-im.i18n :refer [label]]
[taoensso.timbre :as log]
[cljs.reader :refer [read-string]]))
@ -31,9 +32,10 @@
(defmethod nav/preload-data! :contact-list
[db [_ _ click-handler]]
(assoc db :contacts-click-handler click-handler
:contacts-filter nil))
(-> db
(assoc-in [:toolbar-search :show] nil)
(assoc :contacts-click-handler click-handler
:contacts-filter nil)))
(register-handler :remove-contacts-click-handler
(fn [db]
@ -56,6 +58,13 @@
(register-handler :watch-contact (u/side-effect! watch-contact))
(defn stop-watching-contact
[{:keys [web3]} [_ {:keys [whisper-identity]}]]
(protocol/stop-watching-user! {:web3 web3
:identity whisper-identity}))
(register-handler :stop-watching-contact (u/side-effect! stop-watching-contact))
(defn send-contact-request
[{:keys [current-public-key web3 current-account-id accounts]} [_ contact]]
(let [{:keys [whisper-identity]} contact
@ -199,11 +208,13 @@
(register-handler :add-pending-contact
(u/side-effect!
(fn [{:keys [chats]} [_ chat-id]]
(let [contact (read-string (get-in chats [chat-id :contact-info]))]
(fn [{:keys [chats contacts]} [_ chat-id]]
(let [contact (if-let [contact-info (get-in chats [chat-id :contact-info])]
(read-string contact-info)
(-> (get contacts chat-id)
(assoc :pending false)))]
(dispatch [::prepare-contact contact])
(dispatch [:update-chat! {:chat-id chat-id
:contact-info nil
:pending-contact? false}])
(dispatch [:watch-contact contact])
(dispatch [:discoveries-send-portions chat-id])))))
@ -242,3 +253,22 @@
(dispatch [:update-contact! {:whisper-identity from
:last-online timestamp}]))))))
(register-handler :remove-contact
(-> (u/side-effect!
(fn [_ [_ {:keys [whisper-identity] :as contact}]]
(dispatch [:update-chat! {:chat-id whisper-identity
:pending-contact? true}])
(dispatch [:update-contact! (assoc contact :pending true)])))
((after stop-watching-contact))))
(register-handler :open-contact-menu
(u/side-effect!
(fn [_ [_ list-selection-fn {:keys [name] :as contact}]]
(list-selection-fn {:title name
:options [(label :t/remove-contact)]
:callback (fn [index]
(case index
0 (dispatch [:remove-contact contact])
:default))
:cancel-text (label :t/cancel)}))))

View File

@ -1,7 +1,8 @@
(ns status-im.contacts.screen
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[reagent.core :as r]
(:require [reagent.core :as r]
[clojure.string :as str]
[re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.components.react :refer [view
text
image
@ -12,38 +13,40 @@
list-item] :as react]
[status-im.components.action-button :refer [action-button
action-button-item]]
[status-im.contacts.views.contact :refer [contact-extended-view on-press]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.styles :refer [toolbar-background2]]
[status-im.components.toolbar.view :refer [toolbar-with-search]]
[status-im.components.toolbar.actions :as act]
[status-im.components.drawer.view :refer [open-drawer]]
[status-im.components.icons.custom-icons :refer [ion-icon]]
[status-im.components.styles :refer [color-blue
hamburger-icon
icon-search
create-icon]]
[status-im.contacts.styles :as st]
[status-im.contacts.views.contact :refer [contact-view]]
[status-im.utils.platform :refer [platform-specific]]
[status-im.i18n :refer [label]]
[status-im.utils.platform :refer [platform-specific]]))
[status-im.contacts.styles :as st]
[status-im.components.styles :refer [color-blue
create-icon
icon-search]]))
(def contacts-limit 50)
(defn toolbar-view []
(defn toolbar-view [show-search?]
(let [new-contact? (get-in platform-specific [:contacts :new-contact-in-toolbar?])
actions (cond->> [{:image {:source {:uri :icon_search}
:style icon-search}
:handler (fn [])}]
new-contact?
(into [{:image {:source {:uri :icon_add}
:style icon-search}
:handler #(dispatch [:navigate-to :new-contact])}]))]
[toolbar {:nav-action {:image {:source {:uri :icon_hamburger}
:style hamburger-icon}
:handler open-drawer}
:title (label :t/contacts)
:background-color toolbar-background2
:style {:elevation 0}
:actions actions}]))
actions (if new-contact?
[(act/add #(dispatch [:navigate-to :new-contact]))])]
(toolbar-with-search
{:show-search? show-search?
:search-key :contact-list
:title (label :t/contacts)
:search-placeholder (label :t/search-for)
:nav-action (act/hamburger open-drawer)
:actions actions
:on-search-submit (fn [text]
(when-not (str/blank? text)
(dispatch [:set :contacts-filter #(let [name (-> (or (:name %) "")
(str/lower-case))
text (str/lower-case text)]
(not= (.indexOf name text) -1))])
(dispatch [:set :contact-list-search-text text])
(dispatch [:navigate-to :contact-list-search-results])))})))
(defn subtitle-view [subtitle contacts-count]
[view st/contact-group-header-inner
@ -81,9 +84,11 @@
[view
(doall
(map (fn [contact]
(let [click-handler (or click-handler on-press)]
^{:key contact}
[contact-extended-view contact nil (click-handler contact) nil]))
^{:key contact}
[contact-view {:contact contact
:extended? true
:on-click click-handler
:more-on-click nil}])
contacts))]
(when (<= contacts-limit (count contacts))
[view st/show-all
@ -110,16 +115,18 @@
[ion-icon {:name :md-create
:style create-icon}]]]])
(defn contact-list []
(defn contact-list [_]
(let [peoples (subscribe [:get-added-people-with-limit contacts-limit])
dapps (subscribe [:get-added-dapps-with-limit contacts-limit])
people-count (subscribe [:added-people-count])
dapps-count (subscribe [:added-dapps-count])
click-handler (subscribe [:get :contacts-click-handler])
show-search (subscribe [:get-in [:toolbar-search :show]])
show-toolbar-shadow? (r/atom false)]
(fn []
(fn [current-view?]
[view st/contacts-list-container
[toolbar-view]
[toolbar-view (and current-view?
(= @show-search :contact-list))]
[view {:style st/toolbar-shadow}
(when @show-toolbar-shadow?
[linear-gradient {:style st/contact-group-header-gradient-bottom
@ -128,7 +135,8 @@
[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))))}
(reset! show-toolbar-shadow?
(<= st/contact-group-header-height offset))))}
(when (pos? @dapps-count)
[contact-group-view
@dapps

View File

@ -0,0 +1,36 @@
(ns status-im.contacts.search-results
(:require-macros [status-im.utils.views :refer [defview]])
(:require [re-frame.core :refer [subscribe dispatch]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.react :refer [view
text
icon
list-view
list-item]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.contacts.views.contact :refer [contact-view]]
[status-im.utils.listview :refer [to-datasource]]
[status-im.utils.platform :refer [platform-specific]]
[status-im.i18n :refer [label]]
[status-im.contacts.styles :as st]))
(defview contacts-search-results []
[search-text [:get :contact-list-search-text]
contacts [:contacts-with-letters]]
[view st/search-container
[status-bar]
[toolbar {:nav-action (act/back #(dispatch [:navigate-back]))
:title search-text
:style (get-in platform-specific [:component-styles :toolbar])}]
(if (empty? contacts)
[view st/search-empty-view
;; todo change icon
[icon :group_big st/empty-contacts-icon]
[text {:style st/empty-contacts-text}
"No contacts found"]]
[list-view {:dataSource (to-datasource contacts)
:renderRow (fn [row _ _]
(list-item [contact-view {:contact row
:letter? true
:extended? true}]))}])])

View File

@ -10,17 +10,19 @@
color-gray2]]
[status-im.components.toolbar.styles :refer [toolbar-background2]]))
(def contacts-list-container
{:flex 1})
;; Contacts list
(def toolbar-shadow
{:height 2
:backgroundColor toolbar-background2})
{:height 2
:background-color toolbar-background2})
(def contact-groups
{:flex 1
:background-color toolbar-background2})
(def contacts-list-container
{:flex 1})
(def empty-contact-groups
(merge contact-groups
{:align-items :center
@ -170,7 +172,7 @@
{:width 4
:height 16})
; new contact
; New contact
(def contact-form-container
{:flex 1
@ -221,4 +223,18 @@
:margin-right 20
:margin-top 18
:width 20
:height 20})
:height 20})
;; Contacts search
(def search-container
{:flex 1
:background-color color-white})
(def search-empty-view
{:flex 1
:background-color color-white
:align-items :center
:justify-content :center})

View File

@ -1,6 +1,7 @@
(ns status-im.contacts.subs
(:require-macros [reagent.ratom :refer [reaction]])
(:require [re-frame.core :refer [register-sub subscribe]]
[clojure.string :as str]
[status-im.utils.identicon :refer [identicon]]
[taoensso.timbre :as log]))
@ -34,7 +35,6 @@
(let [contacts (subscribe [:all-added-contacts])]
(reaction (filter :dapp? @contacts)))))
(register-sub :get-added-people-with-limit
(fn [_ [_ limit]]
(let [contacts (subscribe [:all-added-people])]

View File

@ -7,8 +7,11 @@
(defn is-address? [s]
(.isAddress web3.prototype s))
(defn unique-identity? [identity]
(not (contacts/exists? identity)))
(defn contact-can-be-added? [identity]
(if (contacts/exists? identity)
(-> (contacts/get-by-id identity)
(get :pending))
true))
(defn valid-length? [identity]
(let [length (count identity)]
@ -18,11 +21,11 @@
(is-address? identity))))
(s/def ::identity-length valid-length?)
(s/def ::unique-identity unique-identity?)
(s/def ::contact-can-be-added contact-can-be-added?)
(s/def ::not-empty-string (s/and string? not-empty))
(s/def ::name ::not-empty-string)
(s/def ::whisper-identity (s/and ::not-empty-string
::unique-identity
::contact-can-be-added
::identity-length))
(s/def ::contact (s/keys :req-un [::name ::whisper-identity]

View File

@ -3,37 +3,31 @@
(:require [status-im.components.react :refer [view text icon touchable-highlight]]
[re-frame.core :refer [dispatch]]
[status-im.contacts.styles :as st]
[status-im.contacts.views.contact-inner :refer [contact-inner-view]]))
[status-im.contacts.views.contact-inner :refer [contact-inner-view]]
[status-im.utils.platform :refer [platform-specific]]))
(defn on-press [{:keys [whisper-identity]}]
#(dispatch [:start-chat whisper-identity {} :navigation-replace]))
(defn- on-press [{:keys [whisper-identity]}]
(dispatch [:start-chat whisper-identity {} :navigation-replace]))
(defn- more-on-press [contact]
(dispatch [:open-contact-menu (:list-selection-fn platform-specific) contact]))
(defn letter-view [letter]
[view st/letter-container
(when letter
[text {:style st/letter-text} letter])])
(defview contact-view-with-letter [{:keys [whisper-identity letter] :as contact} click-handler action params]
[touchable-highlight
{:onPress #(click-handler contact action params)}
[view st/contact-container
[letter-view letter]
[contact-inner-view contact]]])
(defview contact-view [{:keys [whisper-identity] :as contact}]
(defview contact-view [{{:keys [whisper-identity letter dapp?] :as contact} :contact
:keys [extended? letter? on-click more-on-click info]}]
[chat [:get-chat whisper-identity]]
[touchable-highlight
{:onPress (on-press contact)}
[view st/contact-container
[contact-inner-view contact]]])
(defview contact-extended-view [{:keys [whisper-identity] :as contact} info click-handler more-click-handler]
[chat [:get-chat whisper-identity]]
[touchable-highlight
{:onPress click-handler}
{:on-press #((or on-click on-press) contact)}
[view st/contact-container
(when letter?
[letter-view letter])
[contact-inner-view contact info]
[touchable-highlight
{:on-press more-click-handler}
[view st/more-btn
[icon :more_vertical st/more-btn-icon]]]]])
(when (and extended? (not dapp?))
[touchable-highlight
{:on-press #((or more-on-click more-on-press) contact)}
[view st/more-btn
[icon :more_vertical st/more-btn-icon]]])]])

View File

@ -6,12 +6,11 @@
touchable-highlight
list-view
list-item]]
[status-im.contacts.views.contact :refer [contact-view
on-press
contact-view-with-letter]]
[status-im.contacts.views.contact :refer [contact-view]]
[status-im.components.text-field.view :refer [text-field]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.components.toolbar.styles :refer [toolbar-background1]]
[status-im.components.drawer.view :refer [drawer-view open-drawer]]
[status-im.components.styles :refer [icon-search
@ -42,11 +41,10 @@
(defn render-row [chat-modal click-handler action params]
(fn [row _ _]
(list-item
(if chat-modal
[contact-view-with-letter row click-handler action params]
[contact-view row
(or click-handler
(on-press row))]))))
[contact-view {:contact row
:letter? chat-modal
:on-click (if click-handler
#(click-handler row action params))}])))
(defn contact-list-entry [{:keys [click-handler icon icon-style label]}]
[touchable-highlight
@ -71,14 +69,10 @@
:t/contacts-group-dapps
:t/contacts-group-new-chat)))
:nav-action (when modal
{:handler #(dispatch [:navigate-back])
:image {:source {:uri :icon_back}
:style icon-back}})
(act/back #(dispatch [:navigate-back])))
:background-color toolbar-background1
:style (get-in platform-specific [:component-styles :toolbar])
:actions [{:image {:source {:uri :icon_search}
:style icon-search}
:handler (fn [])}]}]])
:actions [(act/search #())]}]])
(defview contact-list []
[contacts [:contacts-with-letters]

View File

@ -11,6 +11,7 @@
[status-im.utils.identicon :refer [identicon]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.components.toolbar.styles :refer [toolbar-title-container
toolbar-title-text
toolbar-background1]]
@ -24,6 +25,7 @@
[cljs.spec :as s]
[status-im.contacts.validations :as v]
[status-im.contacts.styles :as st]
[status-im.data-store.contacts :as contacts]
[status-im.utils.gfycat.core :refer [generate-gfy]]
[status-im.utils.hex :refer [normalize-hex]]
[status-im.utils.platform :refer [platform-specific]]))
@ -44,11 +46,15 @@
:address id
:photo-path (identicon whisper-identity)
:whisper-identity whisper-identity}]
(dispatch [:add-new-contact contact]))
(if (contacts/exists? whisper-identity)
(dispatch [:add-pending-contact whisper-identity])
(dispatch [:add-new-contact contact])))
(dispatch [:set :new-contact-public-key-error (label :t/unknown-address)]))))
(dispatch [:add-new-contact {:name (generate-gfy)
:photo-path (identicon id)
:whisper-identity id}])))
(if (contacts/exists? id)
(dispatch [:add-pending-contact id])
(dispatch [:add-new-contact {:name (generate-gfy)
:photo-path (identicon id)
:whisper-identity id}]))))
(defn- validation-error-message
[whisper-identity {:keys [address public-key]} error]
@ -57,7 +63,7 @@
(normalize-hex whisper-identity))
(label :t/can-not-add-yourself)
(not (s/valid? ::v/unique-identity whisper-identity))
(not (s/valid? ::v/contact-can-be-added whisper-identity))
(label :t/contact-already-added)
(not (s/valid? ::v/whisper-identity whisper-identity))
@ -103,9 +109,7 @@
[status-bar]
[toolbar {:background-color toolbar-background1
:style (get-in platform-specific [:component-styles :toolbar])
:nav-action {:image {:source {:uri :icon_back}
:style icon-back}
:handler #(dispatch [:navigate-back])}
:nav-action (act/back #(dispatch [:navigate-back]))
:title (label :t/add-new-contact)
:actions (toolbar-actions new-contact-identity account error)}]
[view st/form-container

View File

@ -9,26 +9,25 @@
(defn get-by-id
[whisper-identity]
(data-store/get-by-id whisper-identity))
(data-store/get-by-id-cljs whisper-identity))
(defn save
[{:keys [whisper-identity pending] :as contact}]
(let [{pending-db :pending
:as contact-db} (data-store/get-by-id whisper-identity)
contact (assoc contact :pending (boolean (if contact-db
;; TODO:
;; this is temporary fix for pending users
;; we need to change this (if ...) to (and pending-db pending)
(if (nil? pending)
pending-db
(and pending-db pending))
pending)))]
contact (assoc contact :pending
(boolean (if contact-db
(if (nil? pending) pending-db pending)
pending)))]
(data-store/save contact (if contact-db true false))))
(defn save-all
[contacts]
(mapv save contacts))
(defn delete [contact]
(data-store/delete contact))
(defn exists?
[whisper-identity]
(data-store/exists? whisper-identity))
(data-store/exists? whisper-identity))

View File

@ -0,0 +1,15 @@
(ns status-im.data-store.processed-messages
(:require [status-im.data-store.realm.processed-messages :as data-store]
[taoensso.timbre :as log])
(:refer-clojure :exclude [exists?]))
(defn get-filtered
[condition]
(data-store/get-filtered-as-list condition))
(defn save
[processed-message]
(data-store/save processed-message))
(defn delete [condition]
(data-store/delete condition))

View File

@ -14,12 +14,21 @@
(defn get-by-id
[whisper-identity]
(realm/get-one-by-field-clj @realm/account-realm :contact :whisper-identity whisper-identity))
(realm/get-one-by-field @realm/account-realm :contact :whisper-identity whisper-identity))
(defn get-by-id-cljs
[whisper-identity]
(some-> (get-by-id whisper-identity)
(js->clj :keywordize-keys true)))
(defn save
[contact update?]
(realm/save @realm/account-realm :contact contact update?))
(defn delete
[{:keys [whisper-identity]}]
(realm/delete @realm/account-realm (get-by-id whisper-identity)))
(defn exists?
[whisper-identity]
(realm/exists? @realm/account-realm :contact {:whisper-identity whisper-identity}))
(realm/exists? @realm/account-realm :contact {:whisper-identity whisper-identity}))

View File

@ -0,0 +1,25 @@
(ns status-im.data-store.realm.processed-messages
(:require [status-im.data-store.realm.core :as realm])
(:refer-clojure :exclude [exists?]))
(defn get-all
[]
(-> (realm/get-all @realm/account-realm :processed-message)
(realm/sorted :ttl :asc)))
(defn get-filtered
[condition]
(realm/filtered (get-all) condition))
(defn get-filtered-as-list
[condition]
(-> (get-filtered condition)
realm/realm-collection->list))
(defn save
[processed-message]
(realm/save @realm/account-realm :processed-message processed-message))
(defn delete
[condition]
(realm/delete @realm/account-realm (get-filtered condition)))

View File

@ -7,6 +7,7 @@
[status-im.data-store.realm.schemas.account.v1.kv-store :as kv-store]
[status-im.data-store.realm.schemas.account.v1.message :as message]
[status-im.data-store.realm.schemas.account.v1.pending-message :as pending-message]
[status-im.data-store.realm.schemas.account.v1.processed-message :as processed-message]
[status-im.data-store.realm.schemas.account.v1.request :as request]
[status-im.data-store.realm.schemas.account.v1.tag :as tag]
[status-im.data-store.realm.schemas.account.v1.user-status :as user-status]
@ -20,6 +21,7 @@
kv-store/schema
message/schema
pending-message/schema
processed-message/schema
request/schema
tag/schema
user-status/schema])
@ -34,6 +36,7 @@
(kv-store/migration old-realm new-realm)
(message/migration old-realm new-realm)
(pending-message/migration old-realm new-realm)
(processed-message/migration old-realm new-realm)
(request/migration old-realm new-realm)
(tag/migration old-realm new-realm)
(user-status/migration old-realm new-realm))
(user-status/migration old-realm new-realm))

View File

@ -0,0 +1,13 @@
(ns status-im.data-store.realm.schemas.account.v1.processed-message
(:require [taoensso.timbre :as log]))
(def schema {:name :processed-message
:primaryKey :id
:properties {:id :string
:message-id :string
:type {:type "string"
:optional true}
:ttl :int}})
(defn migration [old-realm new-realm]
(log/debug "migrating processed-message schema"))

View File

@ -25,8 +25,8 @@
(defmethod nav/preload-data! :discover
[db _]
(dispatch [:set :discover-show-search? false])
(-> db
(assoc-in [:toolbar-search :show] nil)
(assoc :tags (discoveries/get-all-tags))
(assoc :discoveries (->> (discoveries/get-all :desc)
(map (fn [{:keys [message-id] :as discover}]

View File

@ -1,6 +1,7 @@
(ns status-im.discover.screen
(:require-macros [status-im.utils.views :refer [defview]])
(:require
[reagent.core :as r]
[re-frame.core :refer [dispatch subscribe]]
[clojure.string :as str]
[status-im.components.react :refer [view
@ -8,52 +9,33 @@
text
text-input
icon]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.view :refer [toolbar-with-search]]
[status-im.components.toolbar.actions :as act]
[status-im.components.drawer.view :refer [open-drawer]]
[status-im.discover.styles :as st]
[status-im.i18n :refer [label]]
[status-im.components.carousel.carousel :refer [carousel]]
[status-im.discover.views.popular-list :refer [discover-popular-list]]
[status-im.discover.views.discover-list-item :refer [discover-list-item]]
[status-im.contacts.styles :as contacts-styles]
[status-im.utils.platform :refer [platform-specific]]
[reagent.core :as r]))
[status-im.i18n :refer [label]]
[status-im.discover.styles :as st]
[status-im.contacts.styles :as contacts-st]))
(defn get-hashtags [status]
(let [hashtags (map #(str/lower-case (str/replace % #"#" "")) (re-seq #"[^ !?,;:.]+" status))]
(or hashtags [])))
(defn title-content [show-search?]
[view st/discover-toolbar-content
(if show-search?
[text-input {:style st/discover-search-input
:auto-focus true
:placeholder (label :t/search-tags)
:on-blur (fn [e]
(dispatch [:set :discover-show-search? false]))
:on-submit-editing (fn [e]
(let [search (aget e "nativeEvent" "text")
hashtags (get-hashtags search)]
(dispatch [:set :discover-search-tags hashtags])
(dispatch [:navigate-to :discover-search-results])))}]
[view
[text {:style st/discover-title
:font :toolbar-title}
(label :t/discover)]])])
(defn toogle-search [current-value]
(dispatch [:set :discover-show-search? (not current-value)]))
(defn discover-toolbar [show-search?]
[toolbar
{:style st/discover-toolbar
:nav-action {:image {:source {:uri :icon_hamburger}
:style st/hamburger-icon}
:handler open-drawer}
:custom-content [title-content show-search?]
:actions [{:image {:source {:uri :icon_search}
:style st/search-icon}
:handler #(toogle-search show-search?)}]}])
(defn toolbar-view [show-search?]
[toolbar-with-search
{:show-search? show-search?
:search-key :discover
:title (label :t/discover)
:search-placeholder (label :t/search-tags)
:nav-action (act/hamburger open-drawer)
:on-search-submit (fn [text]
(when-not (str/blank? text)
(let [hashtags (get-hashtags text)]
(dispatch [:set :discover-search-tags hashtags])
(dispatch [:navigate-to :discover-search-results]))))}])
(defn title [label-kw spacing?]
[view st/section-spacing
@ -91,19 +73,20 @@
:current-account current-account}]))]]))
(defview discover [current-view?]
[show-search? [:get :discover-show-search?]
[show-search [:get-in [:toolbar-search :show]]
contacts [:get :contacts]
current-account [:get-current-account]
discoveries [:get-recent-discoveries]]
[view st/discover-container
[discover-toolbar (and current-view? show-search?)]
[toolbar-view (and current-view?
(= show-search :discover))]
(if discoveries
[scroll-view st/scroll-view-container
[discover-popular {:contacts contacts
:current-account current-account}]
[discover-recent {:current-account current-account}]]
[view contacts-styles/empty-contact-groups
[view contacts-st/empty-contact-groups
;; todo change icon
[icon :group_big contacts-styles/empty-contacts-icon]
[text {:style contacts-styles/empty-contacts-text}
[icon :group_big contacts-st/empty-contacts-icon]
[text {:style contacts-st/empty-contacts-text}
(label :t/no-statuses-discovered)]])])

View File

@ -9,12 +9,13 @@
list-view
list-item
scroll-view]]
[status-im.i18n :refer [label]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.discover.views.discover-list-item :refer [discover-list-item]]
[status-im.discover.styles :as st]
[status-im.utils.platform :refer [platform-specific]]
[status-im.contacts.styles :as contacts-styles]
[status-im.i18n :refer [label]]
[status-im.discover.styles :as st]
[status-im.contacts.styles :as contacts-st]
[taoensso.timbre :as log]))
(defn render-separator [_ row-id _]
@ -43,20 +44,19 @@
datasource (to-datasource discoveries)]
[view st/discover-tag-container
[status-bar]
[toolbar {:nav-action {:image {:source {:uri :icon_back}
:style st/icon-back}
:handler #(dispatch [:navigate-back])}
[toolbar {:nav-action (act/back #(dispatch [:navigate-back]))
:custom-content (title-content tags)
:style st/discover-tag-toolbar}]
(if (empty? discoveries)
[view st/empty-view
;; todo change icon
[icon :group_big contacts-styles/empty-contacts-icon]
[text {:style contacts-styles/empty-contacts-text}
[icon :group_big contacts-st/empty-contacts-icon]
[text {:style contacts-st/empty-contacts-text}
(label :t/no-statuses-found)]]
[list-view {:dataSource datasource
:renderRow (fn [row _ _]
(list-item [discover-list-item {:message row
:current-account current-account}]))
(list-item [discover-list-item
{:message row
:current-account current-account}]))
:renderSeparator render-separator
:style st/recent-list}])]))

View File

@ -1,9 +1,10 @@
(ns status-im.discover.styles
(:require [status-im.components.styles :refer [color-gray2
color-white]]
color-white
color-light-gray]]
[status-im.components.toolbar.styles :refer [toolbar-background2]]))
;; common
;; Common
(def row-separator
{:border-bottom-width 1
@ -22,30 +23,6 @@
:align-items :center
:justify-content :center})
;; Toolbar
(def discover-toolbar-content
{:flex 1
:align-items :center
:justify-content :center})
(def discover-toolbar
{:background-color toolbar-background2
:elevation 0})
(def discover-search-input
{:flex 1
:align-self "stretch"
:margin-left 18
:font-size 14
:color "#7099e6"})
(def discover-title
{:color "#000000de"
:align-self :center
:text-align :center
:font-size 16})
(def section-spacing
{:padding 16})
@ -134,7 +111,7 @@
(def discover-tag-container
{:flex 1
:backgroundColor "#eef2f5"})
:backgroundColor color-light-gray})
(def tag-title-scroll
{:flex 1
@ -165,10 +142,6 @@
{:flex 1
:backgroundColor color-white})
(def hamburger-icon
{:width 16
:height 12})
(def search-icon
{:width 17
:height 17})

View File

@ -1,9 +1,12 @@
(ns status-im.group-settings.views.member
(:require [re-frame.core :refer [subscribe dispatch dispatch-sync]]
[status-im.contacts.views.contact :refer [contact-extended-view]]
[status-im.contacts.views.contact :refer [contact-view]]
[status-im.i18n :refer [label]]))
(defn member-view [{:keys [whisper-identity role] :as contact}]
;; TODO implement :role property for group chat contact
[contact-extended-view contact role
#(dispatch [:set :selected-participants #{whisper-identity}])])
[contact-view
{:contact contact
:extended? true
:info role
:on-click #(dispatch [:set :selected-participants #{whisper-identity}])}])

View File

@ -68,6 +68,7 @@
(u/side-effect!
(fn [_ [_ address]]
(dispatch [:initialize-account-db])
(dispatch [:load-processed-messages])
(dispatch [:initialize-protocol address])
(dispatch [:initialize-sync-listener])
(dispatch [:initialize-chats])

View File

@ -10,6 +10,7 @@
orientation
splash-screen]]
[status-im.components.main-tabs :refer [main-tabs]]
[status-im.contacts.search-results :refer [contacts-search-results]]
[status-im.contacts.views.contact-list :refer [contact-list]]
[status-im.contacts.views.new-contact :refer [new-contact]]
[status-im.qr-scanner.screen :refer [qr-scanner]]
@ -82,6 +83,7 @@
:new-group new-group
:group-settings group-settings
:contact-list main-tabs
:contact-list-search-results contacts-search-results
:group-contacts contact-list
:new-contact new-contact
:qr-scanner qr-scanner

View File

@ -12,6 +12,7 @@
[status-im.components.icons.custom-icons :refer [ion-icon]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.components.toolbar.styles :refer [toolbar-background1]]
[status-im.utils.image-processing :refer [img->base64]]
[status-im.profile.photo-capture.styles :as st]
@ -35,9 +36,7 @@
[view st/container
[status-bar]
[toolbar {:title (label :t/image-source-title)
:nav-action {:image {:source {:uri :icon_back}
:style icon-back}
:handler #(dispatch [:navigate-back])}
:nav-action (act/back #(dispatch [:navigate-back]))
:background-color toolbar-background1}]
[camera {:style {:flex 1}
:aspect (:fill aspects)
@ -54,4 +53,4 @@
(.catch #(log/debug "Error capturing image: " %)))))}
[view
[ion-icon {:name :md-camera
:style {:font-size 36}}]]]]]))
:style {:font-size 36}}]]]]]))

View File

@ -35,6 +35,7 @@
;; discoveries
(def watch-user! discoveries/watch-user!)
(def stop-watching-user! discoveries/stop-watching-user!)
(def contact-request! discoveries/contact-request!)
(def broadcast-profile! discoveries/broadcast-profile!)
(def send-status! discoveries/send-status!)

View File

@ -46,6 +46,13 @@
:topics [(make-discover-topic identity)]}
(l/message-listener (dissoc options :identity))))
(defn stop-watching-user!
[{:keys [web3 identity] :as options}]
(f/remove-filter!
web3
{:from identity
:topics [(make-discover-topic identity)]}))
(s/def :contact-request/contact map?)
(s/def :contact-request/payload

View File

@ -5,12 +5,15 @@
[status-im.data-store.contacts :as contacts]
[status-im.data-store.messages :as messages]
[status-im.data-store.pending-messages :as pending-messages]
[status-im.data-store.processed-messages :as processed-messages]
[status-im.data-store.chats :as chats]
[status-im.protocol.core :as protocol]
[status-im.constants :refer [text-content-type
blocks-per-hour]]
[status-im.i18n :refer [label]]
[status-im.utils.random :as random]
[status-im.protocol.message-cache :as cache]
[status-im.utils.datetime :as dt]
[taoensso.timbre :as log :refer-macros [debug]]
[status-im.constants :as c]
[status-im.components.status :as status]))
@ -77,33 +80,41 @@
(register-handler :incoming-message
(u/side-effect!
(fn [_ [_ type {:keys [payload] :as message}]]
(debug :incoming-message type)
(case type
:message (dispatch [:received-protocol-message! message])
:group-message (dispatch [:received-protocol-message! message])
:ack (if (#{:message :group-message} (:type payload))
(dispatch [:message-delivered message])
(dispatch [:pending-message-remove message]))
:seen (dispatch [:message-seen message])
:group-invitation (dispatch [:group-chat-invite-received message])
:update-group (dispatch [:update-group-message message])
:add-group-identity (dispatch [:participant-invited-to-group message])
:remove-group-identity (dispatch [:participant-removed-from-group message])
:leave-group (dispatch [:participant-left-group message])
:contact-request (dispatch [:contact-request-received message])
:discover (dispatch [:status-received message])
:discoveries-request (dispatch [:discoveries-request-received message])
:discoveries-response (dispatch [:discoveries-response-received message])
:profile (dispatch [:contact-update-received message])
:online (dispatch [:contact-online-received message])
:pending (dispatch [:pending-message-upsert message])
:sent (let [{:keys [to id group-id]} message
message' {:from to
:payload {:message-id id
:group-id group-id}}]
(dispatch [:message-sent message']))
(debug "Unknown message type" type)))))
(fn [_ [_ type {:keys [payload ttl id] :as message}]]
(let [message-id (or id (:message-id payload))]
(when-not (cache/exists? message-id type)
(let [ttl-s (* 1000 (or ttl 120))
processed-message {:id (random/id)
:message-id message-id
:type type
:ttl (+ (dt/now-ms) ttl-s)}]
(cache/add! processed-message)
(processed-messages/save processed-message))
(case type
:message (dispatch [:received-protocol-message! message])
:group-message (dispatch [:received-protocol-message! message])
:ack (if (#{:message :group-message} (:type payload))
(dispatch [:message-delivered message])
(dispatch [:pending-message-remove message]))
:seen (dispatch [:message-seen message])
:group-invitation (dispatch [:group-chat-invite-received message])
:update-group (dispatch [:update-group-message message])
:add-group-identity (dispatch [:participant-invited-to-group message])
:remove-group-identity (dispatch [:participant-removed-from-group message])
:leave-group (dispatch [:participant-left-group message])
:contact-request (dispatch [:contact-request-received message])
:discover (dispatch [:status-received message])
:discoveries-request (dispatch [:discoveries-request-received message])
:discoveries-response (dispatch [:discoveries-response-received message])
:profile (dispatch [:contact-update-received message])
:online (dispatch [:contact-online-received message])
:pending (dispatch [:pending-message-upsert message])
:sent (let [{:keys [to id group-id]} message
message' {:from to
:payload {:message-id id
:group-id group-id}}]
(dispatch [:message-sent message']))
(debug "Unknown message type" type)))))))
(defn system-message
([message-id timestamp content]

View File

@ -0,0 +1,21 @@
(ns status-im.protocol.message-cache)
(defonce messages-set (atom #{}))
(defonce messages-map (atom {}))
(defn init!
[messages]
(reset! messages-set (into #{} messages))
(reset! messages-map (->> messages
(map (fn [{:keys [message-id type] :as message}]
[[message-id type] message]))
(into {}))))
(defn add!
[{:keys [message-id type] :as message}]
(swap! messages-set conj message)
(swap! messages-map conj [[message-id type] message]))
(defn exists?
[message-id type]
(get @messages-map [message-id type]))

View File

@ -28,7 +28,8 @@
(assoc :content content')
prn-str
u/from-utf8)]
(-> message (select-keys [:from :to :topics :ttl])
(-> message
(select-keys [:from :to :topics :ttl])
(assoc :payload payload'))))
(s/def :shh/pending-message
@ -110,7 +111,7 @@
(let [message (get-in messages [id to])
message' (when message
(assoc message :was-sent? true
:attemps 1))]
:attempts 1))]
(if message'
(assoc-in messages [id to] message')
messages))))]

View File

@ -8,6 +8,7 @@
icon-back]]
[status-im.components.status-bar :refer [status-bar]]
[status-im.components.toolbar.view :refer [toolbar]]
[status-im.components.toolbar.actions :as act]
[status-im.components.toolbar.styles :refer [toolbar-background1]]
[status-im.qr-scanner.styles :as st]
[status-im.utils.types :refer [json->clj]]
@ -20,12 +21,7 @@
[toolbar {:title title
:background-color toolbar-background1
:nav-action (when modal
{:handler #(dispatch [:navigate-back])
:image {:source {:uri :icon_back}
:style icon-back}})
:actions [{:image {:source {:uri :icon_lock_white}
:style icon-search}
:handler #()}]}]])
(act/back #(dispatch [:navigate-back])))}]])
(defview qr-scanner []
[identifier [:get :current-qr-context]]

View File

@ -8,6 +8,8 @@
:chat-name "Chat name"
:notifications-title "Notifications and sounds"
:offline "Offline"
:search-for "Search for..."
:cancel "Cancel"
;drawer
:invite-friends "Invite friends"
@ -108,7 +110,6 @@
:new-group-chat "New group chat"
;discover
:discover "Discover"
:none "None"
:search-tags "Type your search tags here"
@ -123,6 +124,7 @@
;contacts
:contacts "Contacts"
:new-contact "New Contact"
:remove-contact "Remove contact"
:show-all "SHOW ALL"
:contacts-group-dapps "ÐApps"
:contacts-group-people "People"