diff --git a/src/status_im/chat/handlers.cljs b/src/status_im/chat/handlers.cljs index d7d1f14046..5f74bb5a36 100644 --- a/src/status_im/chat/handlers.cljs +++ b/src/status_im/chat/handlers.cljs @@ -406,9 +406,8 @@ (register-handler :add-chat (u/side-effect! - (fn [{:keys [chats]} [_ chat-id chat]] - (when-not (get chats chat-id) - (dispatch [::add-chat chat-id chat]))))) + (fn [_ [_ chat-id chat]] + (dispatch [::add-chat chat-id chat])))) (register-handler ::add-chat (-> add-new-chat @@ -572,3 +571,29 @@ (let [chat (-> (chats/get-by-id chat-id) (update :clock-value inc))] (dispatch [:update-chat! chat]))))) + +(register-handler :update-group-message + (u/side-effect! + (fn [{:keys [current-public-key web3 chats]} + [_ {:keys [from] + {:keys [group-id keypair timestamp]} :payload}]] + (let [{:keys [private public]} keypair] + (let [{:keys [updated-at removed-at]} (chats/get-by-id group-id) + is-active (chats/is-active? group-id) + chat {:chat-id group-id + :public-key public + :private-key private + :updated-at timestamp}] + (when (and (= from (get-in chats [group-id :group-admin])) + (or (not (chats/exists? group-id)) + is-active + (> timestamp removed-at) + (> timestamp updated-at))) + (dispatch [:update-chat! chat]) + (when is-active + (protocol/start-watching-group! + {:web3 web3 + :group-id group-id + :identity current-public-key + :keypair keypair + :callback #(dispatch [:incoming-message %1 %2])})))))))) diff --git a/src/status_im/chat/handlers/receive_message.cljs b/src/status_im/chat/handlers/receive_message.cljs index d08bb22986..2e809e9e33 100644 --- a/src/status_im/chat/handlers/receive_message.cljs +++ b/src/status_im/chat/handlers/receive_message.cljs @@ -58,13 +58,13 @@ (dispatch [:upsert-chat! {:chat-id chat-id' :group-chat group-chat? :clock-value clock-value}]) - (dispatch [::add-message chat-id message']) + (dispatch [::add-message chat-id' message']) (when (= (:content-type message') content-type-command-request) (dispatch [:add-request chat-id' message'])) (dispatch [:add-unviewed-message chat-id' message-id])) (if (and (= (:content-type message) content-type-command) - (not= chat-id wallet-chat-id) + (not= chat-id' wallet-chat-id) (= "send" (get-in message [:content :command]))) (add-message-to-wallet db message))))) diff --git a/src/status_im/chat/views/message_input.cljs b/src/status_im/chat/views/message_input.cljs index 235bd52cb2..b9db1e67bc 100644 --- a/src/status_im/chat/views/message_input.cljs +++ b/src/status_im/chat/views/message_input.cljs @@ -39,9 +39,10 @@ (defview message-input [input-options set-layout-size] [input-message [:get-chat-input-text] - disable? [:get :disable-input]] + disable? [:get :disable-input] + active? [:chat :is-active]] [text-input (merge - (plain-input-options disable?) + (plain-input-options (or disable? (not active?))) {:placeholder-text-color :#c0c5c9 :auto-focus false :blur-on-submit true diff --git a/src/status_im/data_store/realm/schemas/account/v4/chat.cljs b/src/status_im/data_store/realm/schemas/account/v4/chat.cljs index 41e1458d68..185acef8af 100644 --- a/src/status_im/data_store/realm/schemas/account/v4/chat.cljs +++ b/src/status_im/data_store/realm/schemas/account/v4/chat.cljs @@ -10,6 +10,8 @@ :default default-chat-color} :group-chat {:type :bool :indexed true} + :group-admin {:type :string + :optional true} :is-active :bool :timestamp :int :contacts {:type :list @@ -20,6 +22,12 @@ :optional true} :removed-at {:type :int :optional true} + :removed-from-at {:type :int + :optional true} + :added-to-at {:type :int + :optional true} + :updated-at {:type :int + :optional true} :last-message-id :string :public-key {:type :string :optional true} diff --git a/src/status_im/group_settings/handlers.cljs b/src/status_im/group_settings/handlers.cljs index 204df18947..de0ecaa99d 100644 --- a/src/status_im/group_settings/handlers.cljs +++ b/src/status_im/group_settings/handlers.cljs @@ -80,10 +80,45 @@ (chats/remove-contacts current-chat-id selected-participants)) (defn notify-about-removing! - [{:keys [current-chat-id selected-participants]} _] - (doseq [participant selected-participants] - ;;todo implement - )) + [{: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" @@ -113,7 +148,7 @@ ;; about the api call that removes participants from the group? ((after remove-members-from-chat!)) ;; todo uncomment - ;((after notify-about-removing!)) + ((after notify-about-removing!)) ((after create-removing-messages!)) ((enrich deselect-members)) debug)) @@ -130,25 +165,40 @@ (defn notify-about-new-members! [{:keys [current-chat-id selected-participants current-public-key chats web3]} _] - (let [{:keys [public-key private-key name contacts]} (chats current-chat-id) - identities (map :identity contacts) - keypair {:public public-key - :private private-key}] + (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! - {:web3 web3 - :group {:id current-chat-id - :name name - :contacts (conj identities current-public-key) - :admin current-public-key - :keypair keypair} - :identities selected-participants - :message {:from current-public-key - :message-id (random/id)}}) + (-> 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 keypair + :keypair new-keypair :message {:from current-public-key :message-id (random/id)}})))) diff --git a/src/status_im/group_settings/screen.cljs b/src/status_im/group_settings/screen.cljs index 68d82a9f87..ae733b9642 100644 --- a/src/status_im/group_settings/screen.cljs +++ b/src/status_im/group_settings/screen.cljs @@ -37,8 +37,9 @@ [view st/modal-inner-container [text {:style st/modal-member-name} name] [touchable-highlight {:on-press remove-member} - [text {:style st/modal-remove-text} - (label :t/remove)]]]]])) + [view + [text {:style st/modal-remove-text} + (label :t/remove)]]]]]])) (defview chat-members [] [members [:current-chat-contacts]] @@ -145,19 +146,26 @@ (when (pos? (count validation-messages)) [text {:style st/chat-name-validation-message} (first validation-messages)])]) +(defview members [] + [current-pk [:get :current-public-key] + group-admin [:chat :group-admin]] + (when (= current-pk group-admin) + [view + [text {:style st/members-text} (label :t/members-title)] + [touchable-highlight {:on-press #(dispatch [:navigate-to :add-participants])} + ;; TODO add participants view is not in design + [view st/add-members-container + [icon :add_gray st/add-members-icon] + [text {:style st/add-members-text} + (label :t/add-members)]]] + [chat-members]])) + (defn group-settings [] [view st/group-settings [new-group-toolbar] [scroll-view st/body [chat-name] - [text {:style st/members-text} (label :t/members-title)] - [touchable-highlight {:on-press #(dispatch [:navigate-to :add-participants])} - ;; TODO add participants view is not in design - [view st/add-members-container - [icon :add_gray st/add-members-icon] - [text {:style st/add-members-text} - (label :t/add-members)]]] - [chat-members] + [members] [text {:style st/settings-text} (label :t/settings)] [settings-view]] diff --git a/src/status_im/new_group/handlers.cljs b/src/status_im/new_group/handlers.cljs index 72d1aa1081..2ff5759274 100644 --- a/src/status_im/new_group/handlers.cljs +++ b/src/status_im/new_group/handlers.cljs @@ -30,8 +30,8 @@ (s/join ", "))) (defn prepare-chat - [{:keys [selected-contacts] :as db} [_ group-name]] - (let [contacts (mapv #(hash-map :identity %) selected-contacts) + [{: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)) @@ -42,6 +42,7 @@ :name chat-name :color default-chat-color :group-chat true + :group-admin current-public-key :is-active true :timestamp (.getTime (js/Date.)) :contacts contacts}))) @@ -92,28 +93,31 @@ (register-handler :group-chat-invite-received (u/side-effect! - (fn [{:keys [current-public-key web3] :as db} - [_ {{:keys [group-id group-name contacts keypair timestamp] :as payload} :payload}]] + (fn [{:keys [current-public-key web3]} + [_ {:keys [from] + {:keys [group-id group-name contacts keypair timestamp]} :payload}]] (let [{:keys [private public]} keypair] (let [removed-at (chats/removed-at group-id) - is-active (chats/is-active? group-id) - contacts' (keep (fn [ident] - (when (not= ident current-public-key) - {:identity ident})) contacts) - chat {:name group-name - :group-chat true - :public-key public - :private-key private - :contacts contacts'}] - (when (or (not (chats/exists? group-id)) - is-active - (> timestamp removed-at)) - (dispatch [:add-chat group-id (assoc chat :is-active true - :timestamp timestamp)]) - (when-not is-active - (protocol/start-watching-group! - {:web3 web3 - :group-id group-id - :identity current-public-key - :keypair keypair - :callback #(dispatch [:incoming-message %1 %2])})))))))) + contacts' (keep (fn [ident] + (when (not= ident current-public-key) + {:identity ident})) contacts) + chat {: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} + {:keys [removed-from-at] :as chat-from-db} (chats/get-by-id group-id)] + (when (or (not chat-from-db) + (and (> timestamp removed-at) + (> timestamp removed-from-at))) + (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])}))))))) diff --git a/src/status_im/protocol/core.cljs b/src/status_im/protocol/core.cljs index ee81cb1f5f..d43c216ee4 100644 --- a/src/status_im/protocol/core.cljs +++ b/src/status_im/protocol/core.cljs @@ -63,21 +63,15 @@ (defn init-whisper! [{:keys [rpc-url identity groups callback - hashtags contacts profile-keypair pending-messages] + contacts profile-keypair pending-messages] :as options}] {:pre [(valid? ::options options)]} (debug :init-whisper) (stop-watching-all!) (d/reset-all-pending-messages!) - (let [web3 (u/make-web3 rpc-url) + (let [web3 (u/make-web3 rpc-url) listener-options {:web3 web3 :identity identity}] - ;; start listening to user's inbox - (f/add-filter! - web3 - {:to identity - :topics [f/status-topic]} - (l/message-listener (assoc listener-options :callback callback))) ;; start listening to groups (doseq [{:keys [chat-id keypair]} groups] (f/add-filter! @@ -85,6 +79,12 @@ {:topics [chat-id]} (l/message-listener (assoc listener-options :callback callback :keypair keypair)))) + ;; start listening to user's inbox + (f/add-filter! + web3 + {:to identity + :topics [f/status-topic]} + (l/message-listener (assoc listener-options :callback callback))) ;; start listening to profiles (doseq [{:keys [identity keypair]} contacts] (watch-user! {:web3 web3 diff --git a/src/status_im/protocol/group.cljs b/src/status_im/protocol/group.cljs index bf7aa870c6..1cf9342bf0 100644 --- a/src/status_im/protocol/group.cljs +++ b/src/status_im/protocol/group.cljs @@ -66,9 +66,11 @@ (s/def :group/name string?) (s/def :group/id string?) +(s/def :group/admin string?) (s/def :group/contacts (s/* string?)) (s/def ::group - (s/keys :req-un [:group/name :group/id :group/contacts :message/keypair])) + (s/keys :req-un + [:group/name :group/id :group/contacts :message/keypair :group/admin])) (s/def :invite/options (s/keys :req-un [:options/web3 :protocol/message ::group ::identities])) diff --git a/src/status_im/protocol/handlers.cljs b/src/status_im/protocol/handlers.cljs index d9f2a992cb..11759c91d1 100644 --- a/src/status_im/protocol/handlers.cljs +++ b/src/status_im/protocol/handlers.cljs @@ -83,7 +83,9 @@ (dispatch [:pending-message-remove message])) :seen (dispatch [:message-seen message]) :group-invitation (dispatch [:group-chat-invite-received message]) + :update-group (dispatch [:update-group-message message]) :add-group-identity (dispatch [:participant-invited-to-group message]) + :remove-group-identity (dispatch [:participant-removed-from-group message]) :leave-group (dispatch [:participant-left-group message]) :contact-request (dispatch [:contact-request-received message]) :discover (dispatch [:status-received message]) @@ -117,23 +119,22 @@ invitee-name (if (= identity current-identity) (label :t/You) (:name (contacts/get-by-id identity)))] - (messages/save chat-id {:from "system" - :message-id message-id - :content (str (or inviter-name from) " " (label :t/invited) " " (or invitee-name identity)) - :content-type text-content-type}))) + {:from "system" + :group-id chat-id + :message-id message-id + :content (str (or inviter-name from) " " (label :t/invited) " " (or invitee-name identity)) + :content-type text-content-type})) -(defn participant-removed-from-group-message [chat-id identity from message-id] +(defn participant-removed-from-group-message [identity from message-id] (let [remover-name (:name (contacts/get-by-id from)) removed-name (:name (contacts/get-by-id identity))] (->> (str (or remover-name from) " " (label :t/removed) " " (or removed-name identity)) - (system-message message-id) - (messages/save chat-id)))) + (system-message message-id)))) -(defn you-removed-from-group-message [chat-id from message-id] +(defn you-removed-from-group-message [from message-id] (let [remover-name (:name (contacts/get-by-id from))] (->> (str (or remover-name from) " " (label :t/removed-from-chat)) - (system-message message-id) - (messages/save chat-id)))) + (system-message message-id)))) (defn participant-left-group-message [chat-id from message-id] (let [left-name (:name (contacts/get-by-id from))] @@ -149,17 +150,38 @@ (register-handler :participant-removed-from-group (u/side-effect! - (fn [_ [action from group-id identity message-id]] - (log/debug action message-id from group-id identity) - (chats/remove-contacts group-id [identity]) - (participant-removed-from-group-message group-id identity from message-id)))) + (fn [{:keys [current-public-key chats]} + [_ {:keys [from] + {:keys [group-id identity message-id]} :payload + :as message}]] + (when-not (messages/get-by-id message-id) + (let [admin (get-in chats [group-id :group-admin])] + (when (= admin from) + (if (= current-public-key identity) + (dispatch [::you-removed-from-group message]) + (let [message + (assoc + (participant-removed-from-group-message identity from message-id) + :group-id group-id)] + (chats/remove-contacts group-id [identity]) + (dispatch [:received-message message]))))))))) -(register-handler :you-removed-from-group +(register-handler ::you-removed-from-group (u/side-effect! - (fn [_ [action from group-id message-id]] - (log/debug action message-id from group-id) - (you-removed-from-group-message group-id from message-id) - (chats/set-active group-id false)))) + (fn [{:keys [web3]} + [_ {:keys [from] + {:keys [group-id message-id timestamp]} :payload}]] + (let [{:keys [added-to-at]} (chats/get-by-id group-id)] + (when (> timestamp added-to-at) + (let [message + (-> (you-removed-from-group-message from message-id) + (assoc :group-id group-id))] + (dispatch [:received-message message])) + (protocol/stop-watching-group! {:web3 web3 + :group-id group-id}) + (dispatch [:update-chat! {:chat-id group-id + :removed-from-at timestamp + :is-active false}])))))) (register-handler :participant-left-group (u/side-effect! @@ -186,12 +208,16 @@ (register-handler :participant-invited-to-group (u/side-effect! - (fn [{:keys [current-public-key]} + (fn [{:keys [current-public-key chats]} [_ {:keys [from] {:keys [group-id identity message-id]} :payload}]] - (participant-invited-to-group-message group-id current-public-key identity from message-id) - (when-not (= current-public-key identity) - (dispatch [:add-contact-to-group! group-id identity]))))) + (let [admin (get-in chats [group-id :group-admin])] + (when (= from admin) + (dispatch + [:received-message + (participant-invited-to-group-message group-id current-public-key identity from message-id)]) + (when-not (= current-public-key identity) + (dispatch [:add-contact-to-group! group-id identity]))))))) (register-handler :add-contact-to-group! (u/side-effect! diff --git a/src/status_im/protocol/listeners.cljs b/src/status_im/protocol/listeners.cljs index 913e87ada6..535f520e86 100644 --- a/src/status_im/protocol/listeners.cljs +++ b/src/status_im/protocol/listeners.cljs @@ -12,10 +12,10 @@ (defn- decrypt [key content] (try - (r/read-string (e/decrypt key content)) + {:content (r/read-string (e/decrypt key content))} (catch :default err - (log/warn :decrypt-error err) - nil))) + (debug :decrypt-error err) + {:error err}))) (defn- parse-content [key {:keys [content]} was-encrypted?] (debug :parse-content @@ -23,7 +23,7 @@ "Content exists:" (not (nil? content))) (if (and (not was-encrypted?) key content) (decrypt key content) - content)) + {:content content})) (defn message-listener [{:keys [web3 identity callback keypair]}] @@ -42,9 +42,12 @@ (i/normalize-hex from)) ;; allow user to receive his own discoveries (= type :discover)) - (let [content (parse-content (:private keypair) payload' (not= "0x0" to)) - payload'' (assoc payload' :content content) - - message' (assoc message :payload payload'')] - (callback (if ack? :ack type) message') - (ack/check-ack! web3 from payload'' identity))))))) + (let [{:keys [content error]} (parse-content (:private keypair) + payload' + (not= "0x0" to))] + (if error + (debug :failed-to-handle-message error) + (let [payload'' (assoc payload' :content content) + message' (assoc message :payload payload'')] + (callback (if ack? :ack type) message') + (ack/check-ack! web3 from payload'' identity)))))))))