Add system messages to group chats

Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
Andrea Maria Piana 2018-10-26 13:44:35 +02:00
parent a8481ab3e1
commit 7aa597517e
No known key found for this signature in database
GPG Key ID: AA6CCA6DE0E06424
24 changed files with 350 additions and 191 deletions

2
.env
View File

@ -8,7 +8,7 @@ POW_TARGET=0.002
POW_TIME=1
DEFAULT_NETWORK=mainnet_rpc
DEBUG_WEBVIEW=1
GROUP_CHATS_ENABLED=0
GROUP_CHATS_ENABLED=1
PAIRING_ENABLED=0
CACHED_WEBVIEWS_ENABLED=1
EXTENSIONS=1

View File

@ -7,6 +7,6 @@ POW_TARGET=0.002
POW_TIME=1
DEFAULT_NETWORK=testnet_rpc
DEBUG_WEBVIEW=1
GROUP_CHATS_ENABLED=0
GROUP_CHATS_ENABLED=1
EXTENSIONS=1
PFS_ENCRYPTION_ENABLED=0

View File

@ -8,7 +8,7 @@ POW_TARGET=0.002
POW_TIME=1
DEFAULT_NETWORK=mainnet_rpc
DEBUG_WEBVIEW=1
GROUP_CHATS_ENABLED=0
GROUP_CHATS_ENABLED=1
MAINNET_WARNING_ENABLED=1
CACHED_WEBVIEWS_ENABLED=1
EXTENSIONS=1

View File

@ -8,7 +8,7 @@ POW_TARGET=0.002
POW_TIME=1
DEFAULT_NETWORK=mainnet_rpc
DEBUG_WEBVIEW=1
GROUP_CHATS_ENABLED=0
GROUP_CHATS_ENABLED=1
PAIRING_ENABLED=0
MAINNET_WARNING_ENABLED=1
EXTENSIONS=1

View File

@ -57,8 +57,7 @@
(into (or message-references '()))
(sort-references (get-in db [:chats chat-id :messages]))))))
db
(group-by (comp time/day-relative :timestamp)
(filter :show? messages)))})
(group-by (comp time/day-relative :timestamp) messages))})
(fx/defn group-messages
[{:keys [db]}]

View File

@ -8,7 +8,7 @@
[status-im.utils.config :as config]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.datetime :as time]
[status-im.group-chats.core :as group-chats]
[status-im.transport.message.group-chat :as message.group-chat]
[status-im.chat.models :as chat-model]
[status-im.chat.models.loading :as chat-loading]
[status-im.chat.models.message-content :as message-content]
@ -26,6 +26,15 @@
[status-im.utils.fx :as fx]
[taoensso.timbre :as log]))
(defn- wrap-group-message
"Wrap a group message in a membership update"
[cofx chat-id message]
(when-let [chat (get-in cofx [:db :chats chat-id])]
(message.group-chat/map->GroupMembershipUpdate.
{:chat-id chat-id
:membership-updates (:membership-updates chat)
:message message})))
(defn- prepare-message
[{:keys [content] :as message} chat-id current-chat?]
;; TODO janherich: enable the animations again once we can do them more efficiently
@ -66,8 +75,11 @@
status)
:data-store/tx [(user-statuses-store/save-status-tx status)]}))
(defn add-outgoing-status [{:keys [from] :as message} {:keys [db]}]
(assoc message :outgoing (= from (:current-public-key db))))
(defn add-outgoing-status [{:keys [from message-type] :as message} {:keys [db]}]
(assoc message
:outgoing
(and (= from (:current-public-key db))
(not= :system-message message-type))))
(fx/defn add-message
[{:keys [db] :as cofx} batch? {:keys [chat-id message-id clock-value timestamp content from] :as message} current-chat?]
@ -197,15 +209,17 @@
groups-fx-fns (map #(update-group-messages chat->message %) chat-ids)]
(apply fx/merge cofx (concat chats-fx-fns messages-fx-fns groups-fx-fns))))
(defn system-message [chat-id message-id timestamp content]
{:message-id message-id
:chat-id chat-id
:from constants/system
:username constants/system
:timestamp timestamp
:show? true
:content content
:content-type constants/content-type-text})
(defn system-message [{:keys [now] :as cofx} {:keys [clock-value chat-id content from]}]
(let [{:keys [last-clock-value]} (get-in cofx [:db :chats chat-id])
message {:chat-id chat-id
:from from
:timestamp now
:clock-value (or clock-value
(utils.clocks/send last-clock-value))
:content content
:message-type :system-message
:content-type constants/content-type-status}]
(assoc message :message-id (transport.utils/message-id message))))
(defn group-message? [{:keys [message-type]}]
(#{:group-user-message :public-group-user-message} message-type))
@ -218,7 +232,7 @@
{:dispatch-later [{:ms 10000
:dispatch [:message/update-message-status chat-id message-id :not-sent]}]}
(let [wrapped-record (if (= (:message-type send-record) :group-user-message)
(group-chats/wrap-group-message cofx chat-id send-record)
(wrap-group-message cofx chat-id send-record)
send-record)]
(protocol/send wrapped-record chat-id cofx))))
@ -296,6 +310,10 @@
:data-store/tx [(messages-store/delete-message-tx message-id)]}
(remove-message-from-group chat-id (get-in db [:chats chat-id :messages message-id]))))
(fx/defn add-system-messages [cofx messages]
(let [messages-fx (map #(add-message false (system-message cofx %) true) messages)]
(apply fx/merge cofx messages-fx)))
(fx/defn send-message
[{:keys [db now] :as cofx} {:keys [chat-id] :as message}]
(let [{:keys [current-public-key chats]} db
@ -304,8 +322,7 @@
(assoc :from current-public-key
:timestamp now
:clock-value (utils.clocks/send
last-clock-value)
:show? true)
last-clock-value))
(add-message-type chat))]
(upsert-and-send cofx message-data)))

View File

@ -181,14 +181,16 @@
message-groups))
(defn- set-previous-message-info [stream]
(let [{:keys [display-photo?] :as previous-message} (peek stream)]
(let [{:keys [display-photo? message-type] :as previous-message} (peek stream)]
(conj (pop stream) (assoc previous-message
:display-username? display-photo?
:display-username? (and display-photo?
(not= :system-message message-type))
:first-in-group? true))))
(defn display-photo? [{:keys [outgoing message-type]}]
(and (not outgoing)
(not= message-type :user-message)))
(or (= :system-message message-type)
(and (not outgoing)
(not (= :user-message message-type)))))
; any message that comes after this amount of ms will be grouped separately
(def ^:private group-ms 60000)
@ -201,7 +203,8 @@
(let [previous-message (peek stream)
; Was the previous message from a different author or this message
; comes after x ms
last-in-group? (or (not= from (:from previous-message))
last-in-group? (or (= :system-message message-type)
(not= from (:from previous-message))
(> (- (:timestamp previous-message) timestamp) group-ms))
same-direction? (= outgoing (:outgoing previous-message))
; Have we seen an outgoing message already?

View File

@ -13,7 +13,7 @@
(def content-type-emoji "emoji")
(def desktop-content-types
#{content-type-text content-type-emoji})
#{content-type-text content-type-emoji content-type-status})
(def min-password-length 6)
(def max-chat-name-length 20)

View File

@ -30,10 +30,14 @@
;; it's not in the contact list at all
(nil? pending?))))
(defn- build-contact [whisper-id {{:keys [chats] :contacts/keys [contacts]} :db}]
(assoc (or (get contacts whisper-id)
(utils.contacts/whisper-id->new-contact whisper-id))
:address (utils.contacts/public-key->address whisper-id)))
(defn build-contact [{{:keys [chats current-public-key]
:account/keys [account]
:contacts/keys [contacts]} :db} whisper-id]
(cond-> (assoc (or (get contacts whisper-id)
(utils.contacts/whisper-id->new-contact whisper-id))
:address (utils.contacts/public-key->address whisper-id))
(= whisper-id current-public-key) (assoc :name (:name account))))
(defn- own-info [db]
(let [{:keys [name photo-path address]} (:account/account db)
@ -62,10 +66,11 @@
(protocol/send (message.contact/map->ContactRequest (own-info db)) whisper-identity cofx))))
(fx/defn add-contact [{:keys [db] :as cofx} whisper-id]
(let [contact (build-contact whisper-id cofx)]
(fx/merge cofx
(add-new-contact contact)
(send-contact-request contact))))
(when (not= (get-in db [:account/account :public-key]) whisper-id)
(let [contact (build-contact cofx whisper-id)]
(fx/merge cofx
(add-new-contact contact)
(send-contact-request contact)))))
(fx/defn add-tag
"add a tag to the contact"

View File

@ -105,7 +105,9 @@
(reg-sub :get-all-contacts-not-in-current-chat
:<- [:query-current-chat-contacts remove]
(fn [contacts]
(remove :dapp? contacts)))
(->> contacts
(remove :dapp?)
(sort-by (comp clojure.string/lower-case :name)))))
(defn get-all-contacts-in-group-chat [members contacts current-account]
(let [current-account-contact (-> current-account

View File

@ -136,11 +136,3 @@
(fn [realm]
(core/delete realm (core/get-by-field realm :message :chat-id chat-id))
(core/delete realm (core/get-by-field realm :user-status :chat-id chat-id))))
(defn hide-messages-tx
"Returns tx function for hiding messages for given chat-id"
[chat-id]
(fn [realm]
(.map (core/get-by-field realm :message :chat-id chat-id)
(fn [msg _ _]
(aset msg "show?" false)))))

View File

@ -7,6 +7,8 @@
[status-im.i18n :as i18n]
[status-im.utils.config :as config]
[status-im.utils.clocks :as utils.clocks]
[status-im.chat.models.message :as models.message]
[status-im.contact.core :as models.contact]
[status-im.native-module.core :as native-module]
[status-im.transport.utils :as transport.utils]
[status-im.transport.db :as transport.db]
@ -39,11 +41,7 @@
(defn get-last-clock-value
"Given a chat id get the last clock value of an event"
[cofx chat-id]
(->> (get-in cofx [:db :chats chat-id :membership-updates])
(mapcat :events)
(map :clock-value)
sort
last))
(->> (get-in cofx [:db :chats chat-id :last-clock-value])))
(defn- parse-response [response-js]
(-> response-js
@ -100,15 +98,6 @@
(not= #{from} admins))
false)))
(defn wrap-group-message
"Wrap a group message in a membership update"
[cofx chat-id message]
(when-let [chat (get-in cofx [:db :chats chat-id])]
(message.group-chat/map->GroupMembershipUpdate.
{:chat-id chat-id
:membership-updates (:membership-updates chat)
:message message})))
(defn send-membership-update
"Send a membership update to all participants but the sender"
([cofx payload chat-id]
@ -236,6 +225,15 @@
(assoc-in [:group-chat-profile/profile :valid-name?] (valid-name? name))
(assoc-in [:group-chat-profile/profile :name] name))})
(defn extract-creator
"Takes a chat as an input, returns the creator"
[{:keys [membership-updates]}]
(->> membership-updates
(filter (fn [{:keys [events]}]
(some #(= "chat-created" (:type %)) events)))
first
:from))
(fx/defn handle-name-changed
"Store name in profile scratchpad"
[cofx new-chat-name]
@ -244,27 +242,47 @@
(fx/defn save
"Save chat from edited profile"
[{:keys [db] :as cofx}]
(let [current-chat-id (get-in cofx [:db :current-chat-id])
new-name (get-in cofx [:db :group-chat-profile/profile :name])]
(let [current-chat-id (get-in cofx [:db :current-chat-id])
my-public-key (get-in db [:account/account :public-key])
last-clock-value (get-last-clock-value cofx current-chat-id)
new-name (get-in cofx [:db :group-chat-profile/profile :name])
name-changed-event {:type "name-changed"
:name new-name
:clock-value (utils.clocks/send last-clock-value)}]
(when (valid-name? new-name)
(fx/merge cofx
{:db (assoc db :group-chat-profile/editing? false)}
(models.chat/upsert-chat {:chat-id current-chat-id
:name new-name})))))
{:db (assoc db
:group-chat-profile/editing?
false)
:group-chats/sign-membership {:chat-id current-chat-id
:from my-public-key
:events [name-changed-event]}}))))
(defn process-event
"Add/remove an event to a group"
[group {:keys [type member members chat-id from name] :as event}]
"Add/remove an event to a group, carrying the clock-value at which it happened"
[group {:keys [type member members chat-id clock-value from name] :as event}]
(if (valid-event? group event)
(case type
"chat-created" {:name name
:admins #{from}
:contacts #{from}}
"name-changed" (assoc group :name name)
"members-added" (update group :contacts clojure.set/union (into #{} members))
"admins-added" (update group :admins clojure.set/union (into #{} members))
"member-removed" (update group :contacts disj member)
"admin-removed" (update group :admins disj member))
"chat-created" {:name name
:created-at clock-value
:admins #{from}
:contacts #{from}}
"name-changed" (assoc group
:name name
:name-changed-by from
:name-changed-at clock-value)
"members-added" (as-> group $
(update $ :contacts clojure.set/union (set members))
(reduce (fn [acc member] (assoc-in acc [member :added] clock-value)) $ members))
"admins-added" (as-> group $
(update $ :admins clojure.set/union (set members))
(reduce (fn [acc member] (assoc-in acc [member :admin-added] clock-value)) $ members))
"member-removed" (-> group
(update :contacts disj member)
(assoc-in [member :removed] clock-value))
"admin-removed" (-> group
(update :admins disj member)
(assoc-in [member :admin-removed] clock-value)))
group))
(defn build-group
@ -277,24 +295,78 @@
{:admins #{}
:contacts #{}})))
(fx/defn update-membership
"Upsert chat when version is greater or not existing"
[cofx previous-chat {:keys [chat-id] :as new-chat}]
(let [all-updates (clojure.set/union (into #{} (:membership-updates previous-chat))
(into #{} (:membership-updates new-chat)))
unwrapped-events (mapcat
(fn [{:keys [events from]}]
(map #(assoc % :from from) events))
all-updates)
new-group (build-group unwrapped-events)]
(models.chat/upsert-chat cofx
{:chat-id chat-id
:name (:name new-group)
:is-active (get previous-chat :is-active true)
:group-chat true
:membership-updates (into [] all-updates)
:admins (:admins new-group)
:contacts (:contacts new-group)})))
(defn membership-changes->system-messages [cofx
clock-values
{:keys [chat-id
chat-name
creator
members-added
name-changed?
members-removed]}]
(let [get-contact (partial models.contact/build-contact cofx)
format-message (fn [contact text clock-value]
{:chat-id chat-id
:content {:text text}
:clock-value clock-value
:from (:whisper-identity contact)})
creator-contact (when creator (get-contact creator))
name-changed-author (when name-changed? (get-contact (:name-changed-by clock-values)))
contacts-added (map
get-contact
(disj members-added creator))
contacts-removed (map
get-contact
members-removed)]
(cond-> []
creator-contact (conj (format-message creator-contact
(i18n/label :t/group-chat-created
{:name chat-name
:member (:name creator-contact)})
(:created-at clock-values)))
name-changed? (conj (format-message name-changed-author
(i18n/label :t/group-chat-name-changed
{:name chat-name
:member (:name name-changed-author)})
(:name-changed-at clock-values)))
(seq members-added) (concat (map #(format-message
%
(i18n/label :t/group-chat-member-added {:member (:name %)})
(get-in clock-values [(:whisper-identity %) :added]))
contacts-added))
(seq members-removed) (concat (map #(format-message
%
(i18n/label :t/group-chat-member-removed {:member (:name %)})
(get-in clock-values [(:whisper-identity %) :removed]))
contacts-removed)))))
(fx/defn add-system-messages [cofx chat-id previous-chat clock-values]
(let [current-chat (get-in cofx [:db :chats chat-id])
current-public-key (get-in cofx [:db :current-public-key])
name-changed? (and (seq previous-chat)
(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))
membership-changes (cond-> {:chat-id chat-id
:name-changed? name-changed?
:chat-name (:name current-chat)
:members-added members-added
:members-removed members-removed}
(nil? previous-chat)
(assoc :creator (extract-creator current-chat)))]
(when (or name-changed?
(seq members-added)
(seq members-removed))
(->> membership-changes
(membership-changes->system-messages cofx clock-values)
(models.message/add-system-messages cofx)))))
(defn- unwrap-events
"Flatten all events, denormalizing from field"
[all-updates]
(mapcat
(fn [{:keys [events from]}]
(map #(assoc % :from from) events))
all-updates))
(fx/defn handle-membership-update
"Upsert chat and receive message if valid"
@ -306,10 +378,21 @@
sender-signature]
(let [dev-mode? (get-in cofx [:db :account/account :dev-mode?])]
(when (and (config/group-chats-enabled? dev-mode?)
(valid-chat-id? chat-id (-> membership-updates first :from)))
(let [previous-chat (get-in cofx [:db :chats chat-id])]
(valid-chat-id? chat-id (extract-creator membership-update)))
(let [previous-chat (get-in cofx [:db :chats chat-id])
all-updates (clojure.set/union (set (:membership-updates previous-chat))
(set (:membership-updates membership-update)))
unwrapped-events (unwrap-events all-updates)
new-group (build-group unwrapped-events)]
(fx/merge cofx
(update-membership previous-chat membership-update)
(models.chat/upsert-chat {:chat-id chat-id
:name (:name new-group)
:is-active (get previous-chat :is-active true)
:group-chat true
:membership-updates (into [] all-updates)
:admins (:admins new-group)
:contacts (:contacts new-group)})
(add-system-messages chat-id previous-chat new-group)
#(when (and message
;; don't allow anything but group messages
(instance? protocol/Message message)

View File

@ -108,7 +108,6 @@
{:chat-received-message/add-fx
[(assoc (into {} this)
:message-id (transport.utils/message-id this)
:show? true
:chat-id chat-id
:from signature
:js-obj (:js-obj cofx))]})

View File

@ -22,26 +22,6 @@
[status-im.ui.components.colors :as colors]
[clojure.string :as string]))
(defview message-content-status []
(letsubs [{:keys [chat-id group-id name color public-key]} [:get-current-chat]
members [:get-current-chat-contacts]]
(let [{:keys [status]} (if group-id
{:status nil}
(first members))]
[react/view style/status-container
[chat-icon.screen/chat-icon-message-status chat-id group-id name color false]
[react/text {:style style/status-from
:font :default
:number-of-lines 1}
(if (string/blank? name)
(gfycat/generate-gfy public-key)
(or (i18n/get-contact-translated chat-id :name name)
(i18n/label :t/chat-name)))]
(when status
[react/text {:style style/status-text
:font :default}
status])])))
(defview message-content-command
[command-message]
(letsubs [id->command [:get-id->command]]
@ -178,6 +158,12 @@
:number-of-lines 5}
text]]))
(defview message-content-status [{:keys [content]}]
[react/view style/status-container
[react/text {:style style/status-text
:font :default}
(cached-parse-text (:text content) nil false)]])
(defn text-message
[{:keys [content timestamp-str group-chat outgoing current-public-key] :as message}]
[message-view message
@ -216,8 +202,8 @@
[wrapper message [text-message message]])
(defmethod message-content constants/content-type-status
[_ _]
[message-content-status])
[wrapper message]
[wrapper message [message-content-status message]])
(defmethod message-content constants/content-type-command
[wrapper message]
@ -312,17 +298,18 @@
(let [outgoing-status (or (get-in user-statuses [current-public-key :status]) :not-sent)
delivery-status (get-in user-statuses [chat-id :status])
status (or delivery-status outgoing-status)]
(case status
:sending [message-activity-indicator]
:not-sent [message-not-sent-text chat-id message-id]
(if (and (not outgoing)
(:command content))
[command-status content]
(when last-outgoing?
(if (= message-type :group-user-message)
[group-message-delivery-status message]
(if outgoing
[text-status status])))))))
(when (not= :system-message message-type)
(case status
:sending [message-activity-indicator]
:not-sent [message-not-sent-text chat-id message-id]
(if (and (not outgoing)
(:command content))
[command-status content]
(when last-outgoing?
(if (= message-type :group-user-message)
[group-message-delivery-status message]
(if outgoing
[text-status status]))))))))
(defview message-author-name [from message-username]
(letsubs [username [:get-contact-name-by-identity from]]
@ -333,6 +320,7 @@
[{:keys [last-in-group?
display-photo?
display-username?
message-type
from
outgoing
modal?
@ -345,7 +333,7 @@
[react/touchable-highlight {:on-press #(when-not modal? (re-frame/dispatch [:chat.ui/show-profile from]))}
[react/view
[photos/member-photo from]]])])
[react/view (style/group-message-view outgoing)
[react/view (style/group-message-view outgoing message-type)
(when display-username?
[message-author-name from username])
[react/view {:style (style/timestamp-content-wrapper message)}

View File

@ -74,10 +74,12 @@
{:flex-direction (if outgoing :row-reverse :row)})
(defn group-message-view
[outgoing]
[outgoing message-type]
(let [align (if outgoing :flex-end :flex-start)]
{:flex-direction :column
:width 230
:width (if (= :system-message message-type)
300
230)
:padding-left 8
:padding-right 8
:align-items align}))
@ -152,26 +154,12 @@
:height 33})
(def status-container
{:flex 1
:align-self :center
:align-items :center
:width 249
:padding-bottom 16})
(def status-image-view
{:margin-top 20})
(def status-from
{:margin-top 20
:font-size 18
:color colors/text})
{:padding-horizontal 5})
(def status-text
{:margin-top 10
{:margin-top 9
:font-size 14
:line-height 20
:text-align :center
:color colors/text-gray})
:color colors/gray})
(defn message-container [window-width]
{:position :absolute

View File

@ -124,6 +124,11 @@
:color (if outgoing colors/white colors/blue)
:text-decoration-line :underline))
(def system-message-text
{:color colors/black
:margin-top -5
:font-size 14})
(def message-container
{:flex-direction :column
:margin-right 16})

View File

@ -50,12 +50,6 @@
public?
[react/text {:style styles/public-chat-text}
(i18n/label :t/public-chat)])]]
#_[react/view
[react/popup-menu
[react/popup-menu-trigger {:text "Popup test"}]
[react/popup-menu-options
[react/popup-menu-option {:text "First"}]
[react/popup-menu-option {:text "Second"}]]]]
[react/view
(when (and (not group-chat) (not public?))
[react/text {:style (styles/profile-actions-text colors/black)
@ -69,7 +63,10 @@
:on-press #(re-frame/dispatch [:chat.ui/clear-history-pressed])}
(i18n/label :t/clear-history)]
[react/text {:style (styles/profile-actions-text colors/black)
:on-press #(re-frame/dispatch [:chat.ui/remove-chat-pressed chat-id])}
:on-press #(re-frame/dispatch [(if (and group-chat (not public?))
:group-chats.ui/remove-chat-pressed
:chat.ui/remove-chat-pressed)
chat-id])}
(i18n/label :t/delete-chat)]]]))
(views/defview message-author-name [{:keys [from]}]
@ -155,6 +152,18 @@
[react/view {:style {:width 40
:margin-horizontal 16}}])
(views/defview system-message [text {:keys [content from first-in-group? timestamp] :as message}]
[react/view
[react/view {:style {:flex-direction :row :margin-top 24}}
[member-photo from]
[react/view {:style {:flex 1}}]
[react/text {:style styles/message-timestamp}
(time/timestamp->time timestamp)]]
[react/view {:style styles/not-first-in-group-wrapper}
[photo-placeholder]
[react/text {:style styles/system-message-text}
text]]])
(views/defview message-with-name-and-avatar [text {:keys [from first-in-group? timestamp] :as message}]
[react/view
(when first-in-group?
@ -181,6 +190,14 @@
[react/view {:style styles/message-command-container}
[message/message-content-command message]]]])
(views/defview message-content-status [text message]
[react/view
[system-message text message]])
(defmethod message constants/content-type-status
[text _ message]
[message-content-status text message])
(defmethod message :default
[text me? {:keys [message-id chat-id message-status user-statuses from
current-public-key content-type outgoing type value] :as message}]

View File

@ -40,7 +40,10 @@
(views/defview home-list-item [[home-item-id home-item]]
(views/letsubs [swiped? [:delete-swipe-position home-item-id]]
(let [delete-action (if (:chat-id home-item)
:chat.ui/remove-chat
(if (and (:group-chat home-item)
(not (:public? home-item)))
:group-chats.ui/remove-chat-pressed
:chat.ui/remove-chat)
:browser.ui/remove-browser-pressed)
inner-item-view (if (:chat-id home-item)
inner-item/home-list-chat-item-inner-view

View File

@ -15,17 +15,18 @@
[status-im.i18n :as i18n]
[status-im.utils.utils :as utils]))
(defn group-chat-profile-toolbar []
(defn group-chat-profile-toolbar [admin?]
[toolbar/toolbar {}
toolbar/default-nav-back
[toolbar/content-title ""]
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:group-chat-profile/start-editing])}
[react/view
[react/text {:style common.styles/label-action-text
:uppercase? true
:accessibility-label :edit-button}
(i18n/label :t/edit)]]]])
(when admin?
[react/touchable-highlight
{:on-press #(re-frame/dispatch [:group-chat-profile/start-editing])}
[react/view
[react/text {:style common.styles/label-action-text
:uppercase? true
:accessibility-label :edit-button}
(i18n/label :t/edit)]]])])
(defn group-chat-profile-edit-toolbar []
[toolbar/toolbar {}
@ -95,7 +96,7 @@
[status-bar/status-bar]
(if editing?
[group-chat-profile-edit-toolbar]
[group-chat-profile-toolbar])
[group-chat-profile-toolbar admin?])
[react/scroll-view
[react/view profile.components.styles/profile-form
[profile.components/profile-header

View File

@ -22,20 +22,17 @@
new-messages [{:message-id 1
:content "b"
:clock-value 1
:timestamp 1
:show? false}
:timestamp 1}
{:message-id 2
:content "c"
:clock-value 2
:timestamp 2
:show? true}
:timestamp 2}
{:message-id 3
:content "d"
:clock-value 3
:timestamp 3
:show? true}]]
(testing "New messages are grouped/sorted correctly, hidden messages are not grouped"
(is (= '(2 3)
:timestamp 3}]]
(testing "New messages are grouped/sorted correctly"
(is (= '(1 2 3)
(map :message-id
(-> (get-in (loading/group-chat-messages cofx "chat-id" new-messages)
[:db :chats "chat-id" :message-groups])

View File

@ -51,6 +51,7 @@
(let [actual (message/receive-many {:db db}
[{:from "me"
:message-type :user-message
:timestamp 0
:message-id "id"
:chat-id "chat-id"
:content "b"

View File

@ -107,7 +107,18 @@
(is (:last-in-group? actual-m1))
(is (:last-in-group? actual-m2))
(is (:last-in-group? actual-m3))
(is (not (:last-in-group? actual-m4)))))))
(is (not (:last-in-group? actual-m4))))))
(testing "system-messages"
(let [m1 {:from "system"
:message-type :system-message
:datemark "a"
:outgoing false}
messages [m1]
[actual-m1] (s/messages-stream messages)]
(testing "it does display the photo"
(is (:display-photo? actual-m1))
(testing "it does not display the username"
(is (not (:display-username? actual-m1))))))))
(deftest active-chats-test
(let [active-chat-1 {:is-active true :chat-id 1}

View File

@ -26,13 +26,13 @@
:members [member-2 member-3]}]}]})
(deftest get-last-clock-value-test
(is (= 3 (group-chats/get-last-clock-value {:db {:chats {chat-id initial-message}}} chat-id))))
(is (= 3 (group-chats/get-last-clock-value {:db {:chats {chat-id {:last-clock-value 3}}}} chat-id))))
(deftest handle-group-membership-update
(deftest handle-group-membership-update-test
(with-redefs [config/group-chats-enabled? (constantly true)]
(testing "a brand new chat"
(let [actual (->
(group-chats/handle-membership-update {:db {}} initial-message admin)
(group-chats/handle-membership-update {:now 0 :db {}} initial-message admin)
:db
:chats
(get chat-id))]
@ -52,12 +52,19 @@
(:membership-updates actual))))
(testing "it sets the right admins"
(is (= #{admin}
(:admins actual))))))
(:admins actual))))
(testing "it adds a system message"
(is (= 3 (count (:messages actual)))))
(testing "it adds the right text"
(is (= ["group-chat-created"
"group-chat-member-added"
"group-chat-member-added"]
(map (comp :text :content) (sort-by :clock-value (vals (:messages actual)))))))))
(testing "a chat with the wrong id"
(let [bad-chat-id (str random-id member-2)
actual (->
(group-chats/handle-membership-update
{:db {}}
{:now 0 :db {}}
(assoc initial-message :chat-id bad-chat-id)
admin)
:db
@ -66,18 +73,15 @@
(testing "it does not create a chat"
(is (not actual)))))
(testing "an already existing chat"
(let [cofx {:db {:chats {chat-id {:admins #{admin}
:name "chat-name"
:chat-id chat-id
:is-active true
:group-chat true
:contacts #{member-1 member-2 member-3}
:membership-updates (:membership-updates initial-message)}}}}]
(let [cofx (assoc
(group-chats/handle-membership-update {:now 0 :db {}} initial-message admin)
:now 0)]
(testing "the message has already been received"
(let [actual (group-chats/handle-membership-update cofx initial-message admin)]
(testing "it noops"
(is (= (get-in actual [:db :chats chat-id])
(get-in cofx [:db :chats chat-id]))))))
(is (=
(get-in cofx [:db :chats chat-id])
(get-in actual [:db :chats chat-id]))))))
(testing "a new message comes in"
(let [actual (group-chats/handle-membership-update cofx
{:chat-id chat-id
@ -97,7 +101,10 @@
:member member-3}
{:type "members-added"
:clock-value 12
:members [member-4]}]}]}
:members [member-4]}
{:type "name-changed"
:clock-value 13
:name "new-name"}]}]}
member-3)
actual-chat (get-in actual [:db :chats chat-id])]
(testing "the chat is updated"
@ -105,7 +112,19 @@
(testing "admins are updated"
(is (= #{member-2} (:admins actual-chat))))
(testing "members are updated"
(is (= #{member-1 member-2 member-4} (:contacts actual-chat))))))))))
(is (= #{member-1 member-2 member-4} (:contacts actual-chat))))
(testing "the name is updated"
(is (= "new-name" (:name actual-chat))))
(testing "it adds a system message"
(is (= 6 (count (:messages actual-chat)))))
(testing "it sets the right text"
(is (= ["group-chat-created"
"group-chat-member-added"
"group-chat-member-added"
"group-chat-member-added"
"group-chat-member-removed"
"group-chat-name-changed"]
(map (comp :text :content) (sort-by :clock-value (vals (:messages actual-chat)))))))))))))
(deftest build-group-test
(testing "only adds"
@ -126,6 +145,10 @@
:from "2"
:members ["3"]}]
expected {:name "chat-name"
:created-at 0
"2" {:added 1
:admin-added 2}
"3" {:added 3}
:admins #{"1" "2"}
:contacts #{"1" "2" "3"}}]
(is (= expected (group-chats/build-group events)))))
@ -151,6 +174,11 @@
:from "2"
:member "2"}]
expected {:name "chat-name"
:created-at 0
"2" {:added 1
:admin-added 2
:admin-removed 3
:removed 4}
:admins #{"1"}
:contacts #{"1"}}]
(is (= expected (group-chats/build-group events)))))
@ -172,6 +200,11 @@
:from "2"
:name "new-name"}]
expected {:name "new-name"
:created-at 0
:name-changed-by "2"
:name-changed-at 3
"2" {:added 1
:admin-added 2}
:admins #{"1" "2"}
:contacts #{"1" "2"}}]
(is (= expected (group-chats/build-group events)))))
@ -205,6 +238,10 @@
:from "1"
:member "2"}]
expected {:name "chat-name"
:created-at 0
"2" {:added 2
:admin-added 3}
"3" {:added 4}
:admins #{"1" "2"}
:contacts #{"1" "2" "3"}}]
(is (= expected (group-chats/build-group events)))))
@ -226,6 +263,10 @@
:from "2"
:members ["3"]}]
expected {:name "chat-name"
:created-at 0
"2" {:added 1
:admin-added 2}
"3" {:added 3}
:admins #{"1" "2"}
:contacts #{"1" "2" "3"}}]
(is (= expected (group-chats/build-group events))))))
@ -343,6 +384,7 @@
(let [cofx {:db {:chats {chat-id {:admins #{admin}
:name "chat-name"
:chat-id chat-id
:last-clock-value 3
:is-active true
:group-chat true
:contacts #{member-1 member-2 member-3}
@ -367,7 +409,8 @@
(let [cofx {:db {:current-chat-id chat-id
:selected-participants ["new-member"]
:current-public-key "me"
:chats {chat-id {:membership-updates [{:events [{:clock-value 1}]}]}}}}]
:chats {chat-id {:last-clock-value 1
:membership-updates [{:events [{:clock-value 1}]}]}}}}]
(is (= {:chat-id chat-id
:from "me"
:events [{:type "members-added"
@ -380,6 +423,7 @@
(testing "remove-member"
(let [cofx {:db {:current-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

View File

@ -19,6 +19,10 @@
"name-placeholder": "Display name",
"find": "Find",
"close-app-title": "Warning!",
"group-chat-created": "*{{member}}* created the group *{{name}}*",
"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",
"agree-by-continuing": "By continuing you agree\n to our ",
"wallet-advanced": "Advanced",
"currency-display-name-sos": "Somalia Shilling",