refactored new-group events using fx and cofx
reorganized modules structure, renamed files, improved requirements
This commit is contained in:
parent
d9800fe9a7
commit
b429076cad
|
@ -28,17 +28,16 @@
|
|||
[status-im.transactions.screens.unsigned-transactions :refer [unsigned-transactions]]
|
||||
[status-im.transactions.screens.transaction-details :refer [transaction-details]]
|
||||
[status-im.chats-list.screen :refer [chats-list]]
|
||||
[status-im.new-chat.screen :refer [new-chat]]
|
||||
[status-im.new-group.screen-public :refer [new-public-group]]
|
||||
[status-im.new-group.screen-private :refer [new-group
|
||||
edit-group]]
|
||||
[status-im.new-group.views.chat-group-settings :refer [chat-group-settings]]
|
||||
[status-im.new-group.views.contact-list :refer [edit-group-contact-list
|
||||
[status-im.chat.new-chat.view :refer [new-chat]]
|
||||
[status-im.chat.new-public-chat.view :refer [new-public-chat]]
|
||||
[status-im.group.views :refer [new-group edit-contact-group]]
|
||||
[status-im.group.chat-settings.views :refer [chat-group-settings]]
|
||||
[status-im.group.edit-contacts.views :refer [edit-contact-group-contact-list
|
||||
edit-chat-group-contact-list]]
|
||||
[status-im.new-group.views.contact-toggle-list :refer [contact-toggle-list
|
||||
[status-im.group.add-contacts.views :refer [contact-toggle-list
|
||||
add-contacts-toggle-list
|
||||
add-participants-toggle-list]]
|
||||
[status-im.new-group.views.reorder-groups :refer [reorder-groups]]
|
||||
[status-im.group.reorder.views :refer [reorder-groups]]
|
||||
[status-im.profile.screen :refer [profile my-profile]]
|
||||
[status-im.profile.edit.screen :refer [edit-my-profile]]
|
||||
[status-im.profile.photo-capture.screen :refer [profile-photo-capture]]
|
||||
|
@ -127,13 +126,13 @@
|
|||
:chat-list main-tabs
|
||||
:new-chat new-chat
|
||||
:new-group new-group
|
||||
:edit-group edit-group
|
||||
:edit-contact-group edit-contact-group
|
||||
:chat-group-settings chat-group-settings
|
||||
:add-contacts-toggle-list add-contacts-toggle-list
|
||||
:add-participants-toggle-list add-participants-toggle-list
|
||||
:edit-group-contact-list edit-group-contact-list
|
||||
:edit-group-contact-list edit-contact-group-contact-list
|
||||
:edit-chat-group-contact-list edit-chat-group-contact-list
|
||||
:new-public-group new-public-group
|
||||
:new-public-chat new-public-chat
|
||||
:contact-list main-tabs
|
||||
:contact-toggle-list contact-toggle-list
|
||||
:group-contacts contact-list
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
(ns status-im.chat.handlers
|
||||
(:require-macros [cljs.core.async.macros :as am])
|
||||
(:require [re-frame.core :refer [enrich after debug dispatch]]
|
||||
(:require [re-frame.core :refer [enrich after debug dispatch reg-fx]]
|
||||
[status-im.models.commands :as commands]
|
||||
[clojure.string :as string]
|
||||
[status-im.components.styles :refer [default-chat-color]]
|
||||
|
@ -20,7 +20,7 @@
|
|||
[status-im.utils.random :as random]
|
||||
[status-im.chat.sign-up :as sign-up-service]
|
||||
[status-im.navigation.handlers :as nav]
|
||||
[status-im.utils.handlers :refer [register-handler] :as u]
|
||||
[status-im.utils.handlers :refer [register-handler register-handler-fx] :as u]
|
||||
[status-im.handlers.server :as server]
|
||||
[status-im.utils.phone-number :refer [format-phone-number
|
||||
valid-mobile-number?]]
|
||||
|
@ -389,6 +389,11 @@
|
|||
[db [_ chat-id]]
|
||||
(update db :chats dissoc chat-id))
|
||||
|
||||
(reg-fx
|
||||
::delete-messages
|
||||
(fn [id]
|
||||
(messages/delete-by-chat-id id)))
|
||||
|
||||
(defn delete-messages!
|
||||
[{:keys [current-chat-id]} [_ chat-id]]
|
||||
(let [id (or chat-id current-chat-id)]
|
||||
|
@ -530,3 +535,38 @@
|
|||
(if (= network-status :offline)
|
||||
(chats/inc-message-overhead chat-id)
|
||||
(chats/reset-message-overhead chat-id)))))
|
||||
|
||||
(reg-fx
|
||||
::save-public-chat
|
||||
(fn [chat]
|
||||
(chats/save chat)))
|
||||
|
||||
(reg-fx
|
||||
::start-watching-group
|
||||
(fn [{:keys [group-id web3 current-public-key keypair]}]
|
||||
(protocol/start-watching-group!
|
||||
{:web3 web3
|
||||
:group-id group-id
|
||||
:identity current-public-key
|
||||
:keypair keypair
|
||||
:callback #(dispatch [:incoming-message %1 %2])})))
|
||||
|
||||
(register-handler-fx
|
||||
:create-new-public-chat
|
||||
(fn [{:keys [db]} [_ topic]]
|
||||
(let [exists? (boolean (get-in db [:chats topic]))
|
||||
chat {:chat-id topic
|
||||
:name topic
|
||||
:color default-chat-color
|
||||
:group-chat true
|
||||
:public? true
|
||||
:is-active true
|
||||
:timestamp (random/timestamp)}]
|
||||
(merge
|
||||
(when-not exists?
|
||||
{:db (assoc-in db [:chats (:chat-id chat)] chat)
|
||||
::save-public-chat chat
|
||||
::start-watching-group (merge {:group-id topic}
|
||||
(select-keys db [:web3 :current-public-key]))})
|
||||
{:dispatch-n [[:navigate-to-clean :chat-list]
|
||||
[:navigate-to :chat topic]]}))))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
(ns status-im.new-chat.styles
|
||||
(ns status-im.chat.new-chat.styles
|
||||
(:require [status-im.components.styles :as common]))
|
||||
|
||||
(def contacts-list-container
|
|
@ -0,0 +1,73 @@
|
|||
(ns status-im.chat.new-chat.view
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.action-button.action-button :refer [action-button
|
||||
action-separator]]
|
||||
[status-im.components.action-button.styles :refer [actions-list]]
|
||||
[status-im.components.react :refer [view text list-view list-item]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search]]
|
||||
[status-im.components.drawer.view :refer [drawer-view]]
|
||||
[status-im.chat.new-chat.styles :as styles]
|
||||
[status-im.utils.listview :as lw]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(defn options-list []
|
||||
[view actions-list
|
||||
[action-button (label :t/new-group-chat)
|
||||
:private_group_big
|
||||
#(dispatch [:open-contact-toggle-list :chat-group])]
|
||||
[action-separator]
|
||||
[action-button (label :t/new-public-group-chat)
|
||||
:public_group_big
|
||||
#(dispatch [:navigate-to :new-public-chat])]
|
||||
[action-separator]
|
||||
[action-button (label :t/add-new-contact)
|
||||
:add_blue
|
||||
#(dispatch [:navigate-to :new-contact])]])
|
||||
|
||||
(defn contact-list-row []
|
||||
(fn [row _ _]
|
||||
(list-item ^{:key row}
|
||||
[contact-view {:contact row
|
||||
:on-press #(dispatch [:open-chat-with-contact %])}])))
|
||||
|
||||
(defview new-chat-toolbar []
|
||||
(letsubs [show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
[view
|
||||
[status-bar]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-list
|
||||
:title (label :t/contacts-group-new-chat)
|
||||
:search-placeholder (label :t/search-for)})]))
|
||||
|
||||
(defview new-chat []
|
||||
(letsubs [contacts [:all-added-group-contacts-filtered]
|
||||
params [:get :contacts/click-params]]
|
||||
[drawer-view
|
||||
[view styles/contacts-list-container
|
||||
[new-chat-toolbar]
|
||||
(when contacts
|
||||
[list-view {:dataSource (lw/to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow (contact-list-row)
|
||||
:bounces false
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderHeader #(list-item
|
||||
[view
|
||||
[options-list]
|
||||
[common/bottom-shadow]
|
||||
[common/form-title (label :t/choose-from-contacts)
|
||||
{:count-value (count contacts)}]
|
||||
[common/list-header]])
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:renderFooter #(list-item [view
|
||||
[common/list-footer]
|
||||
[common/bottom-shadow]])
|
||||
:style styles/contacts-list}])]]))
|
|
@ -0,0 +1,20 @@
|
|||
(ns status-im.chat.new-public-chat.db
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[status-im.constants :refer [console-chat-id wallet-chat-id]]
|
||||
[clojure.string :as string]
|
||||
[status-im.utils.homoglyph :as utils]))
|
||||
|
||||
(defn legal-name? [username]
|
||||
(let [username (some-> username string/trim)]
|
||||
(and (not (utils/matches username console-chat-id))
|
||||
(not (utils/matches username wallet-chat-id)))))
|
||||
|
||||
(spec/def ::legal-name legal-name?)
|
||||
(spec/def ::not-empty-string (spec/and string? not-empty))
|
||||
|
||||
(spec/def ::name (spec/and ::not-empty-string
|
||||
::legal-name))
|
||||
|
||||
(spec/def ::topic (spec/and ::not-empty-string
|
||||
::legal-name
|
||||
(partial re-matches #"[a-z0-9\-]+")))
|
|
@ -0,0 +1,46 @@
|
|||
(ns status-im.chat.new-public-chat.styles
|
||||
(:require-macros [status-im.utils.styles :refer [defstyle]])
|
||||
(:require [status-im.components.styles :as common]))
|
||||
|
||||
(def group-chat-name-input
|
||||
{:font-size 17
|
||||
:padding-bottom 0
|
||||
:letter-spacing -0.2
|
||||
:color common/text1-color})
|
||||
|
||||
(defstyle group-chat-topic-input
|
||||
{:font-size 14
|
||||
:line-height 16
|
||||
:color common/text1-color
|
||||
:padding-left 13
|
||||
:ios {:padding-bottom 0}})
|
||||
|
||||
(defstyle topic-hash-style
|
||||
{:width 10
|
||||
:position :absolute
|
||||
:android {:top 8 :left 3}
|
||||
:ios {:top 6 :left 3}})
|
||||
|
||||
(def topic-hash
|
||||
(merge group-chat-name-input
|
||||
topic-hash-style))
|
||||
|
||||
(def group-chat-name-wrapper
|
||||
{:padding-top 0
|
||||
:height 40
|
||||
:padding-bottom 0})
|
||||
|
||||
(def group-container
|
||||
{:flex 1
|
||||
:flex-direction :column
|
||||
:background-color common/color-white})
|
||||
|
||||
(def chat-name-container
|
||||
{:padding-left 16
|
||||
:margin-top 10})
|
||||
|
||||
(defstyle members-text
|
||||
{:color common/color-gray4
|
||||
:ios {:letter-spacing -0.2
|
||||
:font-size 16}
|
||||
:android {:font-size 14}})
|
|
@ -0,0 +1,57 @@
|
|||
(ns status-im.chat.new-public-chat.view
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.react :refer [view text]]
|
||||
[status-im.components.text-field.view :refer [text-field]]
|
||||
[status-im.components.styles :as common]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar.view :refer [toolbar]]
|
||||
[status-im.chat.new-public-chat.styles :as styles]
|
||||
[status-im.chat.new-public-chat.db :as v]
|
||||
[status-im.i18n :refer [label]]
|
||||
[cljs.spec.alpha :as spec]))
|
||||
|
||||
(defview new-public-chat-toolbar []
|
||||
(letsubs [topic [:get :public-group-topic]]
|
||||
(let [create-btn-enabled? (spec/valid? ::v/topic topic)]
|
||||
[view
|
||||
[status-bar]
|
||||
[toolbar
|
||||
{:title (label :t/new-public-group-chat)
|
||||
:actions [{:image {:source {:uri (if create-btn-enabled?
|
||||
:icon_ok_blue
|
||||
:icon_ok_disabled)}
|
||||
:style common/icon-ok}
|
||||
:handler (when create-btn-enabled?
|
||||
#(dispatch [:create-new-public-chat topic]))}]}]])))
|
||||
|
||||
(defview chat-name-input []
|
||||
(letsubs [topic [:get :public-group-topic]]
|
||||
[view
|
||||
[text-field
|
||||
{:error (cond
|
||||
(not (spec/valid? ::v/not-empty-string topic))
|
||||
(label :t/empty-topic)
|
||||
|
||||
(not (spec/valid? ::v/topic topic))
|
||||
(label :t/topic-format))
|
||||
:wrapper-style styles/group-chat-name-wrapper
|
||||
:error-color common/color-blue
|
||||
:line-color common/color-gray4
|
||||
:label-hidden? true
|
||||
:input-style styles/group-chat-topic-input
|
||||
:auto-focus true
|
||||
:on-change-text #(dispatch [:set :public-group-topic %])
|
||||
:value topic
|
||||
:validator #(re-matches #"[a-z\-]*" %)
|
||||
:auto-capitalize :none}]
|
||||
[text {:style styles/topic-hash} "#"]]))
|
||||
|
||||
(defn new-public-chat []
|
||||
[view styles/group-container
|
||||
[new-public-chat-toolbar]
|
||||
[view styles/chat-name-container
|
||||
[text {:style styles/members-text
|
||||
:font :medium}
|
||||
(label :t/public-group-topic)]
|
||||
[chat-name-input]]])
|
|
@ -68,7 +68,7 @@
|
|||
:icon :settings
|
||||
:icon-style {:width 20
|
||||
:height 13}
|
||||
:handler #(dispatch [:show-group-settings])})
|
||||
:handler #(dispatch [:show-group-chat-settings])})
|
||||
|
||||
(defn group-chat-items [members public?]
|
||||
(into (if public? [] [(item-members members)])
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
[status-im.components.tabs.bottom-shadow :refer [bottom-shadow-view]]
|
||||
[status-im.chats-list.screen :refer [chats-list]]
|
||||
[status-im.discover.screen :refer [discover]]
|
||||
[status-im.contacts.views :refer [contact-list]]
|
||||
[status-im.contacts.views :refer [contact-groups-list]]
|
||||
[status-im.components.tabs.tabs :refer [tabs]]
|
||||
[status-im.components.tabs.styles :as st]
|
||||
[status-im.components.styles :as common-st]
|
||||
|
@ -31,7 +31,7 @@
|
|||
:index 1}
|
||||
{:view-id :contact-list
|
||||
:title (label :t/contacts)
|
||||
:screen contact-list
|
||||
:screen contact-groups-list
|
||||
:icon-inactive :icon_contacts
|
||||
:icon-active :icon_contacts_active
|
||||
:index 2}])
|
||||
|
@ -109,7 +109,7 @@
|
|||
:on-momentum-scroll-end (on-scroll-end swiped? scroll-ended @view-id)})
|
||||
[chats-list]
|
||||
[discover (= @view-id :discover)]
|
||||
[contact-list (= @view-id :contact-list)]]
|
||||
[contact-groups-list (= @view-id :contact-list)]]
|
||||
[tabs {:selected-view-id @view-id
|
||||
:prev-view-id @prev-view-id
|
||||
:tab-list tab-list}]
|
||||
|
|
|
@ -56,8 +56,7 @@
|
|||
|
||||
(defview contact-list []
|
||||
(letsubs [edit? [:get-in [:contacts/list-ui-props :edit?]]
|
||||
group [:get :contacts-group]
|
||||
type [:get :group-type]]
|
||||
group [:get-contact-group]]
|
||||
[drawer-view
|
||||
[view {:flex 1}
|
||||
[view
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
(ns status-im.contacts.db
|
||||
(:require-macros [status-im.utils.db :refer [allowed-keys]])
|
||||
(:require [cljs.spec.alpha :as s]
|
||||
[clojure.string :as str]
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[clojure.string :as string]
|
||||
[status-im.data-store.contacts :as contacts]
|
||||
[status-im.js-dependencies :as dependencies]))
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
|||
(.isAddress dependencies/Web3.prototype s))
|
||||
|
||||
(defn hex-string? [s]
|
||||
(let [s' (if (str/starts-with? s "0x")
|
||||
(let [s' (if (string/starts-with? s "0x")
|
||||
(subs s 2)
|
||||
s)]
|
||||
(boolean (re-matches #"(?i)[0-9a-f]+" s'))))
|
||||
|
@ -24,45 +24,45 @@
|
|||
(and
|
||||
(hex-string? identity)
|
||||
(or
|
||||
(and (= 128 length) (not (str/includes? identity "0x")))
|
||||
(and (= 130 length) (str/starts-with? identity "0x"))
|
||||
(and (= 132 length) (str/starts-with? identity "0x04"))
|
||||
(and (= 128 length) (not (string/includes? identity "0x")))
|
||||
(and (= 130 length) (string/starts-with? identity "0x"))
|
||||
(and (= 132 length) (string/starts-with? identity "0x04"))
|
||||
(is-address? identity)))))
|
||||
|
||||
(s/def ::not-empty-string (s/and string? not-empty))
|
||||
(s/def ::public-key (s/and ::not-empty-string valid-length?))
|
||||
(spec/def ::not-empty-string (spec/and string? not-empty))
|
||||
(spec/def ::public-key (spec/and ::not-empty-string valid-length?))
|
||||
|
||||
;;;; DB
|
||||
|
||||
;;Contact
|
||||
|
||||
;we can't validate public key, because for dapps whisper-identity is just string
|
||||
(s/def :contact/whisper-identity ::not-empty-string)
|
||||
(s/def :contact/name ::not-empty-string)
|
||||
(s/def :contact/address (s/nilable is-address?))
|
||||
(s/def :contact/private-key (s/nilable string?))
|
||||
(s/def :contact/public-key (s/nilable string?))
|
||||
(s/def :contact/photo-path (s/nilable string?))
|
||||
(s/def :contact/status (s/nilable string?))
|
||||
(spec/def :contact/whisper-identity ::not-empty-string)
|
||||
(spec/def :contact/name ::not-empty-string)
|
||||
(spec/def :contact/address (spec/nilable is-address?))
|
||||
(spec/def :contact/private-key (spec/nilable string?))
|
||||
(spec/def :contact/public-key (spec/nilable string?))
|
||||
(spec/def :contact/photo-path (spec/nilable string?))
|
||||
(spec/def :contact/status (spec/nilable string?))
|
||||
|
||||
(s/def :contact/last-updated (s/nilable int?))
|
||||
(s/def :contact/last-online (s/nilable int?))
|
||||
(s/def :contact/pending? boolean?)
|
||||
(s/def :contact/unremovable? boolean?)
|
||||
(spec/def :contact/last-updated (spec/nilable int?))
|
||||
(spec/def :contact/last-online (spec/nilable int?))
|
||||
(spec/def :contact/pending? boolean?)
|
||||
(spec/def :contact/unremovable? boolean?)
|
||||
|
||||
(s/def :contact/dapp? boolean?)
|
||||
(s/def :contact/dapp-url (s/nilable string?))
|
||||
(s/def :contact/dapp-hash (s/nilable int?))
|
||||
(s/def :contact/bot-url (s/nilable string?))
|
||||
(s/def :contact/global-command (s/nilable map?))
|
||||
(s/def :contact/commands (s/nilable (s/map-of keyword? map?)))
|
||||
(s/def :contact/responses (s/nilable (s/map-of keyword? map?)))
|
||||
(s/def :contact/commands-loaded? (s/nilable boolean?))
|
||||
(s/def :contact/subscriptions (s/nilable map?))
|
||||
(spec/def :contact/dapp? boolean?)
|
||||
(spec/def :contact/dapp-url (spec/nilable string?))
|
||||
(spec/def :contact/dapp-hash (spec/nilable int?))
|
||||
(spec/def :contact/bot-url (spec/nilable string?))
|
||||
(spec/def :contact/global-command (spec/nilable map?))
|
||||
(spec/def :contact/commands (spec/nilable (spec/map-of keyword? map?)))
|
||||
(spec/def :contact/responses (spec/nilable (spec/map-of keyword? map?)))
|
||||
(spec/def :contact/commands-loaded? (spec/nilable boolean?))
|
||||
(spec/def :contact/subscriptions (spec/nilable map?))
|
||||
;true when contact added using status-dev-cli
|
||||
(s/def :contact/debug? boolean?)
|
||||
(spec/def :contact/debug? boolean?)
|
||||
|
||||
(s/def :contact/contact (allowed-keys
|
||||
(spec/def :contact/contact (allowed-keys
|
||||
:req-un [:contact/name :contact/whisper-identity]
|
||||
:opt-un [:contact/address :contact/private-key :contact/public-key :contact/photo-path
|
||||
:contact/status :contact/last-updated :contact/last-online :contact/pending?
|
||||
|
@ -71,26 +71,26 @@
|
|||
:contact/commands :contact/responses :contact/debug? :contact/subscriptions]))
|
||||
|
||||
;;Contact list ui props
|
||||
(s/def :contact-list-ui/edit? boolean?)
|
||||
(spec/def :contact-list-ui/edit? boolean?)
|
||||
|
||||
;;Contacts ui props
|
||||
(s/def :contacts-ui/edit? boolean?)
|
||||
(spec/def :contacts-ui/edit? boolean?)
|
||||
|
||||
|
||||
(s/def :contacts/contacts (s/nilable (s/map-of ::not-empty-string :contact/contact)))
|
||||
(spec/def :contacts/contacts (spec/nilable (spec/map-of ::not-empty-string :contact/contact)))
|
||||
;public key of new contact during adding this new contact
|
||||
(s/def :contacts/new-identity (s/nilable string?))
|
||||
(s/def :contacts/new-public-key-error (s/nilable string?))
|
||||
(spec/def :contacts/new-identity (spec/nilable string?))
|
||||
(spec/def :contacts/new-public-key-error (spec/nilable string?))
|
||||
;on showing this contact's profile (andrey: better to move into profile ns)
|
||||
(s/def :contacts/identity (s/nilable ::not-empty-string))
|
||||
(s/def :contacts/list-ui-props (s/nilable (allowed-keys :opt-un [:contact-list-ui/edit?])))
|
||||
(s/def :contacts/ui-props (s/nilable (allowed-keys :opt-un [:contacts-ui/edit?])))
|
||||
(spec/def :contacts/identity (spec/nilable ::not-empty-string))
|
||||
(spec/def :contacts/list-ui-props (spec/nilable (allowed-keys :opt-un [:contact-list-ui/edit?])))
|
||||
(spec/def :contacts/ui-props (spec/nilable (allowed-keys :opt-un [:contacts-ui/edit?])))
|
||||
;used in modal list (for example for wallet)
|
||||
(s/def :contacts/click-handler (s/nilable fn?))
|
||||
(spec/def :contacts/click-handler (spec/nilable fn?))
|
||||
;used in modal list (for example for wallet)
|
||||
(s/def :contacts/click-action (s/nilable #{:send :request}))
|
||||
(spec/def :contacts/click-action (spec/nilable #{:send :request}))
|
||||
;used in modal list (for example for wallet)
|
||||
(s/def :contacts/click-params (s/nilable map?))
|
||||
(spec/def :contacts/click-params (spec/nilable map?))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -12,11 +12,11 @@
|
|||
[cljs.reader :refer [read-string]]
|
||||
[status-im.utils.js-resources :as js-res]
|
||||
[status-im.react-native.js-dependencies :as rn-dependencies]
|
||||
[status-im.contacts.navigation]
|
||||
[status-im.utils.identicon :refer [identicon]]
|
||||
[status-im.utils.gfycat.core :refer [generate-gfy]]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.contacts.db :as v]))
|
||||
[status-im.contacts.db :as v]
|
||||
[status-im.contacts.navigation]))
|
||||
|
||||
;;;; COFX
|
||||
|
||||
|
@ -208,7 +208,7 @@
|
|||
(subs (.sha3 js/Web3.prototype normalized-key #js {:encoding "hex"}) 26))))
|
||||
|
||||
(defn- prepare-default-groups-events [groups default-groups]
|
||||
[[:add-groups
|
||||
[[:add-contact-groups
|
||||
(for [[id {:keys [name contacts]}] default-groups
|
||||
:let [id' (clojure.core/name id)]
|
||||
:when (not (get groups id'))]
|
||||
|
@ -412,9 +412,9 @@
|
|||
(register-handler-fx
|
||||
:remove-contact-from-group
|
||||
(fn [{:keys [db]} [_ whisper-identity group-id]]
|
||||
(let [{:keys [contact-groups]} db
|
||||
(let [{:group/keys [contact-groups]} db
|
||||
group' (update (contact-groups group-id) :contacts (remove-contact-from-group whisper-identity))]
|
||||
{:dispatch [:update-group group']})))
|
||||
{:dispatch [:update-contact-group group']})))
|
||||
|
||||
(register-handler-fx
|
||||
:remove-contact
|
||||
|
@ -428,8 +428,8 @@
|
|||
:open-contact-toggle-list
|
||||
(fn [{:keys [db]} [_ group-type]]
|
||||
{:db (-> db
|
||||
(assoc :group-type group-type
|
||||
:selected-contacts #{}
|
||||
(assoc :group/group-type group-type
|
||||
:group/selected-contacts #{}
|
||||
:new-chat-name "")
|
||||
(assoc-in [:toolbar-search :show] nil)
|
||||
(assoc-in [:toolbar-search :text] ""))
|
||||
|
|
|
@ -1,22 +1,6 @@
|
|||
(ns status-im.contacts.navigation
|
||||
(:require [status-im.navigation.handlers :as nav]))
|
||||
|
||||
(defmethod nav/preload-data! :group-contacts
|
||||
[db [_ _ group show-search?]]
|
||||
(-> db
|
||||
(assoc :contacts-group group)
|
||||
(update :toolbar-search assoc
|
||||
:show (when show-search? :contact-list)
|
||||
:text "")))
|
||||
|
||||
(defmethod nav/preload-data! :edit-group
|
||||
[db [_ _ group group-type]]
|
||||
(if group
|
||||
(assoc db :contact-group-id (:group-id group)
|
||||
:group-type group-type
|
||||
:new-chat-name (:name group))
|
||||
db))
|
||||
|
||||
(defmethod nav/preload-data! :contact-list
|
||||
[db [_ _ click-handler]]
|
||||
(-> db
|
||||
|
@ -24,10 +8,3 @@
|
|||
(assoc-in [:contacts/list-ui-props :edit?] false)
|
||||
(assoc-in [:contacts/ui-props :edit?] false)
|
||||
(assoc :contacts/click-handler click-handler)))
|
||||
|
||||
(defmethod nav/preload-data! :reorder-groups
|
||||
[db [_ _]]
|
||||
(assoc db :groups-order (->> (vals (:contact-groups db))
|
||||
(remove :pending?)
|
||||
(sort-by :order >)
|
||||
(map :group-id))))
|
|
@ -50,7 +50,7 @@
|
|||
(reg-sub
|
||||
:group-contacts
|
||||
(fn [db [_ group-id]]
|
||||
(get-in db [:contact-groups group-id :contacts])))
|
||||
(get-in db [:group/contact-groups group-id :contacts])))
|
||||
|
||||
(reg-sub
|
||||
:all-added-group-contacts
|
||||
|
@ -98,16 +98,11 @@
|
|||
(fn [contacts]
|
||||
(count contacts)))
|
||||
|
||||
(reg-sub
|
||||
:contact-groups
|
||||
(fn [db]
|
||||
(vals (:contact-groups db))))
|
||||
|
||||
(reg-sub
|
||||
:all-added-groups
|
||||
:<- [:contact-groups]
|
||||
:<- [:get-contact-groups]
|
||||
(fn [groups]
|
||||
(->> (remove :pending? groups)
|
||||
(->> (remove :pending? (vals groups))
|
||||
(sort-by :order >))))
|
||||
|
||||
(defn search-filter [text item]
|
||||
|
@ -133,8 +128,9 @@
|
|||
|
||||
(reg-sub
|
||||
:contact-group-contacts
|
||||
(fn [db]
|
||||
(get-in db [:contact-groups (:contact-group-id db) :contacts])))
|
||||
:<- [:get-contact-group]
|
||||
(fn [group]
|
||||
(:contacts group)))
|
||||
|
||||
(reg-sub
|
||||
:all-not-added-contact-group-contacts
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
[common/form-title subtitle
|
||||
{:count-value contacts-count
|
||||
:extended? edit?
|
||||
:options [{:value #(dispatch [:navigate-to :edit-group group :contact-group])
|
||||
:options [{:value #(dispatch [:navigate-to :edit-contact-group group :contact-group])
|
||||
:text (label :t/edit-group)}]}])
|
||||
[view st/contacts-list
|
||||
[common/list-footer]
|
||||
|
@ -85,7 +85,7 @@
|
|||
[touchable-highlight {:on-press #(do
|
||||
(when edit?
|
||||
(dispatch [:set-in [:contacts/list-ui-props :edit?] true]))
|
||||
(dispatch [:navigate-to :group-contacts group]))}
|
||||
(dispatch [:navigate-to :group-contacts (:group-id group)]))}
|
||||
[view
|
||||
[text {:style st/show-all-text
|
||||
:uppercase? (get-in platform-specific [:uppercase?])
|
||||
|
@ -112,7 +112,7 @@
|
|||
[ion-icon {:name :md-create
|
||||
:style create-icon}]]])
|
||||
|
||||
(defview contact-list [_]
|
||||
(defview contact-groups-list [_]
|
||||
(letsubs [contacts [:get-added-contacts-with-limit contacts-limit]
|
||||
contacts-count [:added-contacts-count]
|
||||
edit? [:get-in [:contacts/ui-props :edit?]]
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
:navigation-stack '()
|
||||
:contacts/contacts {}
|
||||
:qr-codes {}
|
||||
:contact-groups {}
|
||||
:selected-contacts #{}
|
||||
:group/contact-groups {}
|
||||
:group/selected-contacts #{}
|
||||
:chats {}
|
||||
:current-chat-id console-chat-id
|
||||
:loading-allowed true
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
(ns status-im.group.add-contacts.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.react :refer [view keyboard-avoiding-view
|
||||
text list-view list-item]]
|
||||
[status-im.components.sticky-button :refer [sticky-button]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search]]
|
||||
[status-im.utils.listview :refer [to-datasource]]
|
||||
[status-im.group.styles :as styles]
|
||||
[status-im.contacts.styles :as cstyles]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.components.contact.contact :refer [toogle-contact-view]]))
|
||||
|
||||
(defn on-toggle [checked? whisper-identity]
|
||||
(let [action (if checked? :deselect-contact :select-contact)]
|
||||
(dispatch [action whisper-identity])))
|
||||
|
||||
(defn on-toggle-participant [checked? whisper-identity]
|
||||
(let [action (if checked? :deselect-participant :select-participant)]
|
||||
(dispatch [action whisper-identity])))
|
||||
|
||||
(defn group-toggle-contact [{:keys [whisper-identity] :as contact}]
|
||||
[toogle-contact-view contact :is-contact-selected? on-toggle])
|
||||
|
||||
(defn group-toggle-participant [{:keys [whisper-identity] :as contact}]
|
||||
[toogle-contact-view contact :is-participant-selected? on-toggle-participant])
|
||||
|
||||
(defn title-with-count [title count-value]
|
||||
[view styles/toolbar-title-with-count
|
||||
[text {:style styles/toolbar-title-with-count-text
|
||||
:font :toolbar-title}
|
||||
title]
|
||||
(when (pos? count-value)
|
||||
[view styles/toolbar-title-with-count-container
|
||||
[text {:style styles/toolbar-title-with-count-text-count
|
||||
:font :toolbar-title}
|
||||
count-value]])])
|
||||
|
||||
(defview toggle-list-toolbar [title contacts-count]
|
||||
(letsubs [show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-group-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-group-list
|
||||
:custom-title (title-with-count title contacts-count)
|
||||
:search-placeholder (label :t/search-contacts)})))
|
||||
|
||||
(defn toggle-list [contacts render-function]
|
||||
[view {:flex 1}
|
||||
[list-view
|
||||
{:dataSource (to-datasource contacts)
|
||||
:renderRow (fn [row _ _]
|
||||
(list-item ^{:key row} [render-function row]))
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:renderFooter renderers/list-footer-renderer
|
||||
:renderHeader renderers/list-header-renderer
|
||||
:style cstyles/contacts-list
|
||||
:keyboardShouldPersistTaps :always}]])
|
||||
|
||||
(defview contact-toggle-list []
|
||||
(letsubs [contacts [:all-added-group-contacts-filtered]
|
||||
selected-contacts-count [:selected-contacts-count]
|
||||
group-type [:get-group-type]]
|
||||
[keyboard-avoiding-view {:style styles/group-container}
|
||||
[status-bar]
|
||||
[toggle-list-toolbar
|
||||
(label (if (= group-type :contact-group)
|
||||
:t/new-group
|
||||
:t/new-group-chat))
|
||||
selected-contacts-count]
|
||||
[toggle-list contacts group-toggle-contact]
|
||||
(when (pos? selected-contacts-count)
|
||||
[sticky-button (label :t/next) #(dispatch [:navigate-to :new-group])])]))
|
||||
|
||||
(defview add-contacts-toggle-list []
|
||||
(letsubs [contacts [:all-group-not-added-contacts-filtered]
|
||||
group [:get-contact-group]
|
||||
selected-contacts-count [:selected-contacts-count]]
|
||||
[keyboard-avoiding-view {:style styles/group-container}
|
||||
[status-bar]
|
||||
[toggle-list-toolbar (:name group) selected-contacts-count]
|
||||
[toggle-list contacts group-toggle-contact]
|
||||
(when (pos? selected-contacts-count)
|
||||
[sticky-button (label :t/save) #(do
|
||||
(dispatch [:add-selected-contacts-to-group])
|
||||
(dispatch [:navigate-back]))])]))
|
||||
|
||||
(defview add-participants-toggle-list []
|
||||
(letsubs [contacts [:contacts-filtered :all-new-contacts]
|
||||
chat-name [:chat :name]
|
||||
selected-contacts-count [:selected-participants-count]]
|
||||
[keyboard-avoiding-view {:style styles/group-container}
|
||||
[status-bar]
|
||||
[toggle-list-toolbar chat-name selected-contacts-count]
|
||||
[toggle-list contacts group-toggle-participant]
|
||||
(when (pos? selected-contacts-count)
|
||||
[sticky-button (label :t/save) #(do
|
||||
(dispatch [:add-new-group-chat-participants])
|
||||
(dispatch [:navigate-back]))])]))
|
|
@ -0,0 +1,177 @@
|
|||
(ns status-im.group.chat-settings.events
|
||||
(:require [re-frame.core :refer [dispatch reg-fx]]
|
||||
[status-im.utils.handlers :refer [register-handler-fx]]
|
||||
[status-im.protocol.core :as protocol]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.chat.handlers :as chat-events]
|
||||
[status-im.data-store.contacts :as contacts]
|
||||
[status-im.data-store.messages :as messages]
|
||||
[status-im.data-store.chats :as chats]
|
||||
[status-im.constants :refer [text-content-type]]))
|
||||
|
||||
;;;; COFX
|
||||
|
||||
;;;; FX
|
||||
|
||||
(reg-fx
|
||||
::save-chat-property
|
||||
(fn [[current-chat-id property-name value]]
|
||||
(chats/save-property current-chat-id property-name value)))
|
||||
|
||||
(reg-fx
|
||||
::add-members-to-chat
|
||||
(fn [{:keys [current-chat-id selected-participants]}]
|
||||
(chats/add-contacts current-chat-id selected-participants)))
|
||||
|
||||
(reg-fx
|
||||
::remove-members-from-chat
|
||||
(fn [[current-chat-id participants]]
|
||||
(chats/remove-contacts current-chat-id participants)))
|
||||
|
||||
(defn system-message [message-id content]
|
||||
{:from "system"
|
||||
:message-id message-id
|
||||
:content content
|
||||
:content-type text-content-type})
|
||||
|
||||
(defn removed-participant-message [chat-id identity]
|
||||
(let [contact-name (:name (contacts/get-by-id identity))]
|
||||
(->> (str "You've removed " (or contact-name identity))
|
||||
(system-message (random/id))
|
||||
(messages/save chat-id))))
|
||||
|
||||
(reg-fx
|
||||
::create-removing-messages
|
||||
(fn [{:keys [current-chat-id participants]}]
|
||||
(doseq [participant participants]
|
||||
(removed-participant-message current-chat-id participant))))
|
||||
|
||||
(reg-fx
|
||||
::notify-about-new-members
|
||||
(fn [{:keys [current-chat-id selected-participants
|
||||
current-public-key chats web3]}]
|
||||
(let [{:keys [name contacts]} (chats current-chat-id)
|
||||
identities (map :identity contacts)
|
||||
|
||||
{:keys [public private]
|
||||
:as new-keypair} (protocol/new-keypair!)
|
||||
|
||||
group-message {:web3 web3
|
||||
:group {:id current-chat-id
|
||||
:name name
|
||||
:contacts (conj identities current-public-key)
|
||||
:admin current-public-key}
|
||||
:message {:from current-public-key
|
||||
:message-id (random/id)}}]
|
||||
(dispatch [:update-chat! {:chat-id current-chat-id
|
||||
:public-key public
|
||||
:private-key private}])
|
||||
(protocol/start-watching-group! {:web3 web3
|
||||
:group-id current-chat-id
|
||||
:identity current-public-key
|
||||
:keypair new-keypair
|
||||
:callback #(dispatch [:incoming-message %1 %2])})
|
||||
(protocol/invite-to-group!
|
||||
(-> group-message
|
||||
(assoc-in [:group :keypair] new-keypair)
|
||||
(assoc :identities selected-participants)))
|
||||
(protocol/update-group!
|
||||
(-> group-message
|
||||
(assoc-in [:group :keypair] new-keypair)
|
||||
(assoc :identities identities)))
|
||||
(doseq [identity selected-participants]
|
||||
(protocol/add-to-group! {:web3 web3
|
||||
:group-id current-chat-id
|
||||
:identity identity
|
||||
:keypair new-keypair
|
||||
:message {:from current-public-key
|
||||
:message-id (random/id)}})))))
|
||||
|
||||
(reg-fx
|
||||
::notify-about-removing
|
||||
(fn [{:keys [web3 current-chat-id participants chats current-public-key]}]
|
||||
(let [{:keys [private public] :as new-keypair} (protocol/new-keypair!)
|
||||
{:keys [name private-key public-key]
|
||||
:as chat} (get chats current-chat-id)
|
||||
old-keypair {:private private-key
|
||||
:public public-key}
|
||||
contacts (get chat :contacts)
|
||||
identities (-> (map :identity contacts)
|
||||
set
|
||||
(clojure.set/difference participants))]
|
||||
(dispatch [:update-chat! {:chat-id current-chat-id
|
||||
:private-key private
|
||||
:public-key public}])
|
||||
(doseq [participant participants]
|
||||
(let [id (random/id)]
|
||||
(doseq [keypair [old-keypair new-keypair]]
|
||||
(protocol/remove-from-group!
|
||||
{:web3 web3
|
||||
:group-id current-chat-id
|
||||
:identity participant
|
||||
:keypair keypair
|
||||
:message {:from current-public-key
|
||||
:message-id id}}))))
|
||||
(protocol/start-watching-group!
|
||||
{:web3 web3
|
||||
:group-id current-chat-id
|
||||
:identity current-public-key
|
||||
:keypair new-keypair
|
||||
:callback #(dispatch [:incoming-message %1 %2])})
|
||||
(protocol/update-group!
|
||||
{:web3 web3
|
||||
:group {:id current-chat-id
|
||||
:name name
|
||||
:contacts (conj identities current-public-key)
|
||||
:admin current-public-key
|
||||
:keypair new-keypair}
|
||||
:identities identities
|
||||
:message {:from current-public-key
|
||||
:message-id (random/id)}}))))
|
||||
|
||||
;;;; Handlers
|
||||
|
||||
(register-handler-fx
|
||||
:show-group-chat-settings
|
||||
(fn [{{:keys [current-chat-id] :as db} :db} _]
|
||||
{:db (assoc db :new-chat-name (get-in db [:chats current-chat-id :name])
|
||||
:group/group-type :chat-group)
|
||||
:dispatch [:navigate-to :chat-group-settings]}))
|
||||
|
||||
(register-handler-fx
|
||||
:add-new-group-chat-participants
|
||||
(fn [{{:keys [current-chat-id selected-participants] :as db} :db} _]
|
||||
(let [new-identities (map #(hash-map :identity %) selected-participants)]
|
||||
{:db (-> db
|
||||
(update-in [:chats current-chat-id :contacts] concat new-identities)
|
||||
(assoc :selected-participants #{}))
|
||||
::add-members-to-chat (select-keys db [:current-chat-id :selected-participants])
|
||||
::notify-about-new-members (select-keys db [:current-chat-id :selected-participants
|
||||
:current-public-key :chats :web3])})))
|
||||
|
||||
(defn remove-identities [collection identities]
|
||||
(remove #(identities (:identity %)) collection))
|
||||
|
||||
(register-handler-fx
|
||||
:remove-group-chat-participants
|
||||
(fn [{{:keys [current-chat-id] :as db} :db} [_ participants]]
|
||||
{:db (update-in db [:chats current-chat-id :contacts] remove-identities participants)
|
||||
::remove-members-from-chat [current-chat-id participants]
|
||||
::notify-about-removing (merge {:participants participants}
|
||||
(select-keys db [:web3 :current-chat-id :chats :current-public-key]))
|
||||
::create-removing-messages (merge {:participants participants}
|
||||
(select-keys db [:current-chat-id]))}))
|
||||
|
||||
(register-handler-fx
|
||||
:set-chat-name
|
||||
(fn [{{:keys [current-chat-id new-chat-name] :as db} :db} _]
|
||||
{:db (assoc-in db [:chats current-chat-id :name] new-chat-name)
|
||||
::save-chat-property [current-chat-id :name new-chat-name]}))
|
||||
|
||||
(register-handler-fx
|
||||
:clear-history
|
||||
(fn [{{:keys [current-chat-id] :as db} :db} _]
|
||||
{:db (-> db
|
||||
(assoc-in [:chats current-chat-id :messages] '())
|
||||
(assoc-in [:chats current-chat-id :last-message] nil))
|
||||
::chat-events/delete-messages current-chat-id}))
|
|
@ -1,8 +1,9 @@
|
|||
(ns status-im.group-settings.subs
|
||||
(ns status-im.group.chat-settings.subs
|
||||
(:require [re-frame.core :refer [reg-sub]]
|
||||
[status-im.constants :refer [max-chat-name-length]]))
|
||||
|
||||
(reg-sub :selected-participant
|
||||
(reg-sub
|
||||
:selected-participant
|
||||
(fn [db]
|
||||
(let [identity (first (:selected-participants db))]
|
||||
(get-in db [:contacts/contacts identity]))))
|
||||
|
@ -14,12 +15,19 @@
|
|||
(when (< max-chat-name-length (count chat-name))
|
||||
"Chat name is too long"))))
|
||||
|
||||
(reg-sub :new-chat-name-validation-messages
|
||||
(reg-sub
|
||||
:new-chat-name
|
||||
(fn [db]
|
||||
(let [chat-name (:new-chat-name db)]
|
||||
(get-chat-name-validation-messages chat-name))))
|
||||
(:new-chat-name db)))
|
||||
|
||||
(reg-sub :new-chat-name-valid?
|
||||
(fn [db]
|
||||
(let [chat-name (:new-chat-name db)]
|
||||
(zero? (count (get-chat-name-validation-messages chat-name))))))
|
||||
(reg-sub
|
||||
:new-chat-name-validation-messages
|
||||
:<- [:new-chat-name]
|
||||
(fn [chat-name]
|
||||
(get-chat-name-validation-messages chat-name)))
|
||||
|
||||
(reg-sub
|
||||
:new-chat-name-valid?
|
||||
:<- [:new-chat-name]
|
||||
(fn [chat-name]
|
||||
(zero? (count (get-chat-name-validation-messages chat-name)))))
|
|
@ -0,0 +1,65 @@
|
|||
(ns status-im.group.chat-settings.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.react :refer [view scroll-view keyboard-avoiding-view]]
|
||||
[status-im.components.sticky-button :refer [sticky-button]]
|
||||
[status-im.group.styles :as styles]
|
||||
[status-im.group.views :refer [group-toolbar group-chat-settings-btns
|
||||
group-name-view add-btn more-btn]]
|
||||
[status-im.group.db :as v]
|
||||
[status-im.i18n :refer [label]]
|
||||
[cljs.spec.alpha :as spec]))
|
||||
|
||||
(def ^:const contacts-limit 3)
|
||||
|
||||
(defview chat-group-contacts-view [admin?]
|
||||
(letsubs [contacts [:current-chat-contacts]]
|
||||
(let [limited-contacts (take contacts-limit contacts)
|
||||
contacts-count (count contacts)]
|
||||
[view
|
||||
(when (and admin? (pos? contacts-count))
|
||||
[common/list-separator])
|
||||
[view
|
||||
(doall
|
||||
(map (fn [row]
|
||||
^{:key row}
|
||||
[view
|
||||
[contact-view
|
||||
{:contact row
|
||||
:extend-options [{:value #(dispatch [:remove-group-chat-participants #{(:whisper-identity row)}])
|
||||
:text (label :t/remove)}]
|
||||
:extended? admin?}]
|
||||
(when-not (= row (last limited-contacts))
|
||||
[common/list-separator])])
|
||||
limited-contacts))]
|
||||
(when (< contacts-limit contacts-count)
|
||||
[more-btn contacts-limit contacts-count #(dispatch [:navigate-to :edit-chat-group-contact-list])])])))
|
||||
|
||||
(defview chat-group-members []
|
||||
(letsubs [current-pk [:get :current-public-key]
|
||||
group-admin [:chat :group-admin]]
|
||||
(let [admin? (= current-pk group-admin)]
|
||||
[view
|
||||
(when admin?
|
||||
[add-btn #(dispatch [:navigate-to :add-participants-toggle-list])])
|
||||
[chat-group-contacts-view admin?]])))
|
||||
|
||||
(defview chat-group-settings []
|
||||
(letsubs [new-chat-name [:get :new-chat-name]
|
||||
chat-name [:chat :name]
|
||||
type [:get-group-type]]
|
||||
(let [save-btn-enabled? (and (spec/valid? ::v/name new-chat-name)
|
||||
(not= new-chat-name chat-name))]
|
||||
[keyboard-avoiding-view {:style styles/group-container}
|
||||
[view {:flex 1}
|
||||
[group-toolbar type true]
|
||||
[scroll-view
|
||||
[group-name-view]
|
||||
[chat-group-members]
|
||||
[view styles/separator]
|
||||
[group-chat-settings-btns]]]
|
||||
(when save-btn-enabled?
|
||||
[sticky-button (label :t/save) #(dispatch [:set-chat-name])
|
||||
true])])))
|
|
@ -0,0 +1,36 @@
|
|||
(ns status-im.group.db
|
||||
(:require-macros [status-im.utils.db :refer [allowed-keys]])
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[status-im.constants :refer [console-chat-id wallet-chat-id]]
|
||||
[clojure.string :as string]
|
||||
[status-im.utils.homoglyph :as utils]))
|
||||
|
||||
(spec/def ::not-empty-string (spec/and string? not-empty))
|
||||
|
||||
(spec/def ::name ::not-empty-string)
|
||||
;;;; DB
|
||||
|
||||
(spec/def :group/group-id ::not-empty-string)
|
||||
(spec/def :group/name ::not-empty-string)
|
||||
(spec/def :group/timestamp int?)
|
||||
(spec/def :group/pending? boolean?)
|
||||
(spec/def :group/order int?)
|
||||
|
||||
(spec/def :group-contact/identity ::not-empty-string)
|
||||
|
||||
(spec/def :group/contact (allowed-keys :req-un [:group-contact/identity]))
|
||||
|
||||
(spec/def :group/contacts (spec/nilable (spec/* :group/contact)))
|
||||
|
||||
(spec/def :group/contact-group (allowed-keys
|
||||
:req-un [:group/group-id :group/name :group/timestamp
|
||||
:group/order :group/contacts]
|
||||
:opt-un [:group/pending?]))
|
||||
|
||||
(spec/def :group/contact-groups (spec/nilable (spec/map-of ::not-empty-string :group/contact-group)))
|
||||
;;used during editing contact group
|
||||
(spec/def :group/contact-group-id (spec/nilable string?))
|
||||
(spec/def :group/group-type (spec/nilable #{:chat-group :contact-group}))
|
||||
(spec/def :group/selected-contacts (spec/nilable (spec/* string?)))
|
||||
;;list of group ids
|
||||
(spec/def :group/groups-order (spec/nilable (spec/* string?)))
|
|
@ -0,0 +1,77 @@
|
|||
(ns status-im.group.edit-contacts.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.react :refer [view list-view list-item]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search]]
|
||||
[status-im.utils.listview :refer [to-datasource]]
|
||||
[status-im.group.styles :as styles]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(defview contact-list-toolbar [title]
|
||||
(letsubs [show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-list
|
||||
:title title
|
||||
:search-placeholder (label :t/search-contacts)})))
|
||||
|
||||
(defn contacts-list [contacts extended? extend-options]
|
||||
[view {:flex 1}
|
||||
[list-view {:dataSource (to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow (fn [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row
|
||||
:extended? extended?
|
||||
:extend-options (extend-options row)}]))
|
||||
:bounces false
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:renderFooter renderers/list-footer-renderer
|
||||
:renderHeader renderers/list-header-renderer}]])
|
||||
|
||||
(defn chat-extended-options [item]
|
||||
[{:value #(dispatch [:remove-group-chat-participants #{(:whisper-identity item)}])
|
||||
:text (label :t/remove)}])
|
||||
|
||||
(defn contact-extended-options [group-id]
|
||||
(fn [item]
|
||||
[{:value #(dispatch [:remove-contact-from-group
|
||||
(:whisper-identity item)
|
||||
group-id])
|
||||
:text (label :t/remove-from-group)}]))
|
||||
|
||||
(defview edit-chat-group-contact-list []
|
||||
(letsubs [chat-name [:chat :name]
|
||||
contacts [:contacts-filtered :current-chat-contacts]
|
||||
current-pk [:get :current-public-key]
|
||||
group-admin [:chat :group-admin]]
|
||||
(let [admin? (= current-pk group-admin)]
|
||||
[view styles/group-container
|
||||
[status-bar]
|
||||
[contact-list-toolbar chat-name]
|
||||
[contacts-list
|
||||
contacts
|
||||
admin?
|
||||
chat-extended-options]])))
|
||||
|
||||
(defview contacts-list-view [group-id]
|
||||
(letsubs [contacts [:all-added-group-contacts-filtered group-id]]
|
||||
[contacts-list
|
||||
contacts
|
||||
true
|
||||
(contact-extended-options group-id)]))
|
||||
|
||||
(defview edit-contact-group-contact-list []
|
||||
(letsubs [group [:get-contact-group]
|
||||
type [:get-group-type]]
|
||||
[view styles/group-container
|
||||
[status-bar]
|
||||
[contact-list-toolbar (:name group)]
|
||||
[contacts-list-view (:group-id group)]]))
|
|
@ -0,0 +1,269 @@
|
|||
(ns status-im.group.events
|
||||
(:require [status-im.protocol.core :as protocol]
|
||||
[re-frame.core :refer [dispatch reg-fx reg-cofx inject-cofx]]
|
||||
[status-im.utils.handlers :refer [register-handler-db register-handler-fx]]
|
||||
[status-im.components.styles :refer [default-chat-color]]
|
||||
[status-im.data-store.chats :as chats]
|
||||
[status-im.data-store.contact-groups :as groups]
|
||||
[clojure.string :as string]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.group.navigation]))
|
||||
|
||||
;;;; COFX
|
||||
|
||||
(reg-cofx
|
||||
::get-all-contact-groups
|
||||
(fn [coeffects _]
|
||||
(let [groups (->> (groups/get-all)
|
||||
(map (fn [{:keys [group-id] :as group}]
|
||||
[group-id group]))
|
||||
(into {}))]
|
||||
(assoc coeffects :all-groups groups))))
|
||||
|
||||
;;;; FX
|
||||
|
||||
(reg-fx
|
||||
::save-chat
|
||||
(fn [new-chat]
|
||||
(chats/save new-chat)))
|
||||
|
||||
(reg-fx
|
||||
::save-contact-group
|
||||
(fn [new-group]
|
||||
(groups/save new-group)))
|
||||
|
||||
(reg-fx
|
||||
::save-contact-groups
|
||||
(fn [new-groups]
|
||||
(groups/save-all new-groups)))
|
||||
|
||||
(reg-fx
|
||||
::save-contact-group-property
|
||||
(fn [[contact-group-id property-name value]]
|
||||
(groups/save-property contact-group-id property-name value)))
|
||||
|
||||
(reg-fx
|
||||
::add-contacts-to-contact-group
|
||||
(fn [[contact-group-id selected-contacts]]
|
||||
(groups/add-contacts contact-group-id selected-contacts)))
|
||||
|
||||
(reg-fx
|
||||
::start-listen-group
|
||||
(fn [{:keys [new-chat web3 current-public-key]}]
|
||||
(let [{:keys [chat-id public-key private-key contacts name]} new-chat
|
||||
identities (mapv :identity contacts)]
|
||||
(protocol/invite-to-group!
|
||||
{:web3 web3
|
||||
:group {:id chat-id
|
||||
:name name
|
||||
:contacts (conj identities current-public-key)
|
||||
:admin current-public-key
|
||||
:keypair {:public public-key
|
||||
:private private-key}}
|
||||
:identities identities
|
||||
:message {:from current-public-key
|
||||
:message-id (random/id)}})
|
||||
(protocol/start-watching-group!
|
||||
{:web3 web3
|
||||
:group-id chat-id
|
||||
:identity current-public-key
|
||||
:keypair {:public public-key
|
||||
:private private-key}
|
||||
:callback #(dispatch [:incoming-message %1 %2])}))))
|
||||
|
||||
(reg-fx
|
||||
::start-watching-group
|
||||
(fn [{:keys [group-id web3 current-public-key keypair]}]
|
||||
(protocol/start-watching-group!
|
||||
{:web3 web3
|
||||
:group-id group-id
|
||||
:identity current-public-key
|
||||
:keypair keypair
|
||||
:callback #(dispatch [:incoming-message %1 %2])})))
|
||||
|
||||
;;;; Handlers
|
||||
|
||||
(register-handler-db
|
||||
:deselect-contact
|
||||
(fn [db [_ id]]
|
||||
(update db :group/selected-contacts disj id)))
|
||||
|
||||
(register-handler-db
|
||||
:select-contact
|
||||
(fn [db [_ id]]
|
||||
(update db :group/selected-contacts conj id)))
|
||||
|
||||
(register-handler-db
|
||||
:deselect-participant
|
||||
(fn [db [_ id]]
|
||||
(update db :selected-participants disj id)))
|
||||
|
||||
(register-handler-db
|
||||
:select-participant
|
||||
(fn [db [_ id]]
|
||||
(update db :selected-participants conj id)))
|
||||
|
||||
(defn group-name-from-contacts [contacts selected-contacts username]
|
||||
(->> (select-keys contacts selected-contacts)
|
||||
vals
|
||||
(map :name)
|
||||
(cons username)
|
||||
(string/join ", ")))
|
||||
|
||||
(defn prepare-chat [{:keys [current-public-key username]
|
||||
:group/keys [selected-contacts]
|
||||
:contacts/keys [contacts]} group-name]
|
||||
(let [selected-contacts' (mapv #(hash-map :identity %) selected-contacts)
|
||||
chat-name (if-not (string/blank? group-name)
|
||||
group-name
|
||||
(group-name-from-contacts contacts selected-contacts username))
|
||||
{:keys [public private]} (protocol/new-keypair!)]
|
||||
{:chat-id (random/id)
|
||||
:public-key public
|
||||
:private-key private
|
||||
:name chat-name
|
||||
:color default-chat-color
|
||||
:group-chat true
|
||||
:group-admin current-public-key
|
||||
:is-active true
|
||||
:timestamp (random/timestamp)
|
||||
:contacts selected-contacts'}))
|
||||
|
||||
(register-handler-fx
|
||||
:create-new-group-chat-and-open
|
||||
(fn [{:keys [db]} [_ group-name]]
|
||||
(let [new-chat (prepare-chat (select-keys db [:group/selected-contacts :current-public-key :username
|
||||
:contacts/contacts])
|
||||
group-name)]
|
||||
{:db (-> db
|
||||
(assoc-in [:chats (:chat-id new-chat)] new-chat)
|
||||
(assoc :group/selected-contacts #{}))
|
||||
::save-chat new-chat
|
||||
::start-listen-group (merge {:new-chat new-chat}
|
||||
(select-keys db [:web3 :current-public-key]))
|
||||
:dispatch-n [[:navigate-to-clean :chat-list]
|
||||
[:navigate-to :chat (:chat-id new-chat)]]})))
|
||||
|
||||
(register-handler-fx
|
||||
:group-chat-invite-received
|
||||
(fn [{{:keys [current-public-key] :as db} :db}
|
||||
[_ {:keys [from]
|
||||
{:keys [group-id group-name contacts keypair timestamp]} :payload}]]
|
||||
(let [{:keys [private public]} keypair]
|
||||
(let [contacts' (keep (fn [ident]
|
||||
(when (not= ident current-public-key)
|
||||
{:identity ident})) contacts)
|
||||
chat {:chat-id group-id
|
||||
:name group-name
|
||||
:group-chat true
|
||||
:group-admin from
|
||||
:public-key public
|
||||
:private-key private
|
||||
:contacts contacts'
|
||||
:added-to-at timestamp
|
||||
:timestamp timestamp
|
||||
:is-active true}
|
||||
exists? (chats/exists? group-id)]
|
||||
(when (or (not exists?) (chats/new-update? timestamp group-id))
|
||||
{::start-watching-group (merge {:group-id group-id
|
||||
:keypair keypair}
|
||||
(select-keys db [:web3 :current-public-key]))
|
||||
:dispatch (if exists?
|
||||
[:update-chat! chat]
|
||||
[:add-chat group-id chat])})))))
|
||||
|
||||
(register-handler-fx
|
||||
:create-new-contact-group
|
||||
(fn [{{:group/keys [contact-groups selected-contacts] :as db} :db} [_ group-name]]
|
||||
(let [selected-contacts' (mapv #(hash-map :identity %) selected-contacts)
|
||||
new-group {:group-id (random/id)
|
||||
:name group-name
|
||||
:order (count contact-groups)
|
||||
:timestamp (random/timestamp)
|
||||
:contacts selected-contacts'}]
|
||||
{:db (update db :group/contact-groups merge {(:group-id new-group) new-group})
|
||||
::save-contact-group new-group})))
|
||||
|
||||
(register-handler-fx
|
||||
:update-contact-group
|
||||
(fn [{:keys [db]} [_ new-group]]
|
||||
{:db (update db :group/contact-groups merge {(:group-id new-group) new-group})
|
||||
::save-contact-group new-group}))
|
||||
|
||||
(defn update-pending-status [old-groups {:keys [group-id pending?] :as group}]
|
||||
(let [{old-pending :pending?
|
||||
:as old-group} (get old-groups group-id)
|
||||
pending?' (if old-pending (and old-pending pending?) pending?)]
|
||||
(assoc group :pending? (boolean pending?'))))
|
||||
|
||||
(register-handler-fx
|
||||
:add-contact-groups
|
||||
(fn [{{:group/keys [contact-groups] :as db} :db} [_ new-groups]]
|
||||
(let [identities (set (keys contact-groups))
|
||||
old-groups-count (count identities)
|
||||
new-groups' (->> new-groups
|
||||
(map #(update-pending-status contact-groups %))
|
||||
(remove #(identities (:group-id %)))
|
||||
(map #(vector (:group-id %2) (assoc %2 :order %1)) (iterate inc old-groups-count))
|
||||
(into {}))]
|
||||
{:db (update db :group/contact-groups merge new-groups')
|
||||
::save-contact-groups (into [] (vals new-groups'))})))
|
||||
|
||||
(register-handler-fx
|
||||
:load-contact-groups
|
||||
[(inject-cofx ::get-all-contact-groups)]
|
||||
(fn [{:keys [db all-groups]} _]
|
||||
{:db (assoc db :group/contact-groups all-groups)}))
|
||||
|
||||
(defn move-item [v from to]
|
||||
(if (< from to)
|
||||
(concat (subvec v 0 from)
|
||||
(subvec v (inc from) (inc to))
|
||||
[(v from)]
|
||||
(subvec v (inc to)))
|
||||
(concat (subvec v 0 to)
|
||||
[(v from)]
|
||||
(subvec v to from)
|
||||
(subvec v (inc from)))))
|
||||
|
||||
(register-handler-db
|
||||
:change-contact-group-order
|
||||
(fn [{:group/keys [groups-order] :as db} [_ from to]]
|
||||
(if (>= to 0)
|
||||
(assoc db :group/groups-order (move-item (vec groups-order) from to))
|
||||
db)))
|
||||
|
||||
(register-handler-fx
|
||||
:save-contact-group-order
|
||||
(fn [{{:group/keys [contact-groups groups-order] :as db} :db} _]
|
||||
(let [new-groups (mapv #(assoc (contact-groups (second %)) :order (first %))
|
||||
(map-indexed vector (reverse groups-order)))]
|
||||
{:db (update db :group/contact-groups merge (map #(vector (:group-id %) %) new-groups))
|
||||
::save-contact-groups new-groups})))
|
||||
|
||||
(register-handler-fx
|
||||
:set-contact-group-name
|
||||
(fn [{{:keys [new-chat-name] :group/keys [contact-group-id] :as db} :db} _]
|
||||
{:db (assoc-in db [:group/contact-groups contact-group-id :name] new-chat-name)
|
||||
::save-contact-group-property [contact-group-id :name new-chat-name]}))
|
||||
|
||||
(register-handler-fx
|
||||
:add-selected-contacts-to-group
|
||||
(fn [{{:group/keys [contact-groups contact-group-id selected-contacts] :as db} :db} _]
|
||||
(let [new-identities (mapv #(hash-map :identity %) selected-contacts)]
|
||||
{:db (update-in db [:group/contact-groups contact-group-id :contacts] #(into [] (set (concat % new-identities))))
|
||||
::add-contacts-to-contact-group [contact-group-id selected-contacts]})))
|
||||
|
||||
(register-handler-fx
|
||||
:add-contacts-to-group
|
||||
(fn [{:keys [db]} [_ group-id contacts]]
|
||||
(let [new-identities (mapv #(hash-map :identity %) contacts)]
|
||||
(when (get-in db [:group/contact-groups group-id])
|
||||
{:db (update-in db [:group/contact-groups group-id :contacts] #(into [] (set (concat % new-identities))))
|
||||
::add-contacts-to-contact-group [group-id contacts]}))))
|
||||
|
||||
(register-handler-fx
|
||||
:delete-contact-group
|
||||
(fn [{{:group/keys [contact-group-id] :as db} :db} _]
|
||||
{:db (assoc-in db [:group/contact-groups contact-group-id :pending?] true)
|
||||
::save-contact-group-property [contact-group-id :pending? true]}))
|
|
@ -0,0 +1,48 @@
|
|||
(ns status-im.group.navigation
|
||||
(:require [status-im.navigation.handlers :as nav]))
|
||||
|
||||
(defn clear-toolbar-search [db]
|
||||
(-> db
|
||||
(assoc-in [:toolbar-search :show] nil)
|
||||
(assoc-in [:toolbar-search :text] "")))
|
||||
|
||||
(defmethod nav/preload-data! :add-contacts-toggle-list
|
||||
[db _]
|
||||
(->
|
||||
(assoc db :group/selected-contacts #{})
|
||||
(clear-toolbar-search)))
|
||||
|
||||
|
||||
(defmethod nav/preload-data! :add-participants-toggle-list
|
||||
[db _]
|
||||
(->
|
||||
(assoc db :selected-participants #{})
|
||||
(clear-toolbar-search)))
|
||||
|
||||
(defmethod nav/preload-data! :new-public-chat
|
||||
[db]
|
||||
(dissoc db :public-group-topic))
|
||||
|
||||
(defmethod nav/preload-data! :group-contacts
|
||||
[db [_ _ group-id show-search?]]
|
||||
(-> db
|
||||
(assoc :group/contact-group-id group-id)
|
||||
(update :toolbar-search
|
||||
assoc
|
||||
:show (when show-search? :contact-list)
|
||||
:text "")))
|
||||
|
||||
(defmethod nav/preload-data! :edit-contact-group
|
||||
[db [_ _ group group-type]]
|
||||
(if group
|
||||
(assoc db :group/contact-group-id (:group-id group)
|
||||
:group/group-type group-type
|
||||
:new-chat-name (:name group))
|
||||
db))
|
||||
|
||||
(defmethod nav/preload-data! :reorder-groups
|
||||
[db [_ _]]
|
||||
(assoc db :group/groups-order (->> (vals (:group/contact-groups db))
|
||||
(remove :pending?)
|
||||
(sort-by :order >)
|
||||
(map :group-id))))
|
|
@ -0,0 +1,60 @@
|
|||
(ns status-im.group.reorder.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [reagent.core :as reagent]
|
||||
[re-frame.core :refer [dispatch dispatch-sync]]
|
||||
[status-im.components.react :refer [view text icon list-item]]
|
||||
[status-im.components.sticky-button :refer [sticky-button]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar]]
|
||||
[status-im.components.sortable-list-view :refer [sortable-list-view sortable-item]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.group.styles :as styles]
|
||||
[status-im.i18n :refer [label label-pluralize]]))
|
||||
|
||||
|
||||
(defn toolbar-view []
|
||||
[toolbar {:actions [{:image :blank}]
|
||||
:title (label :t/reorder-groups)}])
|
||||
|
||||
(defn group-item [{:keys [name contacts] :as group}]
|
||||
(let [cnt (count contacts)]
|
||||
[view styles/order-item-container
|
||||
[view styles/order-item-inner-container
|
||||
[text {:style styles/order-item-label}
|
||||
name]
|
||||
[text {:style styles/order-item-contacts}
|
||||
(str cnt " " (label-pluralize cnt :t/contact-s))]
|
||||
[view {:flex 1}]
|
||||
[view styles/order-item-icon
|
||||
[icon :grab_gray]]]]))
|
||||
|
||||
(defn render-separator [last]
|
||||
(fn [_ row-id _]
|
||||
(list-item
|
||||
(if (= row-id last)
|
||||
^{:key "bottom-shadow"}
|
||||
[common/bottom-shadow]
|
||||
^{:key row-id}
|
||||
[view styles/order-item-separator-wrapper
|
||||
[view styles/order-item-separator]]))))
|
||||
|
||||
(defview reorder-groups []
|
||||
(letsubs [groups [:get-contact-groups]
|
||||
order [:get :group/groups-order]]
|
||||
(let [this (reagent/current-component)]
|
||||
[view styles/reorder-groups-container
|
||||
[status-bar]
|
||||
[toolbar-view]
|
||||
[view styles/reorder-list-container
|
||||
[common/top-shadow]
|
||||
[sortable-list-view
|
||||
{:data groups
|
||||
:order order
|
||||
:on-row-moved #(do (dispatch-sync [:change-contact-group-order (:from %) (:to %)])
|
||||
(.forceUpdate this))
|
||||
:render-row (fn [row]
|
||||
(sortable-item [group-item row]))
|
||||
:render-separator (render-separator (last order))}]]
|
||||
[sticky-button (label :t/save) #(do
|
||||
(dispatch [:save-contact-group-order])
|
||||
(dispatch [:navigate-to-clean :contact-list]))]])))
|
|
@ -1,17 +1,6 @@
|
|||
(ns status-im.new-group.styles
|
||||
(ns status-im.group.styles
|
||||
(:require-macros [status-im.utils.styles :refer [defstyle]])
|
||||
(:require [status-im.components.styles :refer [color-white
|
||||
color-blue
|
||||
color-black
|
||||
text1-color
|
||||
text2-color
|
||||
color-light-blue
|
||||
color-light-red
|
||||
color-light-gray
|
||||
selected-contact-color
|
||||
color-gray4
|
||||
color-gray5]]
|
||||
[status-im.utils.platform :refer [platform-specific] :as p]))
|
||||
(:require [status-im.components.styles :as common]))
|
||||
|
||||
(defn toolbar-icon [enabled?]
|
||||
{:width 20
|
||||
|
@ -21,70 +10,38 @@
|
|||
(def group-container
|
||||
{:flex 1
|
||||
:flex-direction :column
|
||||
:background-color color-white})
|
||||
:background-color common/color-white})
|
||||
|
||||
(def reorder-groups-container
|
||||
{:flex 1
|
||||
:flex-direction :column
|
||||
:background-color color-light-gray})
|
||||
:background-color common/color-light-gray})
|
||||
|
||||
(defstyle reorder-list-container
|
||||
{:flex 1
|
||||
:android {:padding-top 16}})
|
||||
|
||||
(def chat-name-container
|
||||
{:padding-left 16
|
||||
:margin-top 10})
|
||||
|
||||
(def group-name-container
|
||||
{:margin-top 10})
|
||||
|
||||
(def add-button-container
|
||||
{:margin-left 16})
|
||||
|
||||
(def group-chat-name-input
|
||||
{:font-size 17
|
||||
:padding-bottom 0
|
||||
:letter-spacing -0.2
|
||||
:color text1-color})
|
||||
|
||||
(defstyle group-chat-topic-input
|
||||
{:font-size 14
|
||||
:line-height 16
|
||||
:color text1-color
|
||||
:padding-left 13
|
||||
:ios {:padding-bottom 0}})
|
||||
|
||||
(defstyle topic-hash-style
|
||||
{:width 10
|
||||
:position :absolute
|
||||
:android {:top 8 :left 3}
|
||||
:ios {:top 6 :left 3}})
|
||||
|
||||
(def topic-hash
|
||||
(merge group-chat-name-input
|
||||
topic-hash-style))
|
||||
|
||||
(def group-chat-name-wrapper
|
||||
{:padding-top 0
|
||||
:height 40
|
||||
:padding-bottom 0})
|
||||
|
||||
(defstyle group-name-text
|
||||
{:letter-spacing -0.1
|
||||
:color color-gray4
|
||||
:color common/color-gray4
|
||||
:ios {:font-size 13}
|
||||
:android {:font-size 12}})
|
||||
|
||||
(defstyle members-text
|
||||
{:color color-gray4
|
||||
{:color common/color-gray4
|
||||
:ios {:letter-spacing -0.2
|
||||
:font-size 16}
|
||||
:android {:font-size 14}})
|
||||
|
||||
(defstyle members-text-count
|
||||
{:margin-left 8
|
||||
:color color-gray4
|
||||
:color common/color-gray4
|
||||
:opacity 0.6
|
||||
:ios {:letter-spacing -0.2
|
||||
:font-size 16}
|
||||
|
@ -110,8 +67,8 @@
|
|||
:height 24})
|
||||
|
||||
(defstyle add-group-text
|
||||
{:color color-light-blue
|
||||
:ios {:color color-light-blue
|
||||
{:color common/color-light-blue
|
||||
:ios {:color common/color-light-blue
|
||||
:letter-spacing -0.2
|
||||
:font-size 17
|
||||
:line-height 20}
|
||||
|
@ -126,10 +83,10 @@
|
|||
|
||||
(def delete-group-text
|
||||
(merge add-group-text
|
||||
{:color color-light-red}))
|
||||
{:color common/color-light-red}))
|
||||
|
||||
(defstyle delete-group-prompt-text
|
||||
{:color color-gray4
|
||||
{:color common/color-gray4
|
||||
:padding-top 5
|
||||
:ios {:font-size 14
|
||||
:letter-spacing -0.2}
|
||||
|
@ -162,7 +119,7 @@
|
|||
:border-radius 50}})
|
||||
|
||||
(def order-item-container
|
||||
{:background-color color-white})
|
||||
{:background-color common/color-white})
|
||||
|
||||
(defstyle order-item-inner-container
|
||||
{:flex-direction :row
|
||||
|
@ -170,7 +127,7 @@
|
|||
:android {:padding-top 17
|
||||
:padding-bottom 15
|
||||
:min-height 56
|
||||
:background-color color-white}
|
||||
:background-color common/color-white}
|
||||
:ios {:padding-vertical 22
|
||||
:min-height 63}})
|
||||
|
||||
|
@ -178,7 +135,7 @@
|
|||
{:padding-left 16
|
||||
:flex-shrink 1
|
||||
:android {:font-size 16
|
||||
:color color-black
|
||||
:color common/color-black
|
||||
:line-height 24}
|
||||
:ios {:font-size 17
|
||||
:line-height 20
|
||||
|
@ -186,7 +143,7 @@
|
|||
|
||||
(defstyle order-item-contacts
|
||||
{:padding-left 8
|
||||
:color color-gray4
|
||||
:color common/color-gray4
|
||||
:ios {:font-size 17
|
||||
:line-height 20
|
||||
:letter-spacing -0.2}
|
||||
|
@ -198,22 +155,22 @@
|
|||
:ios {:padding-horizontal 20}})
|
||||
|
||||
(def order-item-separator-wrapper
|
||||
{:background-color color-white})
|
||||
{:background-color common/color-white})
|
||||
|
||||
(def order-item-separator
|
||||
{:height 1
|
||||
:background-color color-gray5
|
||||
:background-color common/color-gray5
|
||||
:ios {:margin-left 16
|
||||
:opacity 0.5}})
|
||||
|
||||
(def toolbar-title-with-count-text
|
||||
{:color text1-color
|
||||
{:color common/text1-color
|
||||
:letter-spacing -0.2
|
||||
:font-size 17})
|
||||
|
||||
(def toolbar-title-with-count-text-count
|
||||
(merge toolbar-title-with-count-text
|
||||
{:color color-light-blue}))
|
||||
{:color common/color-light-blue}))
|
||||
|
||||
(def toolbar-title-with-count
|
||||
{:flex-direction :row})
|
||||
|
@ -222,7 +179,7 @@
|
|||
{:padding-left 6})
|
||||
|
||||
(def separator
|
||||
{:background-color color-gray5
|
||||
{:background-color common/color-gray5
|
||||
:height 1
|
||||
:opacity 0.5})
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
(ns status-im.group.subs
|
||||
(:require [re-frame.core :refer [reg-sub subscribe]]
|
||||
[status-im.utils.subs :as utils]))
|
||||
|
||||
(reg-sub
|
||||
:is-contact-selected?
|
||||
(utils/contains-sub :group/selected-contacts))
|
||||
|
||||
(reg-sub
|
||||
:is-participant-selected?
|
||||
(utils/contains-sub :selected-participants))
|
||||
|
||||
(defn filter-selected-contacts [selected-contacts contacts]
|
||||
(remove #(true? (:pending? (contacts %))) selected-contacts))
|
||||
|
||||
(reg-sub
|
||||
:selected-contacts-count
|
||||
:<- [:get :group/selected-contacts]
|
||||
:<- [:get-contacts]
|
||||
(fn [[selected-contacts contacts]]
|
||||
(count (filter-selected-contacts selected-contacts contacts))))
|
||||
|
||||
(reg-sub
|
||||
:selected-participants-count
|
||||
:<- [:get :selected-participants]
|
||||
(fn [selected-participants]
|
||||
(count selected-participants)))
|
||||
|
||||
(defn filter-contacts [selected-contacts added-contacts]
|
||||
(filter #(selected-contacts (:whisper-identity %)) added-contacts))
|
||||
|
||||
(reg-sub
|
||||
:selected-group-contacts
|
||||
:<- [:get :group/selected-contacts]
|
||||
:<- [:all-added-contacts]
|
||||
(fn [[selected-contacts added-contacts]]
|
||||
(filter-contacts selected-contacts added-contacts)))
|
||||
|
||||
(reg-sub
|
||||
:get-contact-groups
|
||||
(fn [db]
|
||||
(:group/contact-groups db)))
|
||||
|
||||
(reg-sub
|
||||
:get-contact-group-id
|
||||
(fn [db]
|
||||
(:group/contact-group-id db)))
|
||||
|
||||
(reg-sub
|
||||
:get-contact-group
|
||||
:<- [:get-contact-groups]
|
||||
:<- [:get-contact-group-id]
|
||||
(fn [[groups group-id]]
|
||||
(get groups group-id)))
|
||||
|
||||
(reg-sub
|
||||
:get-group-type
|
||||
(fn [db]
|
||||
(:group/group-type db)))
|
|
@ -0,0 +1,169 @@
|
|||
(ns status-im.group.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.contacts.styles :as cstyles]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.action-button.action-button :refer [action-button action-separator]]
|
||||
[status-im.components.react :refer [view text icon touchable-highlight
|
||||
keyboard-avoiding-view list-view list-item]]
|
||||
[status-im.components.text-input-with-label.view :refer [text-input-with-label]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar]]
|
||||
[status-im.utils.platform :refer [platform-specific]]
|
||||
[status-im.components.sticky-button :refer [sticky-button]]
|
||||
[status-im.utils.listview :refer [to-datasource]]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.group.styles :as styles]
|
||||
[status-im.i18n :refer [label]]
|
||||
[cljs.spec.alpha :as spec]
|
||||
[status-im.group.db :as v]
|
||||
[status-im.utils.utils :as utils]))
|
||||
|
||||
(defn group-toolbar [group-type edit?]
|
||||
[view
|
||||
[status-bar]
|
||||
[toolbar
|
||||
{:title (label
|
||||
(if (= group-type :contact-group)
|
||||
(if edit? :t/edit-group :t/new-group)
|
||||
(if edit? :t/chat-settings :t/new-group-chat)))
|
||||
:actions [{:image :blank}]}]])
|
||||
|
||||
(defview group-name-view []
|
||||
(letsubs [new-group-name [:get :new-chat-name]]
|
||||
[view styles/group-name-container
|
||||
[text-input-with-label
|
||||
{:auto-focus true
|
||||
:label (label :t/name)
|
||||
:on-change-text #(dispatch [:set :new-chat-name %])
|
||||
:default-value new-group-name}]]))
|
||||
|
||||
(defn add-btn [on-press]
|
||||
[action-button (label :t/add-members)
|
||||
:add_blue
|
||||
on-press])
|
||||
|
||||
(defn delete-btn [on-press]
|
||||
[view styles/settings-group-container
|
||||
[touchable-highlight {:on-press on-press}
|
||||
[view styles/settings-group-item
|
||||
[view styles/delete-icon-container
|
||||
[icon :close_red styles/add-icon]]
|
||||
[view styles/settings-group-text-container
|
||||
[text {:style styles/delete-group-text}
|
||||
(label :t/delete-group)]
|
||||
[text {:style styles/delete-group-prompt-text}
|
||||
(label :t/delete-group-prompt)]]]]])
|
||||
|
||||
(defn group-chat-settings-btns []
|
||||
[view styles/settings-group-container
|
||||
[view {:opacity 0.4}
|
||||
[touchable-highlight {:on-press #()}
|
||||
[view styles/settings-group-item
|
||||
[view styles/settings-icon-container
|
||||
[icon :speaker_blue styles/add-icon]]
|
||||
[view styles/settings-group-text-container
|
||||
[text {:style styles/settings-group-text}
|
||||
(label :t/mute-notifications)]]]]]
|
||||
[action-separator]
|
||||
[action-button (label :t/clear-history)
|
||||
:close_blue
|
||||
#(dispatch [:clear-history])]
|
||||
[action-separator]
|
||||
[touchable-highlight {:on-press #(dispatch [:leave-group-chat])}
|
||||
[view styles/settings-group-item
|
||||
[view styles/delete-icon-container
|
||||
[icon :arrow_right_red styles/add-icon]]
|
||||
[view styles/settings-group-text-container
|
||||
[text {:style styles/delete-group-text}
|
||||
(label :t/leave-chat)]]]]])
|
||||
|
||||
(defn more-btn [contacts-limit contacts-count on-press]
|
||||
[view
|
||||
[common/list-separator]
|
||||
[view cstyles/show-all
|
||||
[touchable-highlight {:on-press on-press}
|
||||
[view
|
||||
[text {:style cstyles/show-all-text
|
||||
:uppercase? (get-in platform-specific [:uppercase?])
|
||||
:font (get-in platform-specific [:component-styles :contacts :show-all-text-font])}
|
||||
(str (- contacts-count contacts-limit) " " (label :t/more))]]]]])
|
||||
|
||||
(def ^:const contacts-limit 3)
|
||||
|
||||
(defview group-contacts-view [group]
|
||||
(letsubs [contacts [:all-added-group-contacts-with-limit (:group-id group) contacts-limit]
|
||||
contacts-count [:all-added-group-contacts-count (:group-id group)]]
|
||||
[view
|
||||
(when (pos? contacts-count)
|
||||
[common/list-separator])
|
||||
[view
|
||||
(doall
|
||||
(map (fn [row]
|
||||
^{:key row}
|
||||
[view
|
||||
[contact-view
|
||||
{:contact row
|
||||
:extend-options [{:value #(dispatch [:remove-contact-from-group
|
||||
(:whisper-identity row)
|
||||
(:group-id group)])
|
||||
:text (label :t/remove-from-group)}]
|
||||
:extended? true}]
|
||||
(when-not (= row (last contacts))
|
||||
[common/list-separator])])
|
||||
contacts))]
|
||||
(when (< contacts-limit contacts-count)
|
||||
[more-btn contacts-limit contacts-count #(dispatch [:navigate-to :edit-group-contact-list])])]))
|
||||
|
||||
(defview edit-contact-group []
|
||||
(letsubs [group-name [:get :new-chat-name]
|
||||
group [:get-contact-group]
|
||||
type [:get-group-type]]
|
||||
(let [save-btn-enabled? (and (spec/valid? ::v/name group-name)
|
||||
(not= group-name (:name group)))]
|
||||
[keyboard-avoiding-view {:style styles/group-container}
|
||||
[group-toolbar type true]
|
||||
[group-name-view]
|
||||
[view styles/list-view-container
|
||||
[add-btn #(dispatch [:navigate-to :add-contacts-toggle-list])]
|
||||
[group-contacts-view group]
|
||||
[view styles/separator]
|
||||
[delete-btn #(utils/show-confirmation
|
||||
(str (label :t/delete-group) "?") (label :t/delete-group-confirmation) (label :t/delete)
|
||||
(fn[]
|
||||
(dispatch [:delete-contact-group])
|
||||
(dispatch [:navigate-to-clean :contact-list])))]]
|
||||
(when save-btn-enabled?
|
||||
[sticky-button (label :t/save) #(dispatch [:set-contact-group-name])])])))
|
||||
|
||||
(defn render-row [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row}]))
|
||||
|
||||
(defview new-group []
|
||||
(letsubs [contacts [:selected-group-contacts]
|
||||
group-name [:get :new-chat-name]
|
||||
group-type [:get-group-type]]
|
||||
(let [save-btn-enabled? (and (spec/valid? ::v/name group-name) (pos? (count contacts)))]
|
||||
[keyboard-avoiding-view (merge {:behavior :padding}
|
||||
styles/group-container)
|
||||
[group-toolbar group-type false]
|
||||
[group-name-view]
|
||||
[view styles/list-view-container
|
||||
[list-view {:dataSource (to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow render-row
|
||||
:bounces false
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderSeparator renderers/list-separator-renderer}]]
|
||||
(when save-btn-enabled?
|
||||
[sticky-button (label :t/save)
|
||||
(if (= group-type :contact-group)
|
||||
#(do
|
||||
(dispatch [:create-new-contact-group group-name])
|
||||
(dispatch [:navigate-to-clean :contact-list]))
|
||||
#(dispatch [:create-new-group-chat-and-open group-name]))
|
||||
true])])))
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
(ns status-im.group-settings.handlers
|
||||
(:require [re-frame.core :refer [dispatch after enrich]]
|
||||
[status-im.utils.handlers :refer [register-handler] :as u]
|
||||
[status-im.chat.handlers :refer [delete-messages!]]
|
||||
[status-im.protocol.core :as protocol]
|
||||
[status-im.utils.random :as random]
|
||||
[status-im.data-store.contacts :as contacts]
|
||||
[status-im.data-store.messages :as messages]
|
||||
[status-im.data-store.chats :as chats]
|
||||
[status-im.constants :refer [text-content-type]]
|
||||
[status-im.navigation.handlers :as nav]))
|
||||
|
||||
(defn save-property!
|
||||
[current-chat-id property-name value]
|
||||
(chats/save-property current-chat-id property-name value))
|
||||
|
||||
(defn save-chat-property!
|
||||
[db-name property-name]
|
||||
(fn [{:keys [current-chat-id] :as db} _]
|
||||
(let [property (db-name db)]
|
||||
(save-property! current-chat-id property-name property))))
|
||||
|
||||
(defn update-chat-property
|
||||
[db-name property-name]
|
||||
(fn [{:keys [current-chat-id] :as db} _]
|
||||
(let [property (db-name db)]
|
||||
(assoc-in db [:chats current-chat-id property-name] property))))
|
||||
|
||||
(defn prepare-chat-settings
|
||||
[{:keys [current-chat-id] :as db}]
|
||||
(let [{:keys [name]} (-> db
|
||||
(get-in [:chats current-chat-id])
|
||||
(select-keys [:name :color]))]
|
||||
(assoc db :new-chat-name name
|
||||
:group-type :chat-group)))
|
||||
|
||||
(register-handler :show-group-settings
|
||||
(after (fn [_ _] (dispatch [:navigate-to :chat-group-settings])))
|
||||
prepare-chat-settings)
|
||||
|
||||
(register-handler :set-chat-name
|
||||
(after (save-chat-property! :new-chat-name :name))
|
||||
(update-chat-property :new-chat-name :name))
|
||||
|
||||
(register-handler :set-chat-color
|
||||
(after (fn [{:keys [current-chat-id]} [_ color]]
|
||||
(save-property! current-chat-id :color (name color))))
|
||||
(fn [{:keys [current-chat-id] :as db} [_ color]]
|
||||
(assoc-in db [:chats current-chat-id :color] color)))
|
||||
|
||||
(defn clear-messages
|
||||
[{:keys [current-chat-id] :as db} _]
|
||||
(-> db
|
||||
(assoc-in [:chats current-chat-id :messages] '())
|
||||
(assoc-in [:chats current-chat-id :last-message] nil)))
|
||||
|
||||
(register-handler :clear-history
|
||||
(after delete-messages!)
|
||||
clear-messages)
|
||||
|
||||
(defn remove-identities [collection identities]
|
||||
(remove #(identities (:identity %)) collection))
|
||||
|
||||
(defn remove-members
|
||||
[{:keys [current-chat-id selected-participants] :as db} _]
|
||||
(update-in db [:chats current-chat-id :contacts]
|
||||
remove-identities selected-participants))
|
||||
|
||||
(defn remove-members-from-chat!
|
||||
[{:keys [current-chat-id selected-participants]} _]
|
||||
(chats/remove-contacts current-chat-id selected-participants))
|
||||
|
||||
(defn notify-about-removing!
|
||||
[{:keys [web3 current-chat-id selected-participants chats current-public-key]} _]
|
||||
(let [{:keys [private public] :as new-keypair} (protocol/new-keypair!)
|
||||
{:keys [name private-key public-key]
|
||||
:as chat} (get chats current-chat-id)
|
||||
old-keypair {:private private-key
|
||||
:public public-key}
|
||||
contacts (get chat :contacts)
|
||||
identities (-> (map :identity contacts)
|
||||
set
|
||||
(clojure.set/difference selected-participants))]
|
||||
(dispatch [:update-chat! {:chat-id current-chat-id
|
||||
:private-key private
|
||||
:public-key public}])
|
||||
(doseq [participant selected-participants]
|
||||
(let [id (random/id)]
|
||||
(doseq [keypair [old-keypair new-keypair]]
|
||||
(protocol/remove-from-group!
|
||||
{:web3 web3
|
||||
:group-id current-chat-id
|
||||
:identity participant
|
||||
:keypair keypair
|
||||
:message {:from current-public-key
|
||||
:message-id id}}))))
|
||||
(protocol/start-watching-group!
|
||||
{:web3 web3
|
||||
:group-id current-chat-id
|
||||
:identity current-public-key
|
||||
:keypair new-keypair
|
||||
:callback #(dispatch [:incoming-message %1 %2])})
|
||||
(protocol/update-group!
|
||||
{:web3 web3
|
||||
:group {:id current-chat-id
|
||||
:name name
|
||||
:contacts (conj identities current-public-key)
|
||||
:admin current-public-key
|
||||
:keypair new-keypair}
|
||||
:identities identities
|
||||
:message {:from current-public-key
|
||||
:message-id (random/id)}})))
|
||||
|
||||
(defn system-message [message-id content]
|
||||
{:from "system"
|
||||
:message-id message-id
|
||||
:content content
|
||||
:content-type text-content-type})
|
||||
|
||||
(defn removed-participant-message [chat-id identity]
|
||||
(let [contact-name (:name (contacts/get-by-id identity))]
|
||||
(->> (str "You've removed " (or contact-name identity))
|
||||
(system-message (random/id))
|
||||
(messages/save chat-id))))
|
||||
|
||||
(defn create-removing-messages!
|
||||
[{:keys [current-chat-id selected-participants]} _]
|
||||
(doseq [participant selected-participants]
|
||||
(removed-participant-message current-chat-id participant)))
|
||||
|
||||
(defn deselect-members [db _]
|
||||
(assoc db :selected-participants #{}))
|
||||
|
||||
(register-handler :remove-participants
|
||||
;; todo check if user have rights to add/remove participants
|
||||
;; todo order of operations tb
|
||||
(u/handlers->
|
||||
remove-members
|
||||
remove-members-from-chat!
|
||||
notify-about-removing!
|
||||
create-removing-messages!
|
||||
deselect-members))
|
||||
|
||||
(defn add-members
|
||||
[{:keys [current-chat-id selected-participants] :as db} _]
|
||||
(let [new-identities (map #(hash-map :identity %) selected-participants)]
|
||||
(update-in db [:chats current-chat-id :contacts] concat new-identities)))
|
||||
|
||||
(defn add-members-to-chat!
|
||||
[{:keys [current-chat-id selected-participants]} _]
|
||||
(chats/add-contacts current-chat-id selected-participants))
|
||||
|
||||
(defn notify-about-new-members!
|
||||
[{:keys [current-chat-id selected-participants
|
||||
current-public-key chats web3]} _]
|
||||
(let [{:keys [name contacts]} (chats current-chat-id)
|
||||
identities (map :identity contacts)
|
||||
|
||||
{:keys [public private]
|
||||
:as new-keypair} (protocol/new-keypair!)
|
||||
|
||||
group-message {:web3 web3
|
||||
:group {:id current-chat-id
|
||||
:name name
|
||||
:contacts (conj identities current-public-key)
|
||||
:admin current-public-key}
|
||||
:message {:from current-public-key
|
||||
:message-id (random/id)}}]
|
||||
(dispatch [:update-chat! {:chat-id current-chat-id
|
||||
:public-key public
|
||||
:private-key private}])
|
||||
(protocol/start-watching-group! {:web3 web3
|
||||
:group-id current-chat-id
|
||||
:identity current-public-key
|
||||
:keypair new-keypair
|
||||
:callback #(dispatch [:incoming-message %1 %2])})
|
||||
(protocol/invite-to-group!
|
||||
(-> group-message
|
||||
(assoc-in [:group :keypair] new-keypair)
|
||||
(assoc :identities selected-participants)))
|
||||
(protocol/update-group!
|
||||
(-> group-message
|
||||
(assoc-in [:group :keypair] new-keypair)
|
||||
(assoc :identities identities)))
|
||||
(doseq [identity selected-participants]
|
||||
(protocol/add-to-group! {:web3 web3
|
||||
:group-id current-chat-id
|
||||
:identity identity
|
||||
:keypair new-keypair
|
||||
:message {:from current-public-key
|
||||
:message-id (random/id)}}))))
|
||||
|
||||
(register-handler :add-new-participants
|
||||
(u/handlers->
|
||||
add-members
|
||||
add-members-to-chat!
|
||||
notify-about-new-members!
|
||||
deselect-members))
|
|
@ -9,11 +9,11 @@
|
|||
[status-im.components.permissions :as permissions]
|
||||
[status-im.utils.handlers :refer [register-handler register-handler-fx] :as u]
|
||||
status-im.chat.handlers
|
||||
status-im.group-settings.handlers
|
||||
status-im.group.chat-settings.events
|
||||
status-im.navigation.handlers
|
||||
status-im.contacts.events
|
||||
status-im.discover.handlers
|
||||
status-im.new-group.handlers
|
||||
status-im.group.events
|
||||
status-im.profile.handlers
|
||||
status-im.commands.handlers.loading
|
||||
status-im.commands.handlers.jail
|
||||
|
@ -79,7 +79,7 @@
|
|||
(dispatch [:initialize-sync-listener])
|
||||
(dispatch [:initialize-chats])
|
||||
(dispatch [:load-contacts])
|
||||
(dispatch [:load-groups])
|
||||
(dispatch [:load-contact-groups])
|
||||
(dispatch [:init-chat])
|
||||
(dispatch [:init-discoveries])
|
||||
(dispatch [:init-debug-mode address])
|
||||
|
|
|
@ -25,17 +25,16 @@
|
|||
[status-im.transactions.screens.unsigned-transactions :refer [unsigned-transactions]]
|
||||
[status-im.transactions.screens.transaction-details :refer [transaction-details]]
|
||||
[status-im.chats-list.screen :refer [chats-list]]
|
||||
[status-im.new-chat.screen :refer [new-chat]]
|
||||
[status-im.new-group.screen-private :refer [new-group
|
||||
edit-group]]
|
||||
[status-im.new-group.views.chat-group-settings :refer [chat-group-settings]]
|
||||
[status-im.new-group.views.contact-list :refer [edit-group-contact-list
|
||||
[status-im.chat.new-chat.view :refer [new-chat]]
|
||||
[status-im.chat.new-public-chat.view :refer [new-public-chat]]
|
||||
[status-im.group.views :refer [new-group edit-contact-group]]
|
||||
[status-im.group.chat-settings.views :refer [chat-group-settings]]
|
||||
[status-im.group.edit-contacts.views :refer [edit-contact-group-contact-list
|
||||
edit-chat-group-contact-list]]
|
||||
[status-im.new-group.views.contact-toggle-list :refer [contact-toggle-list
|
||||
[status-im.group.add-contacts.views :refer [contact-toggle-list
|
||||
add-contacts-toggle-list
|
||||
add-participants-toggle-list]]
|
||||
[status-im.new-group.views.reorder-groups :refer [reorder-groups]]
|
||||
[status-im.new-group.screen-public :refer [new-public-group]]
|
||||
[status-im.group.reorder.views :refer [reorder-groups]]
|
||||
[status-im.profile.screen :refer [profile my-profile]]
|
||||
[status-im.profile.edit.screen :refer [edit-my-profile]]
|
||||
[status-im.profile.photo-capture.screen :refer [profile-photo-capture]]
|
||||
|
@ -97,14 +96,14 @@
|
|||
:chat-list main-tabs
|
||||
:new-chat new-chat
|
||||
:new-group new-group
|
||||
:edit-group edit-group
|
||||
:edit-contact-group edit-contact-group
|
||||
:chat-group-settings chat-group-settings
|
||||
:edit-group-contact-list edit-group-contact-list
|
||||
:edit-group-contact-list edit-contact-group-contact-list
|
||||
:edit-chat-group-contact-list edit-chat-group-contact-list
|
||||
:add-contacts-toggle-list add-contacts-toggle-list
|
||||
:add-participants-toggle-list add-participants-toggle-list
|
||||
:reorder-groups reorder-groups
|
||||
:new-public-group new-public-group
|
||||
:new-public-chat new-public-chat
|
||||
:contact-list main-tabs
|
||||
:contact-toggle-list contact-toggle-list
|
||||
:group-contacts contact-list
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
(ns status-im.new-chat.screen
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.action-button.action-button :refer [action-button
|
||||
action-separator]]
|
||||
[status-im.components.action-button.styles :refer [actions-list]]
|
||||
[status-im.components.react :refer [view text
|
||||
image
|
||||
touchable-highlight
|
||||
list-view
|
||||
list-item]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search]]
|
||||
[status-im.components.drawer.view :refer [drawer-view]]
|
||||
[status-im.new-chat.styles :as st]
|
||||
[status-im.utils.listview :as lw]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(defn options-list []
|
||||
[view actions-list
|
||||
[action-button (label :t/new-group-chat)
|
||||
:private_group_big
|
||||
#(dispatch [:open-contact-toggle-list :chat-group])]
|
||||
[action-separator]
|
||||
[action-button (label :t/new-public-group-chat)
|
||||
:public_group_big
|
||||
#(dispatch [:navigate-to :new-public-group])]
|
||||
[action-separator]
|
||||
[action-button (label :t/add-new-contact)
|
||||
:add_blue
|
||||
#(dispatch [:navigate-to :new-contact])]])
|
||||
|
||||
(defn contact-list-row []
|
||||
(fn [row _ _]
|
||||
(list-item ^{:key row}
|
||||
[contact-view {:contact row
|
||||
:on-press #(dispatch [:open-chat-with-contact %])}])))
|
||||
|
||||
(defview new-chat-toolbar []
|
||||
[show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
[view
|
||||
[status-bar]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-list
|
||||
:title (label :t/contacts-group-new-chat)
|
||||
:search-placeholder (label :t/search-for)})])
|
||||
|
||||
(defview new-chat []
|
||||
[contacts [:all-added-group-contacts-filtered]
|
||||
params [:get :contacts/click-params]]
|
||||
[drawer-view
|
||||
[view st/contacts-list-container
|
||||
[new-chat-toolbar]
|
||||
(when contacts
|
||||
[list-view {:dataSource (lw/to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow (contact-list-row)
|
||||
:bounces false
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderHeader #(list-item
|
||||
[view
|
||||
[options-list]
|
||||
[common/bottom-shadow]
|
||||
[common/form-title (label :t/choose-from-contacts)
|
||||
{:count-value (count contacts)}]
|
||||
[common/list-header]])
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:renderFooter #(list-item [view
|
||||
[common/list-footer]
|
||||
[common/bottom-shadow]])
|
||||
:style st/contacts-list}])]])
|
|
@ -1,375 +0,0 @@
|
|||
(ns status-im.new-group.handlers
|
||||
(:require [status-im.protocol.core :as protocol]
|
||||
[re-frame.core :refer [after dispatch debug enrich]]
|
||||
[status-im.utils.handlers :refer [register-handler] :as u]
|
||||
[status-im.components.styles :refer [default-chat-color]]
|
||||
[status-im.data-store.chats :as chats]
|
||||
[status-im.data-store.contact-groups :as groups]
|
||||
[clojure.string :as s]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.utils.handlers :as u]
|
||||
[status-im.utils.random :as random]
|
||||
[taoensso.timbre :refer-macros [debug]]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.navigation.handlers :as nav]))
|
||||
|
||||
(defn clear-toolbar-search [db]
|
||||
(-> db
|
||||
(assoc-in [:toolbar-search :show] nil)
|
||||
(assoc-in [:toolbar-search :text] "")))
|
||||
|
||||
(defmethod nav/preload-data! :add-contacts-toggle-list
|
||||
[db _]
|
||||
(->
|
||||
(assoc db :selected-contacts #{})
|
||||
(clear-toolbar-search)))
|
||||
|
||||
|
||||
(defmethod nav/preload-data! :add-participants-toggle-list
|
||||
[db _]
|
||||
(->
|
||||
(assoc db :selected-participants #{})
|
||||
(clear-toolbar-search)))
|
||||
|
||||
(defn deselect-contact
|
||||
[db [_ id]]
|
||||
(update db :selected-contacts disj id))
|
||||
|
||||
(register-handler :deselect-contact deselect-contact)
|
||||
|
||||
(defn select-contact
|
||||
[db [_ id]]
|
||||
(update db :selected-contacts conj id))
|
||||
|
||||
(register-handler :select-contact select-contact)
|
||||
|
||||
(defn group-name-from-contacts
|
||||
[{:keys [selected-contacts username]
|
||||
:contacts/keys [contacts]}]
|
||||
(->> (select-keys contacts selected-contacts)
|
||||
vals
|
||||
(map :name)
|
||||
(cons username)
|
||||
(s/join ", ")))
|
||||
|
||||
(defn prepare-chat
|
||||
[{:keys [selected-contacts current-public-key] :as db} [_ group-name]]
|
||||
(let [contacts (mapv #(hash-map :identity %) selected-contacts)
|
||||
chat-name (if-not (s/blank? group-name)
|
||||
group-name
|
||||
(group-name-from-contacts db))
|
||||
{:keys [public private]} (protocol/new-keypair!)]
|
||||
(assoc db :new-chat {:chat-id (random/id)
|
||||
:public-key public
|
||||
:private-key private
|
||||
:name chat-name
|
||||
:color default-chat-color
|
||||
:group-chat true
|
||||
:group-admin current-public-key
|
||||
:is-active true
|
||||
:timestamp (random/timestamp)
|
||||
:contacts contacts})))
|
||||
|
||||
(defn add-chat
|
||||
[{:keys [new-chat] :as db} _]
|
||||
(-> db
|
||||
(assoc-in [:chats (:chat-id new-chat)] new-chat)
|
||||
(assoc :selected-contacts #{})))
|
||||
|
||||
(defn create-chat!
|
||||
[{:keys [new-chat]} _]
|
||||
(chats/save new-chat))
|
||||
|
||||
(defn show-chat!
|
||||
[{:keys [new-chat]} _]
|
||||
(dispatch [:navigate-to-clean :chat-list])
|
||||
(dispatch [:navigate-to :chat (:chat-id new-chat)]))
|
||||
|
||||
(defn start-listen-group!
|
||||
[{:keys [new-chat web3 current-public-key]} _]
|
||||
(let [{:keys [chat-id public-key private-key contacts name]} new-chat
|
||||
identities (mapv :identity contacts)]
|
||||
(protocol/invite-to-group!
|
||||
{:web3 web3
|
||||
:group {:id chat-id
|
||||
:name name
|
||||
:contacts (conj identities current-public-key)
|
||||
:admin current-public-key
|
||||
:keypair {:public public-key
|
||||
:private private-key}}
|
||||
:identities identities
|
||||
:message {:from current-public-key
|
||||
:message-id (random/id)}})
|
||||
(protocol/start-watching-group!
|
||||
{:web3 web3
|
||||
:group-id chat-id
|
||||
:identity current-public-key
|
||||
:keypair {:public public-key
|
||||
:private private-key}
|
||||
:callback #(dispatch [:incoming-message %1 %2])})))
|
||||
|
||||
(register-handler :create-new-group-chat
|
||||
(u/handlers->
|
||||
prepare-chat
|
||||
add-chat
|
||||
create-chat!
|
||||
show-chat!
|
||||
start-listen-group!))
|
||||
|
||||
(register-handler :create-new-public-group
|
||||
(after (fn [_ [_ topic]]
|
||||
(dispatch [:navigate-to-clean :chat-list])
|
||||
(dispatch [:navigate-to :chat topic])))
|
||||
(u/side-effect!
|
||||
(fn [db [_ topic]]
|
||||
(let [exists? (boolean (get-in db [:chats topic]))
|
||||
group {:chat-id topic
|
||||
:name topic
|
||||
:color default-chat-color
|
||||
:group-chat true
|
||||
:public? true
|
||||
:is-active true
|
||||
:timestamp (random/timestamp)}]
|
||||
(when-not exists?
|
||||
(dispatch [::add-public-group group])
|
||||
(dispatch [::save-public-group group])
|
||||
(dispatch [::start-watching-group topic]))))))
|
||||
|
||||
(register-handler ::add-public-group
|
||||
(fn [db [_ {:keys [chat-id] :as group}]]
|
||||
(assoc-in db [:chats chat-id] group)))
|
||||
|
||||
(register-handler ::save-public-group
|
||||
(u/side-effect!
|
||||
(fn [_ [_ group]]
|
||||
(chats/save group))))
|
||||
|
||||
(register-handler ::start-watching-group
|
||||
(u/side-effect!
|
||||
(fn [{:keys [web3 current-public-key]} [_ topic]]
|
||||
(protocol/start-watching-group!
|
||||
{:web3 web3
|
||||
:group-id topic
|
||||
:identity current-public-key
|
||||
:callback #(dispatch [:incoming-message %1 %2])}))))
|
||||
|
||||
(register-handler :group-chat-invite-received
|
||||
(u/side-effect!
|
||||
(fn [{:keys [current-public-key web3]}
|
||||
[_ {:keys [from]
|
||||
{:keys [group-id group-name contacts keypair timestamp]} :payload}]]
|
||||
(let [{:keys [private public]} keypair]
|
||||
(let [contacts' (keep (fn [ident]
|
||||
(when (not= ident current-public-key)
|
||||
{:identity ident})) contacts)
|
||||
chat {:chat-id group-id
|
||||
:name group-name
|
||||
:group-chat true
|
||||
:group-admin from
|
||||
:public-key public
|
||||
:private-key private
|
||||
:contacts contacts'
|
||||
:added-to-at timestamp
|
||||
:timestamp timestamp
|
||||
:is-active true}
|
||||
|
||||
exists? (chats/exists? group-id)]
|
||||
(when (or (not exists?) (chats/new-update? timestamp group-id))
|
||||
(if exists?
|
||||
(dispatch [:update-chat! chat])
|
||||
(dispatch [:add-chat group-id chat]))
|
||||
(protocol/start-watching-group!
|
||||
{:web3 web3
|
||||
:group-id group-id
|
||||
:identity current-public-key
|
||||
:keypair keypair
|
||||
:callback #(dispatch [:incoming-message %1 %2])})))))))
|
||||
|
||||
(defn prepare-group
|
||||
[{:keys [selected-contacts contact-groups] :as db} [_ group-name]]
|
||||
(let [contacts (mapv #(hash-map :identity %) selected-contacts)]
|
||||
(assoc db :new-group {:group-id (random/id)
|
||||
:name group-name
|
||||
:order (count contact-groups)
|
||||
:timestamp (random/timestamp)
|
||||
:contacts contacts})))
|
||||
|
||||
(defn add-group
|
||||
[{:keys [new-group] :as db} _]
|
||||
(update db :contact-groups merge {(:group-id new-group) new-group}))
|
||||
|
||||
(defn update-group
|
||||
[{:keys [new-group] :as db} _]
|
||||
(update db :contact-groups merge {(:group-id new-group) new-group}))
|
||||
|
||||
(defn save-group!
|
||||
[{:keys [new-group]} _]
|
||||
(groups/save new-group))
|
||||
|
||||
(defn show-contact-list!
|
||||
[_ _]
|
||||
(dispatch [:navigate-to-clean :contact-list]))
|
||||
|
||||
(register-handler :create-new-group
|
||||
(u/handlers->
|
||||
prepare-group
|
||||
add-group
|
||||
save-group!
|
||||
show-contact-list!))
|
||||
|
||||
(defn update-new-group
|
||||
[db [_ new-group]]
|
||||
(assoc db :new-group new-group))
|
||||
|
||||
(register-handler :update-group
|
||||
(u/handlers->
|
||||
update-new-group
|
||||
update-group
|
||||
save-group!))
|
||||
|
||||
(defn save-groups! [{:keys [new-groups]} _]
|
||||
(groups/save-all new-groups))
|
||||
|
||||
(defn update-pending-status [old-groups {:keys [group-id pending?] :as group}]
|
||||
(let [{old-pending :pending?
|
||||
:as old-group} (get old-groups group-id)
|
||||
pending?' (if old-pending (and old-pending pending?) pending?)]
|
||||
(assoc group :pending? (boolean pending?'))))
|
||||
|
||||
(defn add-new-groups
|
||||
[{:keys [contact-groups] :as db} [_ new-groups]]
|
||||
(let [identities (set (keys contact-groups))
|
||||
old-groups-count (count identities)
|
||||
new-groups' (->> new-groups
|
||||
(map #(update-pending-status contact-groups %))
|
||||
(remove #(identities (:group-id %)))
|
||||
(map #(vector (:group-id %2) (assoc %2 :order %1)) (iterate inc old-groups-count))
|
||||
(into {}))]
|
||||
(-> db
|
||||
(update :contact-groups merge new-groups')
|
||||
(assoc :new-groups (into [] (vals new-groups'))))))
|
||||
|
||||
(register-handler :add-groups
|
||||
(after save-groups!)
|
||||
add-new-groups)
|
||||
|
||||
(defn load-groups! [db _]
|
||||
(let [groups (->> (groups/get-all)
|
||||
(map (fn [{:keys [group-id] :as group}]
|
||||
[group-id group]))
|
||||
(into {}))]
|
||||
(assoc db :contact-groups groups)))
|
||||
|
||||
(register-handler :load-groups load-groups!)
|
||||
|
||||
(defmethod nav/preload-data! :new-public-group
|
||||
[db]
|
||||
(dissoc db :public-group-topic))
|
||||
|
||||
(defn move-item [v from to]
|
||||
(if (< from to)
|
||||
(concat (subvec v 0 from)
|
||||
(subvec v (inc from) (inc to))
|
||||
[(v from)]
|
||||
(subvec v (inc to)))
|
||||
(concat (subvec v 0 to)
|
||||
[(v from)]
|
||||
(subvec v to from)
|
||||
(subvec v (inc from)))))
|
||||
|
||||
(register-handler :change-group-order
|
||||
(fn [{:keys [groups-order] :as db} [_ from to]]
|
||||
(if (>= to 0)
|
||||
(assoc db :groups-order (move-item (vec groups-order) from to))
|
||||
db)))
|
||||
|
||||
(register-handler :update-groups
|
||||
(after save-groups!)
|
||||
(fn [db [_ new-groups]]
|
||||
(-> db
|
||||
(update :contact-groups merge (map #(vector (:group-id %) %) new-groups))
|
||||
(assoc :new-groups new-groups))))
|
||||
|
||||
(register-handler :save-group-order
|
||||
(u/side-effect!
|
||||
(fn [{:keys [groups-order contact-groups] :as db} _]
|
||||
(let [new-groups (mapv #(assoc (contact-groups (second %)) :order (first %))
|
||||
(map-indexed vector (reverse groups-order)))]
|
||||
(dispatch [:update-groups new-groups])
|
||||
(dispatch [:navigate-to-clean :contact-list])))))
|
||||
|
||||
(defn save-property!
|
||||
[contact-group-id property-name value]
|
||||
(groups/save-property contact-group-id property-name value))
|
||||
|
||||
(defn save-group-property!
|
||||
[db-name property-name]
|
||||
(fn [{:keys [contact-group-id] :as db} _]
|
||||
(let [property (db-name db)]
|
||||
(save-property! contact-group-id property-name property))))
|
||||
|
||||
(defn update-group-property
|
||||
[db-name property-name]
|
||||
(fn [{:keys [contact-group-id] :as db} _]
|
||||
(let [property (db-name db)]
|
||||
(assoc-in db [:contact-groups contact-group-id property-name] property))))
|
||||
|
||||
(register-handler :set-group-name
|
||||
(after (save-group-property! :new-chat-name :name))
|
||||
(update-group-property :new-chat-name :name))
|
||||
|
||||
(defn add-selected-contacts-to-group
|
||||
[{:keys [selected-contacts contact-groups contact-group-id] :as db} _]
|
||||
(let [new-identities (mapv #(hash-map :identity %) selected-contacts)]
|
||||
(update-in db [:contact-groups contact-group-id :contacts] #(into [] (set (concat % new-identities))))))
|
||||
|
||||
(defn add-selected-contacts-to-group!
|
||||
[{:keys [contact-group-id selected-contacts]} _]
|
||||
(groups/add-contacts contact-group-id selected-contacts))
|
||||
|
||||
(register-handler :add-selected-contacts-to-group
|
||||
(after add-selected-contacts-to-group!)
|
||||
add-selected-contacts-to-group)
|
||||
|
||||
(defn add-contacts-to-group
|
||||
[db [_ group-id contacts]]
|
||||
(let [new-identities (mapv #(hash-map :identity %) contacts)]
|
||||
(if (get-in db [:contact-groups group-id])
|
||||
(update-in db [:contact-groups group-id :contacts] #(into [] (set (concat % new-identities))))
|
||||
db)))
|
||||
|
||||
(defn add-contacts-to-group!
|
||||
[db [_ group-id contacts]]
|
||||
(when (get-in db [:contact-groups group-id])
|
||||
(groups/add-contacts group-id contacts)))
|
||||
|
||||
(register-handler :add-contacts-to-group
|
||||
(after add-contacts-to-group!)
|
||||
add-contacts-to-group)
|
||||
|
||||
(defn delete-group []
|
||||
(fn [{:keys [contact-group-id] :as db} _]
|
||||
(assoc-in db [:contact-groups contact-group-id :pending?] true)))
|
||||
|
||||
(defn delete-group! []
|
||||
(fn [{:keys [contact-group-id]} _]
|
||||
(save-property! contact-group-id :pending? true)))
|
||||
|
||||
(register-handler :delete-group
|
||||
(after (delete-group!))
|
||||
(delete-group))
|
||||
|
||||
(defn deselect-participant
|
||||
[db [_ id]]
|
||||
(update db :selected-participants disj id))
|
||||
|
||||
(register-handler :deselect-participant deselect-participant)
|
||||
|
||||
(defn select-participant
|
||||
[db [_ id]]
|
||||
(update db :selected-participants conj id))
|
||||
|
||||
(register-handler :select-participant select-participant)
|
||||
|
||||
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
(ns status-im.new-group.screen-private
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.react :refer [view
|
||||
scroll-view
|
||||
keyboard-avoiding-view
|
||||
list-view
|
||||
list-item]]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.sticky-button :refer [sticky-button]]
|
||||
[status-im.utils.utils :as u]
|
||||
[status-im.utils.listview :refer [to-datasource]]
|
||||
[status-im.new-group.styles :as st]
|
||||
[status-im.new-group.views.group :refer [group-toolbar
|
||||
group-chat-settings-btns
|
||||
group-name-view
|
||||
add-btn
|
||||
more-btn
|
||||
delete-btn]]
|
||||
[status-im.new-group.validations :as v]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.utils.platform :refer [ios?]]
|
||||
[cljs.spec.alpha :as s]))
|
||||
|
||||
(def contacts-limit 3)
|
||||
|
||||
(defview group-contacts-view [group]
|
||||
[contacts [:all-added-group-contacts-with-limit (:group-id group) contacts-limit]
|
||||
contacts-count [:all-added-group-contacts-count (:group-id group)]]
|
||||
[view
|
||||
(when (pos? contacts-count)
|
||||
[common/list-separator])
|
||||
[view
|
||||
(doall
|
||||
(map (fn [row]
|
||||
^{:key row}
|
||||
[view
|
||||
[contact-view
|
||||
{:contact row
|
||||
:extend-options [{:value #(dispatch [:remove-contact-from-group
|
||||
(:whisper-identity row)
|
||||
(:group-id group)])
|
||||
:text (label :t/remove-from-group)}]
|
||||
:extended? true}]
|
||||
(when-not (= row (last contacts))
|
||||
[common/list-separator])])
|
||||
contacts))]
|
||||
(when (< contacts-limit contacts-count)
|
||||
[more-btn contacts-limit contacts-count #(dispatch [:navigate-to :edit-group-contact-list])])])
|
||||
|
||||
(defview edit-group []
|
||||
[group-name [:get :new-chat-name]
|
||||
group [:get-contact-group]
|
||||
type [:get :group-type]]
|
||||
(let [save-btn-enabled? (and (s/valid? ::v/name group-name)
|
||||
(not= group-name (:name group)))]
|
||||
[keyboard-avoiding-view {:style st/group-container}
|
||||
[group-toolbar type true]
|
||||
[group-name-view]
|
||||
[view st/list-view-container
|
||||
[add-btn #(dispatch [:navigate-to :add-contacts-toggle-list])]
|
||||
[group-contacts-view group]
|
||||
[view st/separator]
|
||||
[delete-btn #(u/show-confirmation
|
||||
(str (label :t/delete-group) "?") (label :t/delete-group-confirmation) (label :t/delete)
|
||||
(fn[]
|
||||
(dispatch [:delete-group])
|
||||
(dispatch [:navigate-to-clean :contact-list])))]]
|
||||
(when save-btn-enabled?
|
||||
[sticky-button (label :t/save) #(dispatch [:set-group-name])])]))
|
||||
|
||||
(defn render-row [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row}]))
|
||||
|
||||
(defview new-group []
|
||||
[contacts [:selected-group-contacts]
|
||||
group-name [:get :new-chat-name]
|
||||
group-type [:get :group-type]]
|
||||
(let [save-btn-enabled? (and (s/valid? ::v/name group-name) (pos? (count contacts)))]
|
||||
[(if ios? keyboard-avoiding-view view) (merge {:behavior :padding}
|
||||
st/group-container)
|
||||
[group-toolbar group-type false]
|
||||
[group-name-view]
|
||||
[view st/list-view-container
|
||||
[list-view {:dataSource (to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow render-row
|
||||
:bounces false
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderSeparator renderers/list-separator-renderer}]]
|
||||
(when save-btn-enabled?
|
||||
[sticky-button (label :t/save)
|
||||
(if (= group-type :contact-group)
|
||||
#(dispatch [:create-new-group group-name])
|
||||
#(dispatch [:create-new-group-chat group-name]))
|
||||
;; once? set to true, so once component is mounted, on-press handler
|
||||
;; will be executed only once
|
||||
true])]))
|
|
@ -1,67 +0,0 @@
|
|||
(ns status-im.new-group.screen-public
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [subscribe dispatch]]
|
||||
[status-im.react-native.resources :as res]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
image
|
||||
icon
|
||||
touchable-highlight
|
||||
list-view
|
||||
list-item]]
|
||||
[status-im.components.text-field.view :refer [text-field]]
|
||||
[status-im.components.styles :refer [icon-ok
|
||||
color-blue
|
||||
color-gray4]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar.view :refer [toolbar]]
|
||||
[status-im.utils.listview :refer [to-datasource]]
|
||||
[status-im.new-group.styles :as st]
|
||||
[status-im.new-group.validations :as v]
|
||||
[status-im.i18n :refer [label]]
|
||||
[cljs.spec.alpha :as s]))
|
||||
|
||||
(defview new-group-toolbar []
|
||||
[topic [:get :public-group-topic]]
|
||||
(let [create-btn-enabled? (s/valid? ::v/topic topic)]
|
||||
[view
|
||||
[status-bar]
|
||||
[toolbar
|
||||
{:title (label :t/new-public-group-chat)
|
||||
:actions [{:image {:source {:uri (if create-btn-enabled?
|
||||
:icon_ok_blue
|
||||
:icon_ok_disabled)}
|
||||
:style icon-ok}
|
||||
:handler (when create-btn-enabled?
|
||||
#(dispatch [:create-new-public-group topic]))}]}]]))
|
||||
|
||||
(defview group-name-input []
|
||||
[topic [:get :public-group-topic]]
|
||||
[view
|
||||
[text-field
|
||||
{:error (cond
|
||||
(not (s/valid? ::v/not-empty-string topic))
|
||||
(label :t/empty-topic)
|
||||
|
||||
(not (s/valid? ::v/topic topic))
|
||||
(label :t/topic-format))
|
||||
:wrapper-style st/group-chat-name-wrapper
|
||||
:error-color color-blue
|
||||
:line-color color-gray4
|
||||
:label-hidden? true
|
||||
:input-style st/group-chat-topic-input
|
||||
:auto-focus true
|
||||
:on-change-text #(dispatch [:set :public-group-topic %])
|
||||
:value topic
|
||||
:validator #(re-matches #"[a-z\-]*" %)
|
||||
:auto-capitalize :none}]
|
||||
[text {:style st/topic-hash} "#"]])
|
||||
|
||||
(defn new-public-group []
|
||||
[view st/group-container
|
||||
[new-group-toolbar]
|
||||
[view st/chat-name-container
|
||||
[text {:style st/members-text
|
||||
:font :medium}
|
||||
(label :t/public-group-topic)]
|
||||
[group-name-input]]])
|
|
@ -1,11 +0,0 @@
|
|||
(ns status-im.new-group.specs
|
||||
(:require [cljs.spec.alpha :as s]))
|
||||
|
||||
(s/def :group/contact-groups (s/nilable map?)) ;; {id (string) group (map)}
|
||||
(s/def :group/contact-group-id (s/nilable string?)) ;;used during editing contact group
|
||||
(s/def :group/group-type (s/nilable keyword?)) ;;contact group or chat group
|
||||
(s/def :group/new-group (s/nilable map?)) ;;used during creating or edeting contact group
|
||||
(s/def :group/new-groups (s/nilable vector?)) ;;used during creating or edeting contact groups
|
||||
(s/def :group/contacts-group (s/nilable map?))
|
||||
(s/def :group/selected-contacts (s/nilable set?))
|
||||
(s/def :group/groups-order (s/nilable seq?)) ;;list of group ids
|
|
@ -1,37 +0,0 @@
|
|||
(ns status-im.new-group.subs
|
||||
(:require [re-frame.core :refer [reg-sub subscribe]]
|
||||
[status-im.utils.subs :as u]))
|
||||
|
||||
(reg-sub :is-contact-selected?
|
||||
(u/contains-sub :selected-contacts))
|
||||
|
||||
(reg-sub :is-participant-selected?
|
||||
(u/contains-sub :selected-participants))
|
||||
|
||||
(defn filter-selected-contacts [selected-contacts contacts]
|
||||
(remove #(true? (:pending? (contacts %))) selected-contacts))
|
||||
|
||||
(reg-sub :selected-contacts-count
|
||||
:<- [:get :selected-contacts]
|
||||
:<- [:get-contacts]
|
||||
(fn [[selected-contacts contacts]]
|
||||
;TODO temporary, contact should be deleted from group after contact deletion from contacts
|
||||
(count (filter-selected-contacts selected-contacts contacts))))
|
||||
|
||||
(reg-sub :selected-participants-count
|
||||
:<- [:get :selected-participants]
|
||||
(fn [selected-participants]
|
||||
(count selected-participants)))
|
||||
|
||||
(defn filter-contacts [selected-contacts added-contacts]
|
||||
(filter #(selected-contacts (:whisper-identity %)) added-contacts))
|
||||
|
||||
(reg-sub :selected-group-contacts
|
||||
:<- [:get :selected-contacts]
|
||||
:<- [:all-added-contacts]
|
||||
(fn [[selected-contacts added-contacts]]
|
||||
(filter-contacts selected-contacts added-contacts)))
|
||||
|
||||
(reg-sub :get-contact-group
|
||||
(fn [db]
|
||||
((:contact-groups db) (:contact-group-id db))))
|
|
@ -1,21 +0,0 @@
|
|||
(ns status-im.new-group.validations
|
||||
(:require [cljs.spec.alpha :as s]
|
||||
[status-im.utils.phone-number :refer [valid-mobile-number?]]
|
||||
[status-im.constants :refer [console-chat-id wallet-chat-id]]
|
||||
[clojure.string :as str]
|
||||
[status-im.utils.homoglyph :as h]))
|
||||
|
||||
(defn not-illegal-name? [username]
|
||||
(let [username (some-> username (str/trim))]
|
||||
(and (not (h/matches username console-chat-id))
|
||||
(not (h/matches username wallet-chat-id)))))
|
||||
|
||||
(s/def ::not-empty-string (s/and string? not-empty))
|
||||
(s/def ::not-illegal-name not-illegal-name?)
|
||||
|
||||
(s/def ::name (s/and ::not-empty-string
|
||||
::not-illegal-name))
|
||||
|
||||
(s/def ::topic (s/and ::not-empty-string
|
||||
::not-illegal-name
|
||||
(partial re-matches #"[a-z0-9\-]+")))
|
|
@ -1,78 +0,0 @@
|
|||
(ns status-im.new-group.views.chat-group-settings
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.react :refer [view
|
||||
scroll-view
|
||||
keyboard-avoiding-view
|
||||
icon
|
||||
touchable-highlight]]
|
||||
[status-im.components.sticky-button :refer [sticky-button]]
|
||||
[status-im.new-group.styles :as st]
|
||||
[status-im.new-group.views.group :refer [group-toolbar
|
||||
group-chat-settings-btns
|
||||
group-name-view
|
||||
add-btn
|
||||
more-btn
|
||||
delete-btn]]
|
||||
[status-im.new-group.validations :as v]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.utils.platform :refer [ios?]]
|
||||
[cljs.spec.alpha :as s]))
|
||||
|
||||
(def contacts-limit 3)
|
||||
|
||||
(defview chat-group-contacts-view [admin?]
|
||||
[contacts [:current-chat-contacts]]
|
||||
(let [limited-contacts (take contacts-limit contacts)
|
||||
contacts-count (count contacts)]
|
||||
[view
|
||||
(when (and admin? (pos? contacts-count))
|
||||
[common/list-separator])
|
||||
[view
|
||||
(doall
|
||||
(map (fn [row]
|
||||
^{:key row}
|
||||
[view
|
||||
[contact-view
|
||||
{:contact row
|
||||
:extend-options [{:value #(do
|
||||
(dispatch [:set :selected-participants #{(:whisper-identity row)}])
|
||||
(dispatch [:remove-participants]))
|
||||
:text (label :t/remove)}]
|
||||
:extended? admin?}]
|
||||
(when-not (= row (last limited-contacts))
|
||||
[common/list-separator])])
|
||||
limited-contacts))]
|
||||
(when (< contacts-limit contacts-count)
|
||||
[more-btn contacts-limit contacts-count #(dispatch [:navigate-to :edit-chat-group-contact-list])])]))
|
||||
|
||||
(defview chat-group-members []
|
||||
[current-pk [:get :current-public-key]
|
||||
group-admin [:chat :group-admin]]
|
||||
(let [admin? (= current-pk group-admin)]
|
||||
[view
|
||||
(when admin?
|
||||
[add-btn #(dispatch [:navigate-to :add-participants-toggle-list])])
|
||||
[chat-group-contacts-view admin?]]))
|
||||
|
||||
(defview chat-group-settings []
|
||||
[new-chat-name [:get :new-chat-name]
|
||||
chat-name [:chat :name]
|
||||
type [:get :group-type]]
|
||||
(let [save-btn-enabled? (and (s/valid? ::v/name new-chat-name)
|
||||
(not= new-chat-name chat-name))]
|
||||
[keyboard-avoiding-view {:style st/group-container}
|
||||
[view {:flex 1}
|
||||
[group-toolbar type true]
|
||||
[scroll-view
|
||||
[group-name-view]
|
||||
[chat-group-members]
|
||||
[view st/separator]
|
||||
[group-chat-settings-btns]]]
|
||||
(when save-btn-enabled?
|
||||
[sticky-button (label :t/save) #(dispatch [:set-chat-name])
|
||||
;; once? set to true, so once component is mounted, on-press handler
|
||||
;; will be executed only once
|
||||
true])]))
|
|
@ -1,78 +0,0 @@
|
|||
(ns status-im.new-group.views.contact-list
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
list-view
|
||||
list-item]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search]]
|
||||
[status-im.utils.listview :refer [to-datasource]]
|
||||
[status-im.new-group.styles :as st]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(defview contact-list-toolbar [title]
|
||||
[show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-list
|
||||
:title title
|
||||
:search-placeholder (label :t/search-contacts)}))
|
||||
|
||||
(defn contacts-list [contacts renderer-function]
|
||||
[view {:flex 1}
|
||||
[list-view {:dataSource (to-datasource contacts)
|
||||
:enableEmptySections true
|
||||
:renderRow renderer-function
|
||||
:bounces false
|
||||
:keyboardShouldPersistTaps :always
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:renderFooter renderers/list-footer-renderer
|
||||
:renderHeader renderers/list-header-renderer}]])
|
||||
|
||||
(defview chat-contacts-list-view []
|
||||
[contacts [:contacts-filtered :current-chat-contacts]
|
||||
current-pk [:get :current-public-key]
|
||||
group-admin [:chat :group-admin]]
|
||||
(let [admin? (= current-pk group-admin)]
|
||||
[contacts-list contacts (fn [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row
|
||||
:extended? admin?
|
||||
:extend-options [{:value #(do
|
||||
(dispatch [:set :selected-participants
|
||||
#{(:whisper-identity row)}])
|
||||
(dispatch [:remove-participants]))
|
||||
:text (label :t/remove)}]}]))]))
|
||||
|
||||
(defview contacts-list-view [group]
|
||||
[contacts [:all-added-group-contacts-filtered (:group-id group)]]
|
||||
[contacts-list contacts (fn [row _ _]
|
||||
(list-item
|
||||
^{:key row}
|
||||
[contact-view {:contact row
|
||||
:extended? true
|
||||
:extend-options [{:value #(dispatch [:remove-contact-from-group
|
||||
(:whisper-identity row)
|
||||
(:group-id group)])
|
||||
:text (label :t/remove-from-group)}]}]))])
|
||||
|
||||
(defview edit-chat-group-contact-list []
|
||||
[chat-name [:chat :name]]
|
||||
[view st/group-container
|
||||
[status-bar]
|
||||
[contact-list-toolbar chat-name]
|
||||
[chat-contacts-list-view]])
|
||||
|
||||
(defview edit-group-contact-list []
|
||||
[group [:get-contact-group]
|
||||
type [:get :group-type]]
|
||||
[view st/group-container
|
||||
[status-bar]
|
||||
[contact-list-toolbar (:name group)]
|
||||
[contacts-list-view group]])
|
|
@ -1,95 +0,0 @@
|
|||
(ns status-im.new-group.views.contact-toggle-list
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.contact.contact :refer [contact-view]]
|
||||
[status-im.components.renderers.renderers :as renderers]
|
||||
[status-im.components.react :refer [view
|
||||
keyboard-avoiding-view
|
||||
text
|
||||
list-view
|
||||
list-item]]
|
||||
[status-im.components.sticky-button :refer [sticky-button]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar-with-search]]
|
||||
[status-im.utils.listview :refer [to-datasource]]
|
||||
[status-im.utils.platform :refer [ios?]]
|
||||
[status-im.new-group.views.toggle-contact :refer [group-toggle-contact
|
||||
group-toggle-participant]]
|
||||
[status-im.new-group.styles :as st]
|
||||
[status-im.contacts.styles :as cst]
|
||||
[status-im.i18n :refer [label]]
|
||||
[status-im.components.toolbar-new.actions :as act]))
|
||||
|
||||
(defn title-with-count [title count-value]
|
||||
[view st/toolbar-title-with-count
|
||||
[text {:style st/toolbar-title-with-count-text
|
||||
:font :toolbar-title}
|
||||
title]
|
||||
(when (pos? count-value)
|
||||
[view st/toolbar-title-with-count-container
|
||||
[text {:style st/toolbar-title-with-count-text-count
|
||||
:font :toolbar-title}
|
||||
count-value]])])
|
||||
|
||||
(defview toggle-list-toolbar [title contacts-count]
|
||||
[show-search [:get-in [:toolbar-search :show]]
|
||||
search-text [:get-in [:toolbar-search :text]]]
|
||||
(toolbar-with-search
|
||||
{:show-search? (= show-search :contact-group-list)
|
||||
:search-text search-text
|
||||
:search-key :contact-group-list
|
||||
:custom-title (title-with-count title contacts-count)
|
||||
:search-placeholder (label :t/search-contacts)}))
|
||||
|
||||
(defn toggle-list [contacts render-function]
|
||||
[view {:flex 1}
|
||||
[list-view
|
||||
{:dataSource (to-datasource contacts)
|
||||
:renderRow (fn [row _ _]
|
||||
(list-item ^{:key row} [render-function row]))
|
||||
:renderSeparator renderers/list-separator-renderer
|
||||
:renderFooter renderers/list-footer-renderer
|
||||
:renderHeader renderers/list-header-renderer
|
||||
:style cst/contacts-list
|
||||
:keyboardShouldPersistTaps :always}]])
|
||||
|
||||
(defview contact-toggle-list []
|
||||
[contacts [:all-added-group-contacts-filtered]
|
||||
selected-contacts-count [:selected-contacts-count]
|
||||
group-type [:get :group-type]]
|
||||
[keyboard-avoiding-view {:style st/group-container}
|
||||
[status-bar]
|
||||
[toggle-list-toolbar
|
||||
(label (if (= group-type :contact-group)
|
||||
:t/new-group
|
||||
:t/new-group-chat))
|
||||
selected-contacts-count]
|
||||
[toggle-list contacts group-toggle-contact]
|
||||
(when (pos? selected-contacts-count)
|
||||
[sticky-button (label :t/next) #(dispatch [:navigate-to :new-group])])])
|
||||
|
||||
(defview add-contacts-toggle-list []
|
||||
[contacts [:all-group-not-added-contacts-filtered]
|
||||
group [:get-contact-group]
|
||||
selected-contacts-count [:selected-contacts-count]]
|
||||
[keyboard-avoiding-view {:style st/group-container}
|
||||
[status-bar]
|
||||
[toggle-list-toolbar (:name group) selected-contacts-count]
|
||||
[toggle-list contacts group-toggle-contact]
|
||||
(when (pos? selected-contacts-count)
|
||||
[sticky-button (label :t/save) #(do
|
||||
(dispatch [:add-selected-contacts-to-group])
|
||||
(dispatch [:navigate-back]))])])
|
||||
|
||||
(defview add-participants-toggle-list []
|
||||
[contacts [:contacts-filtered :all-new-contacts]
|
||||
chat-name [:chat :name]
|
||||
selected-contacts-count [:selected-participants-count]]
|
||||
[keyboard-avoiding-view {:style st/group-container}
|
||||
[status-bar]
|
||||
[toggle-list-toolbar chat-name selected-contacts-count]
|
||||
[toggle-list contacts group-toggle-participant]
|
||||
(when (pos? selected-contacts-count)
|
||||
[sticky-button (label :t/save) #(do
|
||||
(dispatch [:add-new-participants])
|
||||
(dispatch [:navigate-back]))])])
|
|
@ -1,90 +0,0 @@
|
|||
(ns status-im.new-group.views.group
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.contacts.styles :as cst]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.components.action-button.action-button :refer [action-button
|
||||
action-button-disabled
|
||||
action-separator]]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
icon
|
||||
touchable-highlight]]
|
||||
[status-im.components.text-input-with-label.view :refer [text-input-with-label]]
|
||||
[status-im.components.styles :refer [color-blue color-gray5 color-light-blue]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar]]
|
||||
[status-im.utils.platform :refer [platform-specific]]
|
||||
[status-im.new-group.styles :as st]
|
||||
[status-im.i18n :refer [label]]))
|
||||
|
||||
(defn group-toolbar [group-type edit?]
|
||||
[view
|
||||
[status-bar]
|
||||
[toolbar
|
||||
{:title (label
|
||||
(if (= group-type :contact-group)
|
||||
(if edit? :t/edit-group :t/new-group)
|
||||
(if edit? :t/chat-settings :t/new-group-chat)))
|
||||
:actions [{:image :blank}]}]])
|
||||
|
||||
(defview group-name-view []
|
||||
[new-group-name [:get :new-chat-name]]
|
||||
[view st/group-name-container
|
||||
[text-input-with-label
|
||||
{:auto-focus true
|
||||
:label (label :t/name)
|
||||
:on-change-text #(dispatch [:set :new-chat-name %])
|
||||
:default-value new-group-name}]])
|
||||
|
||||
(defn add-btn [on-press]
|
||||
[action-button (label :t/add-members)
|
||||
:add_blue
|
||||
on-press])
|
||||
|
||||
(defn delete-btn [on-press]
|
||||
[view st/settings-group-container
|
||||
[touchable-highlight {:on-press on-press}
|
||||
[view st/settings-group-item
|
||||
[view st/delete-icon-container
|
||||
[icon :close_red st/add-icon]]
|
||||
[view st/settings-group-text-container
|
||||
[text {:style st/delete-group-text}
|
||||
(label :t/delete-group)]
|
||||
[text {:style st/delete-group-prompt-text}
|
||||
(label :t/delete-group-prompt)]]]]])
|
||||
|
||||
(defn group-chat-settings-btns []
|
||||
[view st/settings-group-container
|
||||
[view {:opacity 0.4}
|
||||
[touchable-highlight {:on-press #()}
|
||||
[view st/settings-group-item
|
||||
[view st/settings-icon-container
|
||||
[icon :speaker_blue st/add-icon]]
|
||||
[view st/settings-group-text-container
|
||||
[text {:style st/settings-group-text}
|
||||
(label :t/mute-notifications)]]]]]
|
||||
[action-separator]
|
||||
[action-button (label :t/clear-history)
|
||||
:close_blue
|
||||
#(dispatch [:clear-history])]
|
||||
[action-separator]
|
||||
[touchable-highlight {:on-press #(dispatch [:leave-group-chat])}
|
||||
[view st/settings-group-item
|
||||
[view st/delete-icon-container
|
||||
[icon :arrow_right_red st/add-icon]]
|
||||
[view st/settings-group-text-container
|
||||
[text {:style st/delete-group-text}
|
||||
(label :t/leave-chat)]]]]])
|
||||
|
||||
(defn more-btn [contacts-limit contacts-count on-press]
|
||||
[view
|
||||
[common/list-separator]
|
||||
[view cst/show-all
|
||||
[touchable-highlight {:on-press on-press}
|
||||
[view
|
||||
[text {:style cst/show-all-text
|
||||
:uppercase? (get-in platform-specific [:uppercase?])
|
||||
:font (get-in platform-specific [:component-styles :contacts :show-all-text-font])}
|
||||
(str (- contacts-count contacts-limit) " " (label :t/more))]]]]])
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
(ns status-im.new-group.views.reorder-groups
|
||||
(:require-macros [status-im.utils.views :refer [defview]])
|
||||
(:require [re-frame.core :refer [dispatch dispatch-sync]]
|
||||
[status-im.components.react :refer [view
|
||||
text
|
||||
icon
|
||||
touchable-highlight
|
||||
list-item]]
|
||||
[status-im.components.sticky-button :refer [sticky-button]]
|
||||
[status-im.components.status-bar :refer [status-bar]]
|
||||
[status-im.components.toolbar-new.view :refer [toolbar]]
|
||||
[status-im.components.sortable-list-view :refer [sortable-list-view sortable-item]]
|
||||
[status-im.components.common.common :as common]
|
||||
[status-im.utils.listview :refer [to-datasource]]
|
||||
[status-im.utils.platform :refer [android?]]
|
||||
[status-im.new-group.styles :as st]
|
||||
[status-im.i18n :refer [label label-pluralize]]
|
||||
[status-im.utils.platform :refer [platform-specific]]
|
||||
[reagent.core :as r]))
|
||||
|
||||
(defn toolbar-view []
|
||||
[toolbar {:actions [{:image :blank}]
|
||||
:title (label :t/reorder-groups)}])
|
||||
|
||||
(defn group-item [{:keys [name contacts] :as group}]
|
||||
(let [cnt (count contacts)]
|
||||
[view st/order-item-container
|
||||
[view st/order-item-inner-container
|
||||
[text {:style st/order-item-label}
|
||||
name]
|
||||
[text {:style st/order-item-contacts}
|
||||
(str cnt " " (label-pluralize cnt :t/contact-s))]
|
||||
[view {:flex 1}]
|
||||
[view st/order-item-icon
|
||||
[icon :grab_gray]]]]))
|
||||
|
||||
(defn render-separator [last]
|
||||
(fn [_ row-id _]
|
||||
(list-item
|
||||
(if (= row-id last)
|
||||
^{:key "bottom-shadow"}
|
||||
[common/bottom-shadow]
|
||||
^{:key row-id}
|
||||
[view st/order-item-separator-wrapper
|
||||
[view st/order-item-separator]]))))
|
||||
|
||||
(defview reorder-groups []
|
||||
[groups [:get :contact-groups]
|
||||
order [:get :groups-order]]
|
||||
(let [this (r/current-component)]
|
||||
[view st/reorder-groups-container
|
||||
[status-bar]
|
||||
[toolbar-view]
|
||||
[view st/reorder-list-container
|
||||
[common/top-shadow]
|
||||
[sortable-list-view
|
||||
{:data groups
|
||||
:order order
|
||||
:on-row-moved #(do (dispatch-sync [:change-group-order (:from %) (:to %)])
|
||||
(.forceUpdate this))
|
||||
:render-row (fn [row]
|
||||
(sortable-item [group-item row]))
|
||||
:render-separator (render-separator (last order))}]]
|
||||
[sticky-button (label :t/save) #(dispatch [:save-group-order])]]))
|
|
@ -1,17 +0,0 @@
|
|||
(ns status-im.new-group.views.toggle-contact
|
||||
(:require [re-frame.core :refer [dispatch]]
|
||||
[status-im.components.contact.contact :refer [toogle-contact-view]]))
|
||||
|
||||
(defn on-toggle [checked? whisper-identity]
|
||||
(let [action (if checked? :deselect-contact :select-contact)]
|
||||
(dispatch [action whisper-identity])))
|
||||
|
||||
(defn on-toggle-participant [checked? whisper-identity]
|
||||
(let [action (if checked? :deselect-participant :select-participant)]
|
||||
(dispatch [action whisper-identity])))
|
||||
|
||||
(defn group-toggle-contact [{:keys [whisper-identity] :as contact}]
|
||||
[toogle-contact-view contact :is-contact-selected? on-toggle])
|
||||
|
||||
(defn group-toggle-participant [{:keys [whisper-identity] :as contact}]
|
||||
[toogle-contact-view contact :is-participant-selected? on-toggle-participant])
|
|
@ -35,17 +35,17 @@
|
|||
(debug :send-group-message message)
|
||||
(d/add-pending-message! web3 message)))
|
||||
|
||||
(s/def :group/message
|
||||
(s/def ::message
|
||||
(s/merge :protocol/message (s/keys :req-un [:chat-message/payload])))
|
||||
|
||||
(s/def :public-group/username (s/and string? (complement str/blank?)))
|
||||
(s/def :public-group/message
|
||||
(s/merge :group/message (s/keys :username :public-group/username)))
|
||||
(s/merge ::message (s/keys :username :public-group/username)))
|
||||
|
||||
(defn send!
|
||||
[{:keys [keypair message] :as options}]
|
||||
{:pre [(valid? :message/keypair keypair)
|
||||
(valid? :group/message message)]}
|
||||
(valid? ::message message)]}
|
||||
(send-group-message! options :group-message))
|
||||
|
||||
(defn send-to-public-group!
|
||||
|
@ -74,16 +74,15 @@
|
|||
identity)]
|
||||
(send-group-message! options' :remove-group-identity)))
|
||||
|
||||
(s/def :group/admin :message/from)
|
||||
(s/def ::identities (s/* string?))
|
||||
|
||||
(s/def :group/name string?)
|
||||
(s/def :group/id string?)
|
||||
(s/def :group/admin string?)
|
||||
(s/def :group/contacts (s/* string?))
|
||||
(s/def ::name string?)
|
||||
(s/def ::id string?)
|
||||
(s/def ::admin string?)
|
||||
(s/def ::contacts (s/* string?))
|
||||
(s/def ::group
|
||||
(s/keys :req-un
|
||||
[:group/name :group/id :group/contacts :message/keypair :group/admin]))
|
||||
[::name ::id ::contacts :message/keypair ::admin]))
|
||||
(s/def :invite/options
|
||||
(s/keys :req-un [:options/web3 :protocol/message ::group ::identities]))
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
(ns status-im.specs
|
||||
(:require-macros [status-im.utils.db :refer [allowed-keys]])
|
||||
(:require [cljs.spec.alpha :as s]
|
||||
(:require [cljs.spec.alpha :as spec]
|
||||
[status-im.accounts.specs]
|
||||
[status-im.navigation.specs]
|
||||
[status-im.contacts.db]
|
||||
[status-im.qr-scanner.specs]
|
||||
[status-im.new-group.specs]
|
||||
[status-im.group.db]
|
||||
[status-im.chat.specs]
|
||||
[status-im.chat.new-public-chat.db]
|
||||
[status-im.profile.specs]
|
||||
[status-im.transactions.specs]
|
||||
[status-im.discover.specs]))
|
||||
|
@ -14,39 +15,39 @@
|
|||
;;;;GLOBAL
|
||||
|
||||
;;public key of current logged in account
|
||||
(s/def ::current-public-key (s/nilable string?))
|
||||
(spec/def ::current-public-key (spec/nilable string?))
|
||||
;;true when application running at first time
|
||||
(s/def ::first-run (s/nilable boolean?))
|
||||
(s/def ::was-modal? (s/nilable boolean?))
|
||||
(spec/def ::first-run (spec/nilable boolean?))
|
||||
(spec/def ::was-modal? (spec/nilable boolean?))
|
||||
;;"http://localhost:8545"
|
||||
(s/def ::rpc-url (s/nilable string?))
|
||||
(spec/def ::rpc-url (spec/nilable string?))
|
||||
;;object? doesn't work
|
||||
(s/def ::web3 (s/nilable any?))
|
||||
(spec/def ::web3 (spec/nilable any?))
|
||||
;;object?
|
||||
(s/def ::webview-bridge (s/nilable any?))
|
||||
(s/def ::status-module-initialized? (s/nilable boolean?))
|
||||
(s/def ::status-node-started? (s/nilable boolean?))
|
||||
(s/def ::toolbar-search (s/nilable map?))
|
||||
(spec/def ::webview-bridge (spec/nilable any?))
|
||||
(spec/def ::status-module-initialized? (spec/nilable boolean?))
|
||||
(spec/def ::status-node-started? (spec/nilable boolean?))
|
||||
(spec/def ::toolbar-search (spec/nilable map?))
|
||||
;;height of native keyboard if shown
|
||||
(s/def ::keyboard-height (s/nilable number?))
|
||||
(s/def ::keyboard-max-height (s/nilable number?))
|
||||
(spec/def ::keyboard-height (spec/nilable number?))
|
||||
(spec/def ::keyboard-max-height (spec/nilable number?))
|
||||
;;:unknown - not used
|
||||
(s/def ::orientation (s/nilable keyword?))
|
||||
(spec/def ::orientation (spec/nilable keyword?))
|
||||
;;:online - presence of internet connection in the phone
|
||||
(s/def ::network-status (s/nilable keyword?))
|
||||
(spec/def ::network-status (spec/nilable keyword?))
|
||||
|
||||
;;;;NODE
|
||||
|
||||
(s/def ::sync-listening-started (s/nilable boolean?))
|
||||
(s/def ::sync-state (s/nilable keyword?))
|
||||
(s/def ::sync-data (s/nilable map?))
|
||||
(spec/def ::sync-listening-started (spec/nilable boolean?))
|
||||
(spec/def ::sync-state (spec/nilable keyword?))
|
||||
(spec/def ::sync-data (spec/nilable map?))
|
||||
|
||||
;;;;NETWORK
|
||||
|
||||
;;network name :testnet
|
||||
(s/def ::network (s/nilable keyword?))
|
||||
(spec/def ::network (spec/nilable keyword?))
|
||||
|
||||
(s/def ::db (allowed-keys
|
||||
(spec/def ::db (allowed-keys
|
||||
:opt
|
||||
[:contacts/contacts
|
||||
:contacts/new-identity
|
||||
|
@ -56,7 +57,12 @@
|
|||
:contacts/list-ui-props
|
||||
:contacts/click-handler
|
||||
:contacts/click-action
|
||||
:contacts/click-params]
|
||||
:contacts/click-params
|
||||
:group/contact-groups
|
||||
:group/contact-group-id
|
||||
:group/group-type
|
||||
:group/selected-contacts
|
||||
:group/groups-order]
|
||||
:opt-un
|
||||
[::current-public-key
|
||||
::first-run
|
||||
|
@ -89,14 +95,6 @@
|
|||
:qr/qr-codes
|
||||
:qr/qr-modal
|
||||
:qr/current-qr-context
|
||||
:group/contact-groups
|
||||
:group/contact-group-id
|
||||
:group/group-type
|
||||
:group/new-group
|
||||
:group/new-groups
|
||||
:group/contacts-group
|
||||
:group/selected-contacts
|
||||
:group/groups-order
|
||||
:chat/chats
|
||||
:chat/current-chat-id
|
||||
:chat/chat-id
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
(:require [re-frame.core :refer [reg-sub subscribe]]
|
||||
status-im.chat.subs
|
||||
status-im.chats-list.subs
|
||||
status-im.group-settings.subs
|
||||
status-im.group.chat-settings.subs
|
||||
status-im.discover.subs
|
||||
status-im.contacts.subs
|
||||
status-im.new-group.subs
|
||||
status-im.group.subs
|
||||
status-im.transactions.subs
|
||||
status-im.bots.subs))
|
||||
|
||||
|
|
|
@ -127,5 +127,5 @@
|
|||
([] (register-exception-handler default-alert-handler))
|
||||
([f] (register-exception-handler true f))
|
||||
([dev? f]
|
||||
(if (or dev? (not js/goog.DEBUG))
|
||||
(if (and dev? (not js/goog.DEBUG))
|
||||
(.setGlobalHandler js/ErrorUtils f dev?))))
|
||||
|
|
Loading…
Reference in New Issue