Allow admin to leave chat

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-11-29 11:06:06 +01:00
parent 2a828007fb
commit dfbc27c5d7
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
10 changed files with 130 additions and 34 deletions

View File

@ -225,7 +225,8 @@
"Validate and return a valid chat-id"
(cond
(and (= :group-user-message message-type)
(get-in cofx [:db :chats chat-id :contacts from])) chat-id
(and (get-in cofx [:db :chats chat-id :contacts from])
(get-in cofx [:db :chats chat-id :contacts (accounts.db/current-public-key cofx)]))) chat-id
(and (= :public-group-user-message message-type)
(get-in cofx [:db :chats chat-id :public?])) chat-id
(and (= :user-message message-type)

View File

@ -128,7 +128,7 @@
(query-fn (comp participant-set :public-key) (vals all-contacts))))
(defn get-all-contacts-in-group-chat
[members contacts current-account]
[members admins contacts current-account]
(let [current-account-contact (-> current-account
(select-keys [:name :photo-path :public-key]))
all-contacts (assoc contacts (:public-key current-account-contact) current-account-contact)]
@ -136,4 +136,7 @@
(map #(or (get all-contacts %)
(public-key->new-contact %)))
(remove :dapp?)
(sort-by (comp clojure.string/lower-case :name)))))
(sort-by (comp clojure.string/lower-case :name))
(map #(if (admins (:public-key %))
(assoc % :admin? true)
%)))))

View File

@ -102,8 +102,8 @@
:<- [:chats/current-chat]
:<- [:contacts/contacts]
:<- [:account/account]
(fn [[{:keys [contacts]} all-contacts current-account]]
(contact.db/get-all-contacts-in-group-chat contacts all-contacts current-account)))
(fn [[{:keys [contacts admins]} all-contacts current-account]]
(contact.db/get-all-contacts-in-group-chat contacts admins all-contacts current-account)))
(re-frame/reg-sub
:contacts/contacts-by-chat

View File

@ -1056,6 +1056,11 @@
(fn [cofx [_ chat-id public-key]]
(group-chats/remove-member cofx chat-id public-key)))
(handlers/register-handler-fx
:group-chats.ui/make-admin-pressed
(fn [cofx [_ chat-id public-key]]
(group-chats/make-admin cofx chat-id public-key)))
(handlers/register-handler-fx
:group-chats.ui/remove-chat-pressed
(fn [_ [_ chat-id group?]]

View File

@ -28,7 +28,6 @@
;; It is importatn that the from field is not trusted for updates coming from the outside.
;; When messages are coming from the database it can be trustured as already verified by us.
(defn sort-events [events]
(sort-by :clock-value events))
@ -91,12 +90,9 @@
(and (admins from)
(not (admins member)))
;; Members can remove themselves
(and (not (admins member))
(contacts member)
(= from member)))
(= from member))
"admin-removed" (and (admins from)
(= from member)
(not= #{from} admins))
(= from member))
false)))
(defn send-membership-update
@ -204,6 +200,22 @@
:from my-public-key
:events [remove-event]}})))
(fx/defn make-admin
"Format group update with make admin message and sign membership"
[{:keys [db] :as cofx} chat-id member]
(let [my-public-key (accounts.db/current-public-key cofx)
last-clock-value (get-last-clock-value cofx chat-id)
chat (get-in cofx [:db :chats chat-id])
event {:type "admins-added"
:members [member]
:clock-value (utils.clocks/send last-clock-value)}]
(when (valid-event? chat (assoc event
:from
my-public-key))
{:group-chats/sign-membership {:chat-id chat-id
:from my-public-key
:events [event]}})))
(fx/defn add-members
"Add members to a group chat"
[{{:keys [current-chat-id selected-participants]} :db :as cofx}]
@ -283,6 +295,7 @@
(reduce (fn [acc member] (assoc-in acc [member :admin-added] clock-value)) $ members))
"member-removed" (-> group
(update :contacts disj member)
(update :admins disj member)
(assoc-in [member :removed] clock-value))
"admin-removed" (-> group
(update :admins disj member)
@ -305,6 +318,7 @@
chat-name
creator
members-added
admins-added
name-changed?
members-removed]}]
(let [get-contact (partial models.contact/build-contact cofx)
@ -315,6 +329,9 @@
:from (:public-key contact)})
creator-contact (when creator (get-contact creator))
name-changed-author (when name-changed? (get-contact (:name-changed-by clock-values)))
admins-added (map
get-contact
(disj admins-added creator))
contacts-added (map
get-contact
(disj members-added creator))
@ -337,6 +354,11 @@
(i18n/label :t/group-chat-member-added {:member (:name %)})
(get-in clock-values [(:public-key %) :added]))
contacts-added))
(seq admins-added) (concat (map #(format-message
%
(i18n/label :t/group-chat-admin-added {:member (:name %)})
(get-in clock-values [(:public-key %) :admin-added]))
admins-added))
(seq members-removed) (concat (map #(format-message
%
(i18n/label :t/group-chat-member-removed {:member (:name %)})
@ -350,14 +372,17 @@
(not= (:name previous-chat) (:name current-chat)))
members-added (clojure.set/difference (:contacts current-chat) (:contacts previous-chat))
members-removed (clojure.set/difference (:contacts previous-chat) (:contacts current-chat))
admins-added (clojure.set/difference (:admins current-chat) (:admins previous-chat))
membership-changes (cond-> {:chat-id chat-id
:name-changed? name-changed?
:chat-name (:name current-chat)
:admins-added admins-added
:members-added members-added
:members-removed members-removed}
(nil? previous-chat)
(assoc :creator (extract-creator current-chat)))]
(when (or name-changed?
(seq admins-added)
(seq members-added)
(seq members-removed))
(->> membership-changes

View File

@ -52,17 +52,25 @@
:action #(re-frame/dispatch [:group-chats.ui/remove-chat-pressed chat-id])
:accessibility-label :delete-chat-button}]))
(defn member-actions [chat-id member]
[{:action #(re-frame/dispatch [(if platform/desktop? :show-profile-desktop :chat.ui/show-profile) (:public-key member)])
:label (i18n/label :t/view-profile)}
{:action #(re-frame/dispatch [:group-chats.ui/remove-member-pressed chat-id (:public-key member)])
:label (i18n/label :t/remove-from-chat)}])
(defn member-actions [chat-id member us-admin?]
(concat
[{:action #(re-frame/dispatch [(if platform/desktop? :show-profile-desktop :chat.ui/show-profile) (:public-key member)])
:label (i18n/label :t/view-profile)}]
(when-not (:admin? member)
[{:action #(re-frame/dispatch [:group-chats.ui/remove-member-pressed chat-id (:public-key member)])
:label (i18n/label :t/remove-from-chat)}])
(when (and us-admin?
(not (:admin? member)))
[{:action #(re-frame/dispatch [:group-chats.ui/make-admin-pressed chat-id (:public-key member)])
:label (i18n/label :t/make-admin)}])))
(defn render-member [chat-id {:keys [name public-key] :as member} admin? current-user-identity]
[react/view
[contact/contact-view
{:contact member
:extend-options (member-actions chat-id member)
:extend-options (member-actions chat-id member admin?)
:info (when (:admin? member)
(i18n/label :t/group-chat-admin))
:extend-title name
:extended? (and admin?
(not= public-key current-user-identity))

View File

@ -66,10 +66,11 @@
(is (= :sent status)))))))
(deftest receive-group-chats
(let [cofx {:db {:chats {"chat-id" {:contacts #{"present"}}}
(let [cofx {:db {:chats {"chat-id" {:contacts #{"present" "a"}}}
:account/account {:public-key "a"}
:current-chat-id "chat-id"
:view-id :chat}}
cofx-without-member (update-in cofx [:db :chats "chat-id" :contacts] disj "a")
valid-message {:chat-id "chat-id"
:from "present"
:message-type :group-user-message
@ -93,7 +94,9 @@
(testing "a message from someone not in the list of participants"
(is (= cofx (message/receive-many cofx [bad-from-message]))))
(testing "a message with non existing chat-id"
(is (= cofx (message/receive-many cofx [bad-chat-id-message]))))))
(is (= cofx (message/receive-many cofx [bad-chat-id-message]))))
(testing "a message from a delete chat"
(is (= cofx-without-member (message/receive-many cofx-without-member [valid-message]))))))
(deftest receive-public-chats
(let [cofx {:db {:chats {"chat-id" {:public? true}}

View File

@ -6,9 +6,11 @@
(deftest contacts-subs
(testing "get-all-contacts-in-group-chat"
(with-redefs [identicon/identicon (constantly "generated")]
(let [chat-contact-ids ["0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"]
(let [chat-contact-ids #{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"
"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}
admins #{"0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"}
contacts {"0x04985040682b77a32bb4bb58268a0719bd24ca4d07c255153fe1eb2ccd5883669627bd1a092d7cc76e8e4b9104327667b19dcda3ac469f572efabe588c38c1985f"
{:description nil,
:last-updated 0,
@ -36,10 +38,12 @@
:public-key
"0x048a2f8b80c60f89a91b4c1316e56f75b087f446e7b8701ceca06a40142d8efe1f5aa36bd0fee9e248060a8d5207b43ae98bef4617c18c71e66f920f324869c09f"}]
(is (= (contact.db/get-all-contacts-in-group-chat chat-contact-ids
admins
contacts
current-account)
[{:name "Snappy Impressive Leonberger"
:photo-path "generated"
:admin? true
:public-key "0x04fcf40c526b09ff9fb22f4a5dbd08490ef9b64af700870f8a0ba2133f4251d5607ed83cd9047b8c2796576bc83fa0de23a13a4dced07654b8ff137fe744047917"}
{:name "User A"
:photo-path "photo2"

View File

@ -116,11 +116,12 @@
(testing "the name is updated"
(is (= "new-name" (:name actual-chat))))
(testing "it adds a system message"
(is (= 6 (count (:messages actual-chat)))))
(is (= 7 (count (:messages actual-chat)))))
(testing "it sets the right text"
(is (= ["group-chat-created"
"group-chat-member-added"
"group-chat-member-added"
"group-chat-admin-added"
"group-chat-member-added"
"group-chat-member-removed"
"group-chat-name-changed"]
@ -182,6 +183,31 @@
:admins #{"1"}
:contacts #{"1"}}]
(is (= expected (group-chats/build-group events)))))
(testing "an admin removing themselves"
(let [events [{:type "chat-created"
:clock-value 0
:name "chat-name"
:from "1"}
{:type "members-added"
:clock-value 1
:from "1"
:members ["2"]}
{:type "admins-added"
:clock-value 2
:from "1"
:members ["2"]}
{:type "member-removed"
:clock-value 3
:from "2"
:member "2"}]
expected {:name "chat-name"
:created-at 0
"2" {:added 1
:admin-added 2
:removed 3}
:admins #{"1"}
:contacts #{"1"}}]
(is (= expected (group-chats/build-group events)))))
(testing "name changed"
(let [events [{:type "chat-created"
:clock-value 0
@ -297,9 +323,9 @@
(testing "admins can remove non-admin members"
(is (group-chats/valid-event? multi-admin-group
{:type "member-removed" :clock-value 6 :from "1" :member "3"})))
(testing "admins can't remove themselves"
(is (not (group-chats/valid-event? multi-admin-group
{:type "member-removed" :clock-value 6 :from "1" :member "1"}))))
(testing "admins can remove themselves"
(is (group-chats/valid-event? multi-admin-group
{:type "member-removed" :clock-value 6 :from "1" :member "1"})))
(testing "participants non-admin can remove themselves"
(is (group-chats/valid-event? multi-admin-group
{:type "member-removed" :clock-value 6 :from "3" :member "3"})))
@ -316,9 +342,9 @@
(testing "participants non-admin can't remove other admins"
(is (not (group-chats/valid-event? multi-admin-group
{:type "admin-removed" :clock-value 6 :from "3" :member "1"}))))
(testing "the last admin can't be removed"
(is (not (group-chats/valid-event? single-admin-group
{:type "admin-removed" :clock-value 6 :from "1" :member "1"}))))
(testing "the last admin can be removed"
(is (group-chats/valid-event? single-admin-group
{:type "admin-removed" :clock-value 6 :from "1" :member "1"})))
(testing "name-changed"
(testing "a change from an admin"
(is (group-chats/valid-event? multi-admin-group
@ -381,7 +407,7 @@
(deftest remove-group-chat-test
(with-redefs [utils.clocks/send inc]
(let [cofx {:db {:chats {chat-id {:admins #{admin}
(let [cofx {:db {:chats {chat-id {:admins #{member-1 member-2}
:name "chat-name"
:chat-id chat-id
:last-clock-value 3
@ -398,10 +424,13 @@
(assoc-in cofx [:db :account/account :public-key] member-3)
chat-id)))))
(testing "removing an admin"
(is (not (:group-chats/sign-membership
(group-chats/remove
(assoc-in cofx [:db :account/account :public-key] member-1)
chat-id))))))))
(is (= {:from member-1
:chat-id chat-id
:events [{:type "member-removed" :member member-1 :clock-value 4}]}
(:group-chats/sign-membership
(group-chats/remove
(assoc-in cofx [:db :account/account :public-key] member-1)
chat-id))))))))
(deftest add-members-test
(with-redefs [utils.clocks/send inc]
@ -432,3 +461,18 @@
:clock-value 2
:member "member"}]}
(:group-chats/sign-membership (group-chats/remove-member cofx chat-id "member"))))))))
(deftest make-admin-test
(with-redefs [utils.clocks/send inc]
(testing "make-admin"
(let [cofx {:db {:account/account {:public-key "me"}
:chats {chat-id {:admins #{"me"}
:last-clock-value 1
:contacts #{"member"}
:membership-updates [{:events [{:clock-value 1}]}]}}}}]
(is (= {:chat-id chat-id
:from "me"
:events [{:type "admins-added"
:clock-value 2
:members ["member"]}]}
(:group-chats/sign-membership (group-chats/make-admin cofx chat-id "member"))))))))

View File

@ -27,9 +27,11 @@
"find": "Find",
"close-app-title": "Warning!",
"group-chat-created": "*{{member}}* created the group *{{name}}*",
"group-chat-admin": "Admin",
"group-chat-name-changed": "*{{member}}* changed the group's name to *{{name}}*",
"group-chat-member-added": "*{{member}}* joined the group",
"group-chat-member-removed": "*{{member}}* left the group",
"group-chat-admin-added": "*{{member}}* has been made admin",
"group-chat-no-contacts": "You don't have any contacts yet.\nInvite your friends to start chatting",
"agree-by-continuing": "By continuing you agree\n to our ",
"wallet-advanced": "Advanced",
@ -180,6 +182,7 @@
"see-it-again": "See it again",
"delete-group-confirmation": "This group will be removed from your groups. This will not affect your contacts",
"leave-group-chat": "Leave group chat",
"make-admin": "Make admin",
"public-chats": "Public chats",
"specify-recipient": "Specify recipient...",
"not-applicable": "Not applicable for unsigned transactions",