[#10179] Create invite link for group chat
Signed-off-by: andrey <motor4ik@gmail.com>
This commit is contained in:
parent
4304a5dd97
commit
89ad4f59d0
|
@ -1,32 +1,12 @@
|
|||
(ns status-im.acquisition.install-referrer
|
||||
(:require [taoensso.timbre :as log]
|
||||
[clojure.string :as cstr]
|
||||
["react-native-device-info" :refer [getInstallReferrer]]))
|
||||
|
||||
(defn- split-param [param]
|
||||
(->
|
||||
(cstr/split param #"=")
|
||||
(concat (repeat ""))
|
||||
(->>
|
||||
(take 2))))
|
||||
|
||||
(defn- url-decode
|
||||
[string]
|
||||
(some-> string str (cstr/replace #"\+" "%20") (js/decodeURIComponent)))
|
||||
|
||||
(defn- query->map
|
||||
[qstr]
|
||||
(when-not (cstr/blank? qstr)
|
||||
(some->> (cstr/split qstr #"&")
|
||||
seq
|
||||
(mapcat split-param)
|
||||
(map url-decode)
|
||||
(apply hash-map))))
|
||||
["react-native-device-info" :refer [getInstallReferrer]]
|
||||
[status-im.utils.http :as http]))
|
||||
|
||||
(defn parse-referrer
|
||||
"Google return query params for referral with all utm tags"
|
||||
[referrer]
|
||||
(-> referrer query->map (get "referrer")))
|
||||
(-> referrer http/query->map (get "referrer")))
|
||||
|
||||
(defn get-referrer [cb]
|
||||
(-> (getInstallReferrer)
|
||||
|
|
|
@ -110,10 +110,12 @@
|
|||
|
||||
(defn map-chats [{:keys [db] :as cofx}]
|
||||
(fn [val]
|
||||
(merge
|
||||
(or (get (:chats db) (:chat-id val))
|
||||
(create-new-chat (:chat-id val) cofx))
|
||||
val)))
|
||||
(assoc
|
||||
(merge
|
||||
(or (get (:chats db) (:chat-id val))
|
||||
(create-new-chat (:chat-id val) cofx))
|
||||
val)
|
||||
:invitation-admin (:invitation-admin val))))
|
||||
|
||||
(defn filter-chats [db]
|
||||
(fn [val]
|
||||
|
|
|
@ -31,6 +31,11 @@
|
|||
emoji-reaction-sad (:sad resources/reactions)
|
||||
emoji-reaction-angry (:angry resources/reactions)})
|
||||
|
||||
(def invitation-state-unknown 0)
|
||||
(def invitation-state-requested 1)
|
||||
(def invitation-state-rejected 2)
|
||||
(def invitation-state-approved 3)
|
||||
|
||||
(def message-type-one-to-one 1)
|
||||
(def message-type-public-group 2)
|
||||
(def message-type-private-group 3)
|
||||
|
|
|
@ -89,7 +89,8 @@
|
|||
:unviewedMessagesCount :unviewed-messages-count
|
||||
:lastMessage :last-message
|
||||
:active :is-active
|
||||
:lastClockValue :last-clock-value})
|
||||
:lastClockValue :last-clock-value
|
||||
:invitationAdmin :invitation-admin})
|
||||
(update :last-message #(when % (messages/<-rpc %)))
|
||||
(dissoc :chatType :members)))
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
(ns status-im.data-store.invitations
|
||||
(:require clojure.set))
|
||||
|
||||
(defn <-rpc [message]
|
||||
(-> message
|
||||
(clojure.set/rename-keys {:chatId :chat-id
|
||||
:introductionMessage :introduction-message
|
||||
:messageType :message-type})))
|
|
@ -51,6 +51,7 @@
|
|||
"shhext_leaveGroupChat" {}
|
||||
"shhext_changeGroupChatName" {}
|
||||
"shhext_createGroupChatWithMembers" {}
|
||||
"shhext_createGroupChatFromInvitation" {}
|
||||
"shhext_reSendChatMessage" {}
|
||||
"shhext_getOurInstallations" {}
|
||||
"shhext_setInstallationMetadata" {}
|
||||
|
@ -89,6 +90,9 @@
|
|||
"shhext_sendTransaction" {}
|
||||
"shhext_acceptRequestTransaction" {}
|
||||
"shhext_signMessageWithChatKey" {}
|
||||
"shhext_sendGroupChatInvitationRequest" {}
|
||||
"shhext_sendGroupChatInvitationRejection" {}
|
||||
"shhext_getGroupChatInvitations" {}
|
||||
"wakuext_post" {}
|
||||
"wakuext_startMessenger" {}
|
||||
"wakuext_sendPairInstallation" {}
|
||||
|
@ -106,6 +110,7 @@
|
|||
"wakuext_leaveGroupChat" {}
|
||||
"wakuext_changeGroupChatName" {}
|
||||
"wakuext_createGroupChatWithMembers" {}
|
||||
"wakuext_createGroupChatFromInvitation" {}
|
||||
"wakuext_reSendChatMessage" {}
|
||||
"wakuext_getOurInstallations" {}
|
||||
"wakuext_setInstallationMetadata" {}
|
||||
|
@ -144,6 +149,9 @@
|
|||
"wakuext_sendTransaction" {}
|
||||
"wakuext_acceptRequestTransaction" {}
|
||||
"wakuext_signMessageWithChatKey" {}
|
||||
"wakuext_sendGroupChatInvitationRequest" {}
|
||||
"wakuext_sendGroupChatInvitationRejection" {}
|
||||
"wakuext_getGroupChatInvitations" {}
|
||||
"status_chats" {}
|
||||
"wallet_getTransfers" {}
|
||||
"wallet_getTokensBalances" {}
|
||||
|
|
|
@ -67,7 +67,8 @@
|
|||
status-im.http.core
|
||||
status-im.ui.screens.profile.events
|
||||
status-im.chat.models.images
|
||||
status-im.ui.screens.privacy-and-security-settings.events))
|
||||
status-im.ui.screens.privacy-and-security-settings.events
|
||||
[status-im.data-store.invitations :as data-store.invitations]))
|
||||
|
||||
;; init module
|
||||
(handlers/register-handler-fx
|
||||
|
@ -776,14 +777,16 @@
|
|||
(fn [_ [_ err]]
|
||||
(log/error :send-status-message-error err)))
|
||||
|
||||
(fx/defn handle-update [cofx {:keys [chats messages emojiReactions] :as response}]
|
||||
(fx/defn handle-update [cofx {:keys [chats messages emojiReactions invitations]}]
|
||||
(let [chats (map data-store.chats/<-rpc chats)
|
||||
messages (map data-store.messages/<-rpc messages)
|
||||
message-fxs (map chat.message/receive-one messages)
|
||||
emoji-reactions (map data-store.reactions/<-rpc emojiReactions)
|
||||
emoji-react-fxs (map chat.reactions/receive-one emoji-reactions)
|
||||
invitations-fxs [(group-chats/handle-invitations
|
||||
(map data-store.invitations/<-rpc invitations))]
|
||||
chat-fxs (map #(chat/ensure-chat (dissoc % :unviewed-messages-count)) chats)]
|
||||
(apply fx/merge cofx (concat chat-fxs message-fxs emoji-react-fxs))))
|
||||
(apply fx/merge cofx (concat chat-fxs message-fxs emoji-react-fxs invitations-fxs))))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:transport/message-sent
|
||||
|
@ -805,6 +808,11 @@
|
|||
[cofx response]
|
||||
(handle-update cofx response))
|
||||
|
||||
(fx/defn invitation-sent
|
||||
{:events [:transport/invitation-sent]}
|
||||
[cofx response]
|
||||
(handle-update cofx response))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:transport.callback/node-info-fetched
|
||||
(fn [cofx [_ node-info]]
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
[status-im.transport.filters.core :as transport.filters]
|
||||
[status-im.navigation :as navigation]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.waku.core :as waku]))
|
||||
[status-im.waku.core :as waku]
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(fx/defn remove-member
|
||||
"Format group update message and sign membership"
|
||||
|
@ -71,6 +72,14 @@
|
|||
:params [nil group-name (into [] selected-contacts)]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]}))
|
||||
|
||||
(fx/defn create-from-link
|
||||
[cofx {:keys [chat-id invitation-admin chat-name]}]
|
||||
(if (get-in cofx [:db :chats chat-id :is-active])
|
||||
(models.chat/navigate-to-chat cofx chat-id)
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method (waku/enabled? cofx) "createGroupChatFromInvitation")
|
||||
:params [chat-name chat-id invitation-admin]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]}))
|
||||
|
||||
(fx/defn make-admin
|
||||
[{:keys [db] :as cofx} chat-id member]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method (waku/enabled? cofx) "addAdminsToGroupChat")
|
||||
|
@ -84,6 +93,15 @@
|
|||
:params [nil current-chat-id selected-participants]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
|
||||
(fx/defn add-members-from-invitation
|
||||
"Add members to a group chat"
|
||||
{:events [:group-chats.ui/add-members-from-invitation]}
|
||||
[{{:keys [current-chat-id] :as db} :db :as cofx} id participant]
|
||||
{:db (assoc-in db [:group-chat/invitations id :state] constants/invitation-state-approved)
|
||||
::json-rpc/call [{:method (json-rpc/call-ext-method (waku/enabled? cofx) "addMembersToGroupChat")
|
||||
:params [nil current-chat-id [participant]]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
|
||||
(fx/defn leave
|
||||
"Leave chat"
|
||||
{:events [:group-chats.ui/leave-chat-confirmed]}
|
||||
|
@ -92,6 +110,16 @@
|
|||
:params [nil chat-id true]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]})
|
||||
|
||||
(fx/defn remove
|
||||
"Remove chat"
|
||||
{:events [:group-chats.ui/remove-chat-confirmed]}
|
||||
[cofx chat-id]
|
||||
(fx/merge cofx
|
||||
(models.chat/deactivate-chat chat-id)
|
||||
(models.chat/upsert-chat {:chat-id chat-id
|
||||
:is-active false})
|
||||
(navigation/navigate-to-cofx :home {})))
|
||||
|
||||
(defn- valid-name? [name]
|
||||
(spec/valid? :profile/name name))
|
||||
|
||||
|
@ -104,3 +132,39 @@
|
|||
::json-rpc/call [{:method (json-rpc/call-ext-method (waku/enabled? cofx) "changeGroupChatName")
|
||||
:params [nil chat-id new-name]
|
||||
:on-success #(re-frame/dispatch [::chat-updated %])}]}))
|
||||
|
||||
(fx/defn membership-retry
|
||||
{:events [:group-chats.ui/membership-retry]}
|
||||
[{{:keys [current-chat-id] :as db} :db}]
|
||||
{:db (assoc-in db [:chat/memberships current-chat-id :retry?] true)})
|
||||
|
||||
(fx/defn membership-message
|
||||
{:events [:group-chats.ui/update-membership-message]}
|
||||
[{{:keys [current-chat-id] :as db} :db} message]
|
||||
{:db (assoc-in db [:chat/memberships current-chat-id :message] message)})
|
||||
|
||||
(fx/defn send-group-chat-membership-request
|
||||
"Send group chat membership request"
|
||||
{:events [:send-group-chat-membership-request]}
|
||||
[{{:keys [current-chat-id chats] :as db} :db :as cofx}]
|
||||
(let [{:keys [invitation-admin]} (get chats current-chat-id)
|
||||
message (get-in db [:chat/memberships current-chat-id :message])]
|
||||
{:db (assoc-in db [:chat/memberships current-chat-id] nil)
|
||||
::json-rpc/call [{:method (json-rpc/call-ext-method (waku/enabled? cofx) "sendGroupChatInvitationRequest")
|
||||
:params [nil current-chat-id invitation-admin message]
|
||||
:on-success #(re-frame/dispatch [:transport/invitation-sent %])}]}))
|
||||
|
||||
(fx/defn send-group-chat-membership-rejection
|
||||
"Send group chat membership rejection"
|
||||
{:events [:send-group-chat-membership-rejection]}
|
||||
[cofx invitation-id]
|
||||
{::json-rpc/call [{:method (json-rpc/call-ext-method (waku/enabled? cofx) "sendGroupChatInvitationRejection")
|
||||
:params [nil invitation-id]
|
||||
:on-success #(re-frame/dispatch [:transport/invitation-sent %])}]})
|
||||
|
||||
(fx/defn handle-invitations
|
||||
[{db :db} invitations]
|
||||
{:db (update db :group-chat/invitations #(reduce (fn [acc {:keys [id] :as inv}]
|
||||
(assoc acc id inv))
|
||||
%
|
||||
invitations))})
|
|
@ -29,7 +29,9 @@
|
|||
[status-im.wallet.core :as wallet]
|
||||
[status-im.wallet.prices :as prices]
|
||||
[status-im.acquisition.core :as acquisition]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.data-store.invitations :as data-store.invitations]
|
||||
[status-im.waku.core :as waku]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::login
|
||||
|
@ -107,6 +109,14 @@
|
|||
all-stored-browsers)]
|
||||
{:db (assoc db :browser/browsers browsers)}))
|
||||
|
||||
(fx/defn initialize-invitations
|
||||
{:events [::initialize-invitations]}
|
||||
[{:keys [db]} invitations]
|
||||
{:db (assoc db :group-chat/invitations (reduce (fn [acc {:keys [id] :as inv}]
|
||||
(assoc acc id (data-store.invitations/<-rpc inv)))
|
||||
{}
|
||||
invitations))})
|
||||
|
||||
(fx/defn initialize-web3-client-version
|
||||
{:events [::initialize-web3-client-version]}
|
||||
[{:keys [db]} node-version]
|
||||
|
@ -158,6 +168,11 @@
|
|||
(fx/defn initialize-appearance [cofx]
|
||||
{::multiaccounts/switch-theme (get-in cofx [:db :multiaccount :appearance])})
|
||||
|
||||
(fx/defn get-group-chat-invitations [cofx]
|
||||
{::json-rpc/call
|
||||
[{:method (json-rpc/call-ext-method (waku/enabled? cofx) "getGroupChatInvitations")
|
||||
:on-success #(re-frame/dispatch [::initialize-invitations %])}]})
|
||||
|
||||
(fx/defn get-settings-callback
|
||||
{:events [::get-settings-callback]}
|
||||
[{:keys [db] :as cofx} settings]
|
||||
|
@ -190,6 +205,7 @@
|
|||
(contact/initialize-contacts)
|
||||
(stickers/init-stickers-packs)
|
||||
(mobile-network/on-network-status-change)
|
||||
(get-group-chat-invitations)
|
||||
(logging/set-log-level (:log-level multiaccount))
|
||||
(multiaccounts/switch-preview-privacy-mode-flag))))
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@
|
|||
[status-im.utils.utils :as utils]
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.ui.screens.add-new.new-chat.db :as new-chat.db]
|
||||
[status-im.utils.fx :as fx]))
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.group-chats.core :as group-chats]))
|
||||
|
||||
(fx/defn scan-qr-code
|
||||
{:events [::scan-code]}
|
||||
|
@ -50,6 +51,9 @@
|
|||
(when (seq topic)
|
||||
(chat/start-public-chat cofx topic {})))
|
||||
|
||||
(fx/defn handle-group-chat [cofx params]
|
||||
(group-chats/create-from-link cofx params))
|
||||
|
||||
(fx/defn handle-view-profile
|
||||
[{:keys [db] :as cofx} {:keys [public-key]}]
|
||||
(let [own (new-chat.db/own-public-key? db public-key)]
|
||||
|
@ -79,6 +83,7 @@
|
|||
[cofx {:keys [type] :as data}]
|
||||
(case type
|
||||
:public-chat (handle-public-chat cofx data)
|
||||
:group-chat (handle-group-chat cofx data)
|
||||
:private-chat (handle-private-chat cofx data)
|
||||
:contact (handle-view-profile cofx data)
|
||||
:browser (handle-browse cofx data)
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
[status-im.ethereum.resolver :as resolver]
|
||||
[status-im.ethereum.stateofus :as stateofus]
|
||||
[cljs.spec.alpha :as spec]
|
||||
[status-im.ethereum.core :as ethereum]))
|
||||
[status-im.ethereum.core :as ethereum]
|
||||
[status-im.utils.db :as utils.db]
|
||||
[status-im.utils.http :as http]))
|
||||
|
||||
(def ethereum-scheme "ethereum:")
|
||||
|
||||
|
@ -27,6 +29,9 @@
|
|||
(def browser-extractor {[#"(.*)" :domain] {"" :browser
|
||||
"/" :browser}})
|
||||
|
||||
(def group-chat-extractor {[#"(.*)" :params] {"" :group-chat
|
||||
"/" :group-chat}})
|
||||
|
||||
(def eip-extractor {#{[:prefix "-" :address]
|
||||
[:address]}
|
||||
{#{["@" :chain-id] ""}
|
||||
|
@ -38,13 +43,19 @@
|
|||
"b/" browser-extractor
|
||||
"browser/" browser-extractor
|
||||
["p/" :chat-id] :private-chat
|
||||
"g/" group-chat-extractor
|
||||
["u/" :user-id] :user
|
||||
["user/" :user-id] :user
|
||||
["referral/" :referrer] :referrals}
|
||||
ethereum-scheme eip-extractor}])
|
||||
|
||||
(defn parse-query-params
|
||||
[url]
|
||||
(let [url (goog.Uri. url)]
|
||||
(http/query->map (.getQuery url))))
|
||||
|
||||
(defn match-uri [uri]
|
||||
(assoc (bidi/match-route routes uri) :uri uri))
|
||||
(assoc (bidi/match-route routes uri) :uri uri :query-params (parse-query-params uri)))
|
||||
|
||||
(defn- ens-name-parse [contact-identity]
|
||||
(when (string? contact-identity)
|
||||
|
@ -87,6 +98,21 @@
|
|||
{:type :public-chat
|
||||
:error :invalid-topic}))
|
||||
|
||||
(defn match-group-chat [{:strs [a a1 a2]}]
|
||||
(let [[admin-pk encoded-chat-name chat-id] [a a1 a2]
|
||||
chat-id-parts (when (not (string/blank? chat-id)) (string/split chat-id #"-"))
|
||||
chat-name (when (not (string/blank? encoded-chat-name)) (js/decodeURI encoded-chat-name))]
|
||||
(if (and (not (string/blank? chat-id)) (not (string/blank? admin-pk)) (not (string/blank? chat-name))
|
||||
(> (count chat-id-parts) 1)
|
||||
(not (string/blank? (first chat-id-parts)))
|
||||
(utils.db/valid-public-key? admin-pk)
|
||||
(utils.db/valid-public-key? (last chat-id-parts)))
|
||||
{:type :group-chat
|
||||
:chat-id chat-id
|
||||
:invitation-admin admin-pk
|
||||
:chat-name chat-name}
|
||||
{:error :invalid-group-chat-data})))
|
||||
|
||||
(defn match-private-chat-async [chain {:keys [chat-id]} cb]
|
||||
(match-contact-async chain
|
||||
{:user-id chat-id}
|
||||
|
@ -143,7 +169,7 @@
|
|||
:referrer referrer})
|
||||
|
||||
(defn handle-uri [chain uri cb]
|
||||
(let [{:keys [handler route-params]} (match-uri uri)]
|
||||
(let [{:keys [handler route-params query-params]} (match-uri uri)]
|
||||
(log/info "[router] uri " uri " matched " handler " with " route-params)
|
||||
(cond
|
||||
(= handler :public-chat)
|
||||
|
@ -161,6 +187,9 @@
|
|||
(= handler :private-chat)
|
||||
(match-private-chat-async chain route-params cb)
|
||||
|
||||
(= handler :group-chat)
|
||||
(cb (match-group-chat query-params))
|
||||
|
||||
(spec/valid? :global/public-key uri)
|
||||
(match-contact-async chain {:user-id uri} cb)
|
||||
|
||||
|
|
|
@ -3,11 +3,18 @@
|
|||
[cljs.test :refer [deftest are] :include-macros true]))
|
||||
|
||||
(def public-key "0x04fbce10971e1cd7253b98c7b7e54de3729ca57ce41a2bfb0d1c4e0a26f72c4b6913c3487fa1b4bb86125770f1743fb4459da05c1cbe31d938814cfaf36e252073")
|
||||
(def chat-id "59eb36e6-9d4d-4724-9d3a-8a3cdc5e8a8e-0x04f383daedc92a66add4c90d8884004ef826cba113183a0052703c8c77fed1522f88f44550498d20679af98907627059a295e43212a1cd3c1f21a157704d608c13")
|
||||
(def chat-name-url "Test%20group%20chat")
|
||||
(def chat-name "Test group chat")
|
||||
|
||||
(deftest parse-uris
|
||||
(are [uri expected] (= (router/match-uri uri) {:handler (first expected)
|
||||
:route-params (second expected)
|
||||
:uri uri})
|
||||
(are [uri expected] (= (cond-> (router/match-uri uri)
|
||||
(< (count expected) 3)
|
||||
(assoc :query-params nil))
|
||||
{:handler (first expected)
|
||||
:route-params (second expected)
|
||||
:query-params (when (= 3 (count expected)) (last expected))
|
||||
:uri uri})
|
||||
|
||||
"status-im://status" [:public-chat {:chat-id "status"}]
|
||||
|
||||
|
@ -17,6 +24,12 @@
|
|||
|
||||
"status-im://b/www.cryptokitties.co" [:browser {:domain "www.cryptokitties.c"}]
|
||||
|
||||
(str "status-im://g/args?a=" public-key "&a1=" chat-name-url "&a2=" chat-id)
|
||||
[:group-chat {:params "arg"} {"a" public-key "a1" chat-name "a2" chat-id}]
|
||||
|
||||
(str "https://join.status.im/g/args?a=" public-key "&a1=" chat-name-url "&a2=" chat-id)
|
||||
[:group-chat {:params "arg"} {"a" public-key "a1" chat-name "a2" chat-id}]
|
||||
|
||||
"https://join.status.im/status" [:public-chat {:chat-id "status"}]
|
||||
|
||||
"https://join.status.im/u/statuse2e" [:user {:user-id "statuse2e"}]
|
||||
|
@ -50,3 +63,19 @@
|
|||
"ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@1/transfer?uint256=1" [:ethereum {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
|
||||
:chain-id "1"
|
||||
:function "transfer"}]))
|
||||
|
||||
(def error {:error :invalid-group-chat-data})
|
||||
|
||||
(deftest match-group-chat-query
|
||||
(are [query-params expected] (= (router/match-group-chat query-params)
|
||||
expected)
|
||||
nil error
|
||||
{} error
|
||||
{"b" public-key} error
|
||||
{"a" public-key "a1" chat-name} error
|
||||
{"a" "0x00ceded" "a1" chat-name "a2" chat-id} error
|
||||
{"a" public-key "a1" chat-name "a2" public-key} error
|
||||
{"a" public-key "a1" chat-name "a2" chat-id} {:type :group-chat
|
||||
:chat-id chat-id
|
||||
:invitation-admin public-key
|
||||
:chat-name chat-name}))
|
|
@ -115,7 +115,9 @@
|
|||
(reg-root-key-sub :group-chat-profile/profile :group-chat-profile/profile)
|
||||
(reg-root-key-sub :selected-participants :selected-participants)
|
||||
(reg-root-key-sub :chat/inputs :chat/inputs)
|
||||
(reg-root-key-sub :chat/memberships :chat/memberships)
|
||||
(reg-root-key-sub :camera-roll-photos :camera-roll-photos)
|
||||
(reg-root-key-sub :group-chat/invitations :group-chat/invitations)
|
||||
|
||||
;;browser
|
||||
(reg-root-key-sub :browsers :browser/browsers)
|
||||
|
@ -602,6 +604,13 @@
|
|||
(fn [[chat-id inputs]]
|
||||
(get-in inputs [chat-id :input-text])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat-membership
|
||||
:<- [:chats/current-chat-id]
|
||||
:<- [:chat/memberships]
|
||||
(fn [[chat-id memberships]]
|
||||
(get memberships chat-id)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/current-chat
|
||||
:<- [:chats/current-raw-chat]
|
||||
|
@ -626,7 +635,7 @@
|
|||
:<- [:chats/current-raw-chat]
|
||||
(fn [current-chat]
|
||||
(select-keys current-chat
|
||||
[:public? :group-chat :chat-id :chat-name :color])))
|
||||
[:public? :group-chat :chat-id :chat-name :color :invitation-admin])))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:current-chat/one-to-one-chat?
|
||||
|
@ -816,6 +825,19 @@
|
|||
{:joined? (group-chats.db/joined? my-public-key chat)
|
||||
:inviter-pk (group-chats.db/get-inviter-pk my-public-key chat)}))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:group-chat/invitations-by-chat-id
|
||||
:<- [:group-chat/invitations]
|
||||
(fn [invitations [_ chat-id]]
|
||||
(filter #(= (:chat-id %) chat-id) (vals invitations))))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:group-chat/pending-invitations-by-chat-id
|
||||
(fn [[_ chat-id] _]
|
||||
[(re-frame/subscribe [:group-chat/invitations-by-chat-id chat-id])])
|
||||
(fn [[invitations]]
|
||||
(filter #(= constants/invitation-state-requested (:state %)) invitations)))
|
||||
|
||||
(re-frame/reg-sub
|
||||
:chats/transaction-status
|
||||
;;TODO address here for transactions
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
[status-im.data-store.reactions :as data-store.reactions]
|
||||
[status-im.data-store.contacts :as data-store.contacts]
|
||||
[status-im.data-store.chats :as data-store.chats]
|
||||
[status-im.data-store.invitations :as data-store.invitations]
|
||||
[status-im.group-chats.core :as models.group]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.types :as types]))
|
||||
|
||||
|
@ -24,6 +26,9 @@
|
|||
(fx/defn handle-reactions [cofx reactions]
|
||||
(models.reactions/receive-signal cofx reactions))
|
||||
|
||||
(fx/defn handle-invitations [cofx invitations]
|
||||
(models.group/handle-invitations cofx invitations))
|
||||
|
||||
(fx/defn process-response
|
||||
{:events [::process]}
|
||||
[cofx ^js response-js]
|
||||
|
@ -31,7 +36,8 @@
|
|||
^js contacts (.-contacts response-js)
|
||||
^js installations (.-installations response-js)
|
||||
^js messages (.-messages response-js)
|
||||
^js emoji-reactions (.-emojiReactions response-js)]
|
||||
^js emoji-reactions (.-emojiReactions response-js)
|
||||
^js invitations (.-invitations response-js)]
|
||||
(cond
|
||||
(seq installations)
|
||||
(let [installations-clj (types/js->clj installations)]
|
||||
|
@ -68,7 +74,14 @@
|
|||
(js-delete response-js "emojiReactions")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-reactions (map data-store.reactions/<-rpc reactions)))))))
|
||||
(handle-reactions (map data-store.reactions/<-rpc reactions))))
|
||||
|
||||
(seq invitations)
|
||||
(let [invitations (types/js->clj invitations)]
|
||||
(js-delete response-js "invitations")
|
||||
(fx/merge cofx
|
||||
{:utils/dispatch-later [{:ms 20 :dispatch [::process response-js]}]}
|
||||
(handle-invitations (map data-store.invitations/<-rpc invitations)))))))
|
||||
|
||||
(fx/defn remove-hash
|
||||
[{:keys [db] :as cofx} envelope-hash]
|
||||
|
|
|
@ -6,30 +6,69 @@
|
|||
[status-im.ui.screens.chat.styles.main :as style]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.list-selection :as list-selection]
|
||||
[status-im.ui.components.colors :as colors])
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.utils.debounce :as debounce])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn join-chat-button [chat-id]
|
||||
[quo/button
|
||||
{:type :secondary
|
||||
:on-press #(re-frame/dispatch [:group-chats.ui/join-pressed chat-id])}
|
||||
:on-press #(debounce/dispatch-and-chill [:group-chats.ui/join-pressed chat-id] 2000)}
|
||||
(i18n/label :t/join-group-chat)])
|
||||
|
||||
(defn decline-chat [chat-id]
|
||||
[react/touchable-highlight
|
||||
{:on-press
|
||||
#(re-frame/dispatch [:group-chats.ui/leave-chat-confirmed chat-id])}
|
||||
#(debounce/dispatch-and-chill [:group-chats.ui/leave-chat-confirmed chat-id] 2000)}
|
||||
[react/text {:style style/decline-chat}
|
||||
(i18n/label :t/group-chat-decline-invitation)]])
|
||||
|
||||
(defn request-membership [{:keys [state introduction-message] :as invitation}]
|
||||
(let [{:keys [message retry?]} @(re-frame/subscribe [:chats/current-chat-membership])]
|
||||
[react/view {:margin-horizontal 16 :margin-top 10}
|
||||
(cond
|
||||
(and invitation (= constants/invitation-state-requested state) (not retry?))
|
||||
[react/view
|
||||
[react/text (i18n/label :t/introduce-yourself)]
|
||||
[react/text {:style {:margin-top 10 :margin-bottom 16 :min-height 66
|
||||
:padding-horizontal 16 :padding-vertical 11
|
||||
:border-color colors/gray-lighter :border-width 1
|
||||
:border-radius 8
|
||||
:color colors/gray}}
|
||||
introduction-message]
|
||||
[react/text {:style {:align-self :flex-end :margin-bottom 30
|
||||
:color colors/gray}}
|
||||
(str (count introduction-message) "/100")]]
|
||||
|
||||
(and invitation (= constants/invitation-state-rejected state) (not retry?))
|
||||
[react/view
|
||||
[react/text {:style {:align-self :center :margin-bottom 30}}
|
||||
(i18n/label :t/membership-declined)]]
|
||||
|
||||
:else
|
||||
[react/view
|
||||
[react/text (i18n/label :t/introduce-yourself)]
|
||||
[quo/text-input {:placeholder (i18n/label :t/message)
|
||||
:on-change-text #(re-frame/dispatch [:group-chats.ui/update-membership-message %])
|
||||
:max-length 100
|
||||
:multiline true
|
||||
:default-value message
|
||||
:container-style {:margin-top 10 :margin-bottom 16}}]
|
||||
[react/text {:style {:align-self :flex-end :margin-bottom 30}}
|
||||
(str (count message) "/100")]])]))
|
||||
|
||||
(defview group-chat-footer
|
||||
[chat-id]
|
||||
(letsubs [{:keys [joined?]} [:group-chat/inviter-info chat-id]]
|
||||
(when-not joined?
|
||||
[react/view {:style style/group-chat-join-footer}
|
||||
[react/view {:style style/group-chat-join-container}
|
||||
[join-chat-button chat-id]
|
||||
[decline-chat chat-id]]])))
|
||||
[chat-id invitation-admin]
|
||||
(letsubs [{:keys [joined?]} [:group-chat/inviter-info chat-id]
|
||||
invitations [:group-chat/invitations-by-chat-id chat-id]]
|
||||
(if invitation-admin
|
||||
[request-membership (first invitations)]
|
||||
(when-not joined?
|
||||
[react/view {:style style/group-chat-join-footer}
|
||||
[react/view {:style style/group-chat-join-container}
|
||||
[join-chat-button chat-id]
|
||||
[decline-chat chat-id]]]))))
|
||||
|
||||
(def group-chat-description-loading
|
||||
[react/view {:style (merge style/intro-header-description-container
|
||||
|
@ -96,8 +135,13 @@
|
|||
:else
|
||||
[created-group-chat-description chat-name])))
|
||||
|
||||
(defn group-chat-membership-description []
|
||||
[react/text {:style {:text-align :center :margin-horizontal 30}}
|
||||
(i18n/label :t/membership-description)])
|
||||
|
||||
(defn group-chat-description-container
|
||||
[{:keys [public?
|
||||
invitation-admin
|
||||
chat-id
|
||||
chat-name
|
||||
loading-messages?
|
||||
|
@ -108,5 +152,8 @@
|
|||
(and no-messages? public?)
|
||||
[no-messages-group-chat-description-container chat-id]
|
||||
|
||||
invitation-admin
|
||||
[group-chat-membership-description]
|
||||
|
||||
(not public?)
|
||||
[group-chat-inviter-description-container chat-id chat-name]))
|
||||
|
|
|
@ -91,42 +91,49 @@
|
|||
:on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])}]]))
|
||||
|
||||
(defn group-chat-accents []
|
||||
(fn [{:keys [chat-id group-chat chat-name color]}]
|
||||
(fn [{:keys [chat-id group-chat chat-name color invitation-admin]}]
|
||||
(let [{:keys [joined?]} @(re-frame/subscribe [:group-chat/inviter-info chat-id])]
|
||||
[react/view
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title chat-name
|
||||
:subtitle (i18n/label :t/group-info)
|
||||
:icon [chat-icon/chat-icon-view-chat-sheet
|
||||
chat-id group-chat chat-name color]
|
||||
:chevron true
|
||||
:on-press #(hide-sheet-and-dispatch [:show-group-chat-profile chat-id])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/mark-all-read)
|
||||
:accessibility-label :mark-all-read-button
|
||||
:icon :main-icons/check
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/mark-all-read-pressed chat-id])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/clear-history)
|
||||
:accessibility-label :clear-history-button
|
||||
:icon :main-icons/close
|
||||
:on-press #(re-frame/dispatch [:chat.ui/clear-history-pressed chat-id])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/fetch-history)
|
||||
:accessibility-label :fetch-history-button
|
||||
:icon :main-icons/arrow-down
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/fetch-history-pressed chat-id])}]
|
||||
(when joined?
|
||||
(if invitation-admin
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/remove)
|
||||
:accessibility-label :remove-group-chat
|
||||
:icon :main-icons/delete
|
||||
:on-press #(hide-sheet-and-dispatch [:group-chats.ui/remove-chat-confirmed chat-id])}]
|
||||
[react/view
|
||||
[quo/list-item
|
||||
{:theme :negative
|
||||
:title (i18n/label :t/leave-chat)
|
||||
:accessibility-label :leave-chat-button
|
||||
:icon :main-icons/arrow-left
|
||||
:on-press #(re-frame/dispatch [:group-chats.ui/leave-chat-pressed chat-id])}])])))
|
||||
{:theme :accent
|
||||
:title chat-name
|
||||
:subtitle (i18n/label :t/group-info)
|
||||
:icon [chat-icon/chat-icon-view-chat-sheet
|
||||
chat-id group-chat chat-name color]
|
||||
:chevron true
|
||||
:on-press #(hide-sheet-and-dispatch [:show-group-chat-profile chat-id])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/mark-all-read)
|
||||
:accessibility-label :mark-all-read-button
|
||||
:icon :main-icons/check
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/mark-all-read-pressed chat-id])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/clear-history)
|
||||
:accessibility-label :clear-history-button
|
||||
:icon :main-icons/close
|
||||
:on-press #(re-frame/dispatch [:chat.ui/clear-history-pressed chat-id])}]
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:title (i18n/label :t/fetch-history)
|
||||
:accessibility-label :fetch-history-button
|
||||
:icon :main-icons/arrow-down
|
||||
:on-press #(hide-sheet-and-dispatch [:chat.ui/fetch-history-pressed chat-id])}]
|
||||
(when joined?
|
||||
[quo/list-item
|
||||
{:theme :negative
|
||||
:title (i18n/label :t/leave-chat)
|
||||
:accessibility-label :leave-chat-button
|
||||
:icon :main-icons/arrow-left
|
||||
:on-press #(re-frame/dispatch [:group-chats.ui/leave-chat-pressed chat-id])}])]))))
|
||||
|
||||
(defn actions [{:keys [public? group-chat]
|
||||
:as current-chat}]
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
(defview toolbar-content-view []
|
||||
(letsubs [{:keys [group-chat
|
||||
invitation-admin
|
||||
color
|
||||
chat-id
|
||||
contacts
|
||||
|
@ -51,6 +52,6 @@
|
|||
[one-to-one-name chat-id])
|
||||
(when-not group-chat
|
||||
[contact-indicator chat-id])
|
||||
(when group-chat
|
||||
(when (and group-chat (not invitation-admin))
|
||||
[group-last-activity {:contacts contacts
|
||||
:public? public?}])]]))
|
||||
|
|
|
@ -28,7 +28,11 @@
|
|||
[status-im.ui.components.invite.chat :as invite.chat]
|
||||
[status-im.ui.screens.chat.components.accessory :as accessory]
|
||||
[status-im.ui.screens.chat.components.input :as components]
|
||||
[status-im.ui.screens.chat.message.datemark :as message-datemark]))
|
||||
[status-im.ui.screens.chat.message.datemark :as message-datemark]
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[quo.core :as quo]
|
||||
[clojure.string :as string]
|
||||
[status-im.constants :as constants]))
|
||||
|
||||
(defn topbar []
|
||||
(let [current-chat @(re-frame/subscribe [:current-chat/metadata])]
|
||||
|
@ -43,6 +47,19 @@
|
|||
[sheets/actions current-chat])
|
||||
:height 256}])}]}]))
|
||||
|
||||
(defn invitation-requests [chat-id admins]
|
||||
(let [current-pk @(re-frame/subscribe [:multiaccount/public-key])
|
||||
admin? (get admins current-pk)]
|
||||
(when admin?
|
||||
(let [invitations @(re-frame/subscribe [:group-chat/pending-invitations-by-chat-id chat-id])]
|
||||
(when (seq invitations)
|
||||
[react/touchable-highlight
|
||||
{:on-press #(re-frame/dispatch [:navigate-to :group-chat-invite])
|
||||
:accessibility-label :invitation-requests-button}
|
||||
[react/view {:style (style/add-contact)}
|
||||
[react/text {:style style/add-contact-text}
|
||||
(i18n/label :t/group-membership-request)]]])))))
|
||||
|
||||
(defn add-contact-bar [public-key]
|
||||
(let [added? @(re-frame/subscribe [:contacts/contact-added? public-key])]
|
||||
(when-not added?
|
||||
|
@ -58,6 +75,7 @@
|
|||
(defn chat-intro [{:keys [chat-id
|
||||
chat-name
|
||||
group-chat
|
||||
invitation-admin
|
||||
contact-name
|
||||
public?
|
||||
color
|
||||
|
@ -78,6 +96,7 @@
|
|||
;; Description section
|
||||
(if group-chat
|
||||
[chat.group/group-chat-description-container {:chat-id chat-id
|
||||
:invitation-admin invitation-admin
|
||||
:loading-messages? loading-messages?
|
||||
:chat-name chat-name
|
||||
:public? public?
|
||||
|
@ -95,7 +114,7 @@
|
|||
(chat-intro (assoc opts :contact-name (first contact-names)))))
|
||||
|
||||
(defn chat-intro-header-container
|
||||
[{:keys [group-chat
|
||||
[{:keys [group-chat invitation-admin
|
||||
might-have-join-time-messages?
|
||||
color chat-id chat-name
|
||||
public?]}
|
||||
|
@ -108,6 +127,7 @@
|
|||
(let [opts
|
||||
{:chat-id chat-id
|
||||
:group-chat group-chat
|
||||
:invitation-admin invitation-admin
|
||||
:chat-name chat-name
|
||||
:public? public?
|
||||
:color color
|
||||
|
@ -136,7 +156,7 @@
|
|||
|
||||
(defn messages-view
|
||||
[{:keys [chat bottom-space pan-responder space-keeper]}]
|
||||
(let [{:keys [group-chat chat-id public?]} chat
|
||||
(let [{:keys [group-chat chat-id public? invitation-admin]} chat
|
||||
|
||||
messages @(re-frame/subscribe [:chats/current-chat-messages-stream])
|
||||
no-messages? @(re-frame/subscribe [:chats/current-chat-no-messages?])
|
||||
|
@ -147,7 +167,7 @@
|
|||
{:key-fn #(or (:message-id %) (:value %))
|
||||
:ref #(reset! messages-list-ref %)
|
||||
:header (when (and group-chat (not public?))
|
||||
[chat.group/group-chat-footer chat-id])
|
||||
[chat.group/group-chat-footer chat-id invitation-admin])
|
||||
:footer [:<>
|
||||
[chat-intro-header-container chat no-messages?]
|
||||
(when (and (not group-chat) (not public?))
|
||||
|
@ -188,6 +208,41 @@
|
|||
[audio-message/audio-message-view]
|
||||
nil))
|
||||
|
||||
(defn invitation-bar [chat-id]
|
||||
(let [{:keys [state chat-id] :as invitation}
|
||||
(first @(re-frame/subscribe [:group-chat/invitations-by-chat-id chat-id]))
|
||||
{:keys [retry? message]} @(re-frame/subscribe [:chats/current-chat-membership])]
|
||||
[react/view {:margin-horizontal 16 :margin-top 10}
|
||||
(cond
|
||||
(and invitation (= constants/invitation-state-requested state) (not retry?))
|
||||
[toolbar/toolbar {:show-border? true
|
||||
:center
|
||||
[quo/button
|
||||
{:type :secondary
|
||||
:disabled true}
|
||||
(i18n/label :t/request-pending)]}]
|
||||
|
||||
(and invitation (= constants/invitation-state-rejected state) (not retry?))
|
||||
[toolbar/toolbar {:show-border? true
|
||||
:right
|
||||
[quo/button
|
||||
{:type :secondary
|
||||
:on-press #(re-frame/dispatch [:group-chats.ui/membership-retry])}
|
||||
(i18n/label :t/mailserver-retry)]
|
||||
:left
|
||||
[quo/button
|
||||
{:type :secondary
|
||||
:on-press #(re-frame/dispatch [:group-chats.ui/remove-chat-confirmed chat-id])}
|
||||
(i18n/label :t/remove-group)]}]
|
||||
:else
|
||||
[toolbar/toolbar {:show-border? true
|
||||
:center
|
||||
[quo/button
|
||||
{:type :secondary
|
||||
:disabled (string/blank? message)
|
||||
:on-press #(re-frame/dispatch [:send-group-chat-membership-request])}
|
||||
(i18n/label :t/request-membership)]}])]))
|
||||
|
||||
(defn chat []
|
||||
(let [bottom-space (reagent/atom 0)
|
||||
panel-space (reagent/atom 0)
|
||||
|
@ -220,18 +275,23 @@
|
|||
(when panel
|
||||
(js/setTimeout #(react/dismiss-keyboard!) 100)))]
|
||||
(fn []
|
||||
(let [{:keys [chat-id show-input? group-chat] :as current-chat}
|
||||
(let [{:keys [chat-id show-input? group-chat admins invitation-admin] :as current-chat}
|
||||
@(re-frame/subscribe [:chats/current-chat])]
|
||||
[react/view {:style {:flex 1}}
|
||||
[connectivity/connectivity
|
||||
[topbar]
|
||||
[react/view {:style {:flex 1}}
|
||||
(when-not group-chat
|
||||
(if group-chat
|
||||
[invitation-requests chat-id admins]
|
||||
[add-contact-bar chat-id])
|
||||
[messages-view {:chat current-chat
|
||||
:bottom-space (max @bottom-space @panel-space)
|
||||
:pan-responder pan-responder
|
||||
:space-keeper space-keeper}]]]
|
||||
(when (and group-chat invitation-admin)
|
||||
[accessory/view {:y position-y
|
||||
:on-update-inset on-update}
|
||||
[invitation-bar chat-id]])
|
||||
(when show-input?
|
||||
[accessory/view {:y position-y
|
||||
:pan-state pan-state
|
||||
|
|
|
@ -11,7 +11,17 @@
|
|||
[status-im.ui.screens.chat.sheets :as chat.sheets]
|
||||
[status-im.ui.screens.profile.components.styles
|
||||
:as
|
||||
profile.components.styles])
|
||||
profile.components.styles]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.copyable-text :as copyable-text]
|
||||
[status-im.ui.components.list-selection :as list-selection]
|
||||
[status-im.utils.universal-links.core :as universal-links]
|
||||
[status-im.ui.components.common.common :as components.common]
|
||||
[status-im.ui.screens.chat.message.message :as message]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.ui.screens.chat.utils :as chat.utils]
|
||||
[status-im.utils.debounce :as debounce])
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
||||
|
||||
(defn member-sheet [chat-id member us-admin?]
|
||||
|
@ -87,6 +97,84 @@
|
|||
:on-press #(re-frame/dispatch [:navigate-to :add-participants-toggle-list])}])
|
||||
[chat-group-members-view chat-id admin? current-pk]])
|
||||
|
||||
(defn hide-sheet-and-dispatch [event]
|
||||
(re-frame/dispatch [:bottom-sheet/hide])
|
||||
(debounce/dispatch-and-chill event 2000))
|
||||
|
||||
(defn invitation-sheet [{:keys [introduction-message id]} contact]
|
||||
(let [members @(re-frame/subscribe [:contacts/current-chat-contacts])
|
||||
allow-adding-members? (< (count members) constants/max-group-chat-participants)]
|
||||
[react/view
|
||||
(let [message {:content {:parsed-text
|
||||
[{:type "paragraph"
|
||||
:children [{:literal introduction-message}]}]}
|
||||
:content-type constants/content-type-text}]
|
||||
[react/view {:margin-bottom 8 :margin-right 16}
|
||||
[react/view {:padding-left 72}
|
||||
(chat.utils/format-author (multiaccounts/displayed-name contact))]
|
||||
[react/view {:flex-direction :row :align-items :flex-end}
|
||||
[react/view {:padding-left 16 :padding-top 4}
|
||||
[photos/photo (multiaccounts/displayed-photo contact) {:size 36}]]
|
||||
[message/->message message {:on-long-press identity}]]])
|
||||
[quo/list-item
|
||||
{:theme :accent
|
||||
:disabled (not allow-adding-members?)
|
||||
:title (i18n/label :t/accept)
|
||||
:subtitle (when-not allow-adding-members? (i18n/label :t/members-limit-reached))
|
||||
:accessibility-label :fetch-history-button
|
||||
:icon :main-icons/checkmark-circle
|
||||
:on-press #(hide-sheet-and-dispatch
|
||||
[:group-chats.ui/add-members-from-invitation id (:public-key contact)])}]
|
||||
[quo/list-item
|
||||
{:theme :negative
|
||||
:title (i18n/label :t/decline)
|
||||
:accessibility-label :delete-chat-button
|
||||
:icon :main-icons/cancel
|
||||
:on-press #(hide-sheet-and-dispatch [:send-group-chat-membership-rejection id])}]]))
|
||||
|
||||
(defn contacts-list-item [{:keys [from] :as invitation}]
|
||||
(let [contact (or @(re-frame/subscribe [:contacts/contact-by-identity from]) {:public-key from})]
|
||||
[quo/list-item
|
||||
{:title (multiaccounts/displayed-name contact)
|
||||
:icon [chat-icon/contact-icon-contacts-tab
|
||||
(multiaccounts/displayed-photo contact)]
|
||||
:on-press #(re-frame/dispatch [:bottom-sheet/show-sheet
|
||||
{:content (fn []
|
||||
[invitation-sheet invitation contact])}])}]))
|
||||
|
||||
(defview group-chat-invite []
|
||||
(letsubs [{:keys [chat-id chat-name]} [:chats/current-chat]
|
||||
current-pk [:multiaccount/public-key]]
|
||||
(let [invite-link (universal-links/generate-link
|
||||
:group-chat
|
||||
:external
|
||||
(str "args?a=" current-pk "&a1=" (js/encodeURI chat-name) "&a2=" chat-id))
|
||||
invitations @(re-frame/subscribe [:group-chat/pending-invitations-by-chat-id chat-id])]
|
||||
[react/view {:flex 1}
|
||||
[topbar/topbar {:title (i18n/label :t/group-invite)
|
||||
:accessories [{:icon :main-icons/share
|
||||
:handler #(list-selection/open-share {:message invite-link})}]}]
|
||||
[react/scroll-view {:flex 1}
|
||||
[react/view {:margin-top 26}
|
||||
[react/view {:padding-horizontal 16}
|
||||
[react/text {:style {:color colors/gray}} (i18n/label :t/group-invite-link)]
|
||||
[copyable-text/copyable-text-view
|
||||
{:copied-text invite-link}
|
||||
[react/view {:border-width 1 :border-color colors/gray-lighter
|
||||
:justify-content :center :margin-top 10
|
||||
:border-radius 8 :padding-horizontal 16 :padding-vertical 11}
|
||||
[react/text invite-link]]]
|
||||
[react/text {:style {:color colors/gray :margin-top 22}}
|
||||
(i18n/label :t/pending-invitations)]]
|
||||
(if (seq invitations)
|
||||
[list/flat-list
|
||||
{:data invitations
|
||||
:key-fn :id
|
||||
:render-fn contacts-list-item}]
|
||||
[react/text {:style {:color colors/gray :margin-top 28 :text-align :center
|
||||
:padding-horizontal 16}}
|
||||
(i18n/label :t/empty-pending-invitations-descr)])]]])))
|
||||
|
||||
(defview group-chat-profile []
|
||||
(letsubs [{:keys [admins chat-id joined? chat-name color contacts] :as current-chat} [:chats/current-chat]
|
||||
members [:contacts/current-chat-contacts]
|
||||
|
@ -111,6 +199,18 @@
|
|||
:subtitle (i18n/label :t/members-count {:count (count contacts)})
|
||||
:subtitle-icon :icons/tiny-group})}
|
||||
[react/view profile.components.styles/profile-form
|
||||
(when admin?
|
||||
[quo/list-item
|
||||
{:chevron true
|
||||
:title (i18n/label :t/group-invite)
|
||||
:accessibility-label :invite-chat-button
|
||||
:icon :main-icons/share
|
||||
:accessory (let [invitations
|
||||
(count @(re-frame/subscribe
|
||||
[:group-chat/pending-invitations-by-chat-id chat-id]))]
|
||||
(when (pos? invitations)
|
||||
[components.common/counter {:size 22} invitations]))
|
||||
:on-press #(re-frame/dispatch [:navigate-to :group-chat-invite])}])
|
||||
(when joined?
|
||||
[quo/list-item
|
||||
{:theme :negative
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
{:name :group-chat-profile
|
||||
:insets {:top false}
|
||||
:component profile.group-chat/group-chat-profile}
|
||||
{:name :group-chat-invite
|
||||
:component profile.group-chat/group-chat-invite}
|
||||
{:name :stickers
|
||||
:component stickers/packs}
|
||||
{:name :stickers-pack
|
||||
|
|
|
@ -133,3 +133,23 @@
|
|||
|
||||
(defn url-sanitized? [uri]
|
||||
(not (nil? (re-find #"^(https:)([/|.|\w|\s|-])*\.(?:jpg|svg|png)$" uri))))
|
||||
|
||||
(defn- split-param [param]
|
||||
(->
|
||||
(string/split param #"=")
|
||||
(concat (repeat ""))
|
||||
(->>
|
||||
(take 2))))
|
||||
|
||||
(defn- url-decode
|
||||
[string]
|
||||
(some-> string str (string/replace #"\+" "%20") (js/decodeURIComponent)))
|
||||
|
||||
(defn query->map
|
||||
[qstr]
|
||||
(when-not (string/blank? qstr)
|
||||
(some->> (string/split qstr #"&")
|
||||
seq
|
||||
(mapcat split-param)
|
||||
(map url-decode)
|
||||
(apply hash-map))))
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.acquisition.core :as acquisition]
|
||||
[status-im.wallet.choose-recipient.core :as choose-recipient]))
|
||||
[status-im.wallet.choose-recipient.core :as choose-recipient]
|
||||
[status-im.group-chats.core :as group-chats]))
|
||||
|
||||
;; TODO(yenda) investigate why `handle-universal-link` event is
|
||||
;; dispatched 7 times for the same link
|
||||
|
@ -22,10 +23,11 @@
|
|||
(def domains {:external "https://join.status.im"
|
||||
:internal "status-im:/"})
|
||||
|
||||
(def links {:public-chat "%s/%s"
|
||||
(def links {:public-chat "%s/%s"
|
||||
:private-chat "%s/p/%s"
|
||||
:user "%s/u/%s"
|
||||
:browse "%s/b/%s"})
|
||||
:group-chat "%s/g/%s"
|
||||
:user "%s/u/%s"
|
||||
:browse "%s/b/%s"})
|
||||
|
||||
(defn generate-link [link-type domain-type param]
|
||||
(gstring/format (get links link-type)
|
||||
|
@ -44,6 +46,10 @@
|
|||
(log/info "universal-links: handling browse" url)
|
||||
{:browser/show-browser-selection url})
|
||||
|
||||
(fx/defn handle-group-chat [cofx params]
|
||||
(log/info "universal-links: handling group" params)
|
||||
(group-chats/create-from-link cofx params))
|
||||
|
||||
(fx/defn handle-private-chat [{:keys [db] :as cofx} {:keys [chat-id]}]
|
||||
(log/info "universal-links: handling private chat" chat-id)
|
||||
(when chat-id
|
||||
|
@ -93,6 +99,7 @@
|
|||
{:events [::match-value]}
|
||||
[cofx url {:keys [type] :as data}]
|
||||
(case type
|
||||
:group-chat (handle-group-chat cofx data)
|
||||
:public-chat (handle-public-chat cofx data)
|
||||
:private-chat (handle-private-chat cofx data)
|
||||
:contact (handle-view-profile cofx data)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.56.9",
|
||||
"commit-sha1": "435eacecb587571887ef547d78f82d67f026db28",
|
||||
"src-sha256": "0nk4fn7avj5yqsbvc3yjb2hdfhswxsvyvmxcgf0g3rr9rdhm7m19"
|
||||
"version": "0.60.0",
|
||||
"commit-sha1": "3b748a2e467fe0850b2630947a9dadfe180f35fb",
|
||||
"src-sha256": "1fvrh62480xfjcaagvwl7n7njhavb6plw48rv9w5vy9ykqj089bx"
|
||||
}
|
||||
|
|
|
@ -1230,5 +1230,18 @@
|
|||
"update-to-see-sticker": "Update to latest version to see a nice sticker here!",
|
||||
"nickname": "Nickname",
|
||||
"add-nickname": "Add a nickname (optional)",
|
||||
"nickname-description": "Nicknames help you identify others in Status.\nOnly you can see the nicknames you’ve added"
|
||||
"nickname-description": "Nicknames help you identify others in Status.\nOnly you can see the nicknames you’ve added",
|
||||
"accept": "Accept",
|
||||
"group-invite": "Group invite",
|
||||
"group-invite-link": "Group invite link",
|
||||
"pending-invitations": "Pending membership requests",
|
||||
"empty-pending-invitations-descr": "People who wish to join the group\nvia an invite link will appear here",
|
||||
"introduce-yourself": "Introduce yourself with a brief message",
|
||||
"request-pending": "Request pending…",
|
||||
"membership-declined": "Membership request was declined",
|
||||
"remove-group": "Remove group",
|
||||
"request-membership": "Request membership",
|
||||
"membership-description": "Group membership requires you to be accepted by the group admin",
|
||||
"group-membership-request": "Group membership request",
|
||||
"members-limit-reached": "Members limit reached"
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue