diff --git a/src/quo/components/text_combinations/username/view.cljs b/src/quo/components/text_combinations/username/view.cljs index 33529d9c997..81dea268fb6 100644 --- a/src/quo/components/text_combinations/username/view.cljs +++ b/src/quo/components/text_combinations/username/view.cljs @@ -40,17 +40,17 @@ :color (colors/resolve-color color theme)}])) (defn status-icon - [{:keys [theme name-type status] + [{:keys [name-type status] :or {name-type :default}}] [rn/view {:style (style/status-icon-container name-type status)} (case status - :verified [icon-20 :i/verified theme :success] - :contact [icon-20 :i/contact theme :blue] - :untrustworthy [icon-20 :i/untrustworthy theme :danger] - :blocked [icon-20 :i/block theme :danger] + :verified [icon-20 :i/verified :success] + :contact [icon-20 :i/contact :blue] + :untrustworthy [icon-20 :i/untrustworthy :danger] + :blocked [icon-20 :i/block :danger] :untrustworthy-contact [:<> - [icon-20 :i/untrustworthy theme :danger] - [icon-20 :i/contact theme :blue]] + [icon-20 :i/untrustworthy :danger] + [icon-20 :i/contact :blue]] nil)]) (defn view diff --git a/src/status_im/common/confirmation_drawer/style.cljs b/src/status_im/common/confirmation_drawer/style.cljs index 581235183e3..a4086a5b77e 100644 --- a/src/status_im/common/confirmation_drawer/style.cljs +++ b/src/status_im/common/confirmation_drawer/style.cljs @@ -1,19 +1,10 @@ -(ns status-im.common.confirmation-drawer.style - (:require - [quo.foundations.colors :as colors])) - -(defn context-container - [theme] - {:flex-direction :row - :background-color (colors/theme-colors colors/neutral-10 colors/neutral-80 theme) - :border-radius 20 - :align-items :center - :align-self :flex-start - :padding 2 - :margin-top 4 - :margin-bottom 16}) +(ns status-im.common.confirmation-drawer.style) (def buttons-container {:flex-direction :row :justify-content :space-between :margin-top 25}) + +(def context-tag + {:margin-bottom 12 + :margin-top 4}) diff --git a/src/status_im/common/confirmation_drawer/view.cljs b/src/status_im/common/confirmation_drawer/view.cljs index 808a4cf2811..20a3e4ab813 100644 --- a/src/status_im/common/confirmation_drawer/view.cljs +++ b/src/status_im/common/confirmation_drawer/view.cljs @@ -9,18 +9,6 @@ [utils.i18n :as i18n] [utils.re-frame :as rf])) -(defn avatar - [group-chat color display-name photo-path] - (if group-chat - [quo/group-avatar - {:customization-color color - :size :size-20}] - [quo/user-avatar - {:full-name display-name - :profile-picture photo-path - :size :xxs - :status-indicator? false}])) - (defn extra-action-view [extra-action extra-text extra-action-selected?] (when extra-action @@ -38,7 +26,6 @@ (let [{:keys [group-chat chat-id public-key color chat-type profile-picture name]} context id (or chat-id public-key) - theme (quo.context/use-theme) [primary-name _] (when-not (or group-chat (= chat-type constants/public-chat-type)) (rf/sub [:contacts/contact-two-names-by-identity id])) @@ -56,12 +43,14 @@ [quo/text {:weight :semi-bold :size :heading-2} title] - [rn/view {:style (style/context-container theme)} - [avatar group-chat color display-name photo-path] - [quo/text - {:weight :medium - :size :paragraph-2 - :style {:margin-left 4}} display-name]] + [quo/context-tag + {:type (if group-chat :group :default) + :profile-picture photo-path + :full-name display-name + :group-name display-name + :customization-color color + :container-style style/context-tag + :size 24}] [quo/text description] [extra-action-view extra-action extra-text extra-action-selected?] [rn/view {:style style/buttons-container} diff --git a/src/status_im/common/contact_list_item/view.cljs b/src/status_im/common/contact_list_item/view.cljs index 90b88c5589c..d9dfe1acfef 100644 --- a/src/status_im/common/contact_list_item/view.cljs +++ b/src/status_im/common/contact_list_item/view.cljs @@ -1,16 +1,18 @@ (ns status-im.common.contact-list-item.view (:require [quo.core :as quo] + [status-im.constants :as constants] [utils.address :as address] [utils.re-frame :as rf])) (defn contact-list-item [{:keys [on-press on-long-press accessory allow-multiple-presses? disabled?]} {:keys [primary-name secondary-name public-key compressed-key ens-verified added? - container-style]} + container-style trust-status]} theme] (let [photo-path (rf/sub [:chats/photo-path public-key]) online? (rf/sub [:visibility-status-updates/online? public-key]) + untrustworthy? (= constants/contact-trust-status-untrustworthy trust-status) customization-color (rf/sub [:profile/customization-color])] [quo/user {:customization-color customization-color @@ -23,6 +25,7 @@ :online? online? :verified? ens-verified :contact? added? + :untrustworthy? untrustworthy? :on-press on-press :on-long-press on-long-press :accessory accessory diff --git a/src/status_im/common/home/actions/view.cljs b/src/status_im/common/home/actions/view.cljs index 397cb20623b..78de71a6583 100644 --- a/src/status_im/common/home/actions/view.cljs +++ b/src/status_im/common/home/actions/view.cljs @@ -10,6 +10,7 @@ [status-im.contexts.chat.actions.view :as chat-actions] [status-im.contexts.chat.contacts.drawers.nickname-drawer.view :as nickname-drawer] [status-im.contexts.communities.actions.chat.view :as communities-chat-actions] + [status-im.contexts.profile.utils :as profile.utils] [utils.i18n :as i18n] [utils.re-frame :as rf])) @@ -130,6 +131,14 @@ :on-press #(hide-sheet-and-dispatch [:contact/block-contact public-key])}])}])) +(defn handle-trust-mark-action + [{:keys [public-key trust-status] :as item}] + (hide-sheet-and-dispatch + (if (= trust-status + constants/contact-trust-status-untrustworthy) + [:contact/remove-trust-status public-key (profile.utils/displayed-name item)] + [:contact/mark-as-untrusted-sheet item]))) + (defn mute-chat-entry [chat-id chat-type muted-till] (let [muted? (rf/sub [:chats/muted chat-id])] @@ -260,7 +269,7 @@ (entry {:icon :i/remove-user :label (i18n/label :t/remove-from-contacts) :on-press #(hide-sheet-and-dispatch [:contact.ui/remove-contact-pressed contact]) - :danger? false + :danger? true :accessibility-label :remove-from-contacts :sub-label nil :chevron? false})) @@ -300,17 +309,19 @@ :sub-label nil :chevron? false})) -;; TODO(OmarBasem): Requires status-go impl. -(defn mark-untrustworthy-entry - [] - (entry {:icon :i/alert - :label (i18n/label :t/mark-untrustworthy) - :on-press #(js/alert "TODO: to be implemented, requires status-go impl.") +(defn change-trust-status-entry + [{:keys [trust-status] :as item}] + (entry {:icon :i/untrustworthy + :label (i18n/label (if (= trust-status + constants/contact-trust-status-untrustworthy) + :t/remove-untrusted-mark + :t/mark-as-untrusted)) + :on-press #(handle-trust-mark-action item) :danger? true - :accessibility-label :mark-untrustworthy + :add-divider? true + :accessibility-label :mark-as-untrusted :sub-label nil - :chevron? false - :add-divider? true})) + :chevron? false})) (defn block-user-entry [item] @@ -445,8 +456,7 @@ (show-qr-entry public-key) (share-profile-entry public-key)] [(when-not (= current-pub-key public-key) - (when config/show-not-implemented-features? - (mark-untrustworthy-entry))) + (change-trust-status-entry contact)) (when added? (remove-from-contacts-entry contact)) (when-not (= current-pub-key public-key) (block-user-entry contact))] (when (and admin? chat-id) diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 90ca9497148..d9b276069b8 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -495,3 +495,8 @@ ;; Community help links (def ^:const create-community-help-url "https://status.app/help/communities/create-a-status-community#create-a-status-community") + +;; Contact trust status +(def ^:const contact-trust-status-unknown 0) +(def ^:const contact-trust-status-trusted 1) +(def ^:const contact-trust-status-untrustworthy 2) diff --git a/src/status_im/contexts/chat/contacts/events.cljs b/src/status_im/contexts/chat/contacts/events.cljs index 046166035d0..80f5d9f7c32 100644 --- a/src/status_im/contexts/chat/contacts/events.cljs +++ b/src/status_im/contexts/chat/contacts/events.cljs @@ -22,6 +22,7 @@ :last-updated (oops/oget js-contact "lastUpdated") :active? (oops/oget js-contact "active") :blocked? (oops/oget js-contact "blocked") + :trust-status (oops/oget js-contact "trustStatus") :added? (oops/oget js-contact "added") :has-added-us? (oops/oget js-contact "hasAddedUs") :mutual? (oops/oget js-contact "mutual") diff --git a/src/status_im/contexts/chat/home/chat_list_item/view.cljs b/src/status_im/contexts/chat/home/chat_list_item/view.cljs index 567b7bb2394..9b215356b58 100644 --- a/src/status_im/contexts/chat/home/chat_list_item/view.cljs +++ b/src/status_im/contexts/chat/home/chat_list_item/view.cljs @@ -249,7 +249,9 @@ (rf/sub [:contacts/contact-two-names-by-identity chat-id])) {:keys [ens-verified added?] :as contact} (when-not group-chat (rf/sub [:contacts/contact-by-address chat-id])) - unread-messages? (pos? unviewed-messages-count)] + unread-messages? (pos? unviewed-messages-count) + trust-status (rf/sub [:contacts/contact-trust-status chat-id]) + untrustworthy? (= constants/contact-trust-status-untrustworthy trust-status)] [rn/view {:style {:flex-direction :row}} [avatar-view {:contact contact @@ -266,6 +268,7 @@ :verified? ens-verified :contact? added? :muted? muted + :untrustworthy? untrustworthy? :time-str (datetime/to-short-str timestamp) :style {:flex-shrink 1}}] [last-message-preview group-chat last-message muted unread-messages?]]] diff --git a/src/status_im/contexts/contact/trust/events.cljs b/src/status_im/contexts/contact/trust/events.cljs new file mode 100644 index 00000000000..d31197582c9 --- /dev/null +++ b/src/status_im/contexts/contact/trust/events.cljs @@ -0,0 +1,96 @@ +(ns status-im.contexts.contact.trust.events + (:require + [status-im.common.confirmation-drawer.view :as confirmation-drawer] + [status-im.constants :as constants] + [status-im.contexts.profile.utils :as profile.utils] + [taoensso.timbre :as log] + [utils.i18n :as i18n] + [utils.re-frame :as rf])) + +(rf/reg-event-fx :contact/mark-as-untrusted-success + (fn [{:keys [db]} [contact-id]] + {:db (update-in db + [:contacts/contacts contact-id] + assoc + :trust-status + constants/contact-trust-status-untrustworthy) + :fx [[:dispatch + [:toasts/upsert + {:type :positive + :text (i18n/label :t/marked-as-untrusted)}]]]})) + +(rf/reg-event-fx :contact/mark-as-untrusted + (fn [_ [contact-id name]] + {:json-rpc/call + [{:method "wakuext_markAsUntrustworthy" + :params [contact-id] + :on-success [:contact/mark-as-untrusted-success contact-id name] + :on-error #(log/error "failed mark contact as untrusted" + {:event :contact/mark-as-untrusted + :contact-id contact-id + :error %})}]})) + +(rf/reg-event-fx :contact/remove-trust-status-success + (fn [{:keys [db]} [contact-id]] + {:db (update-in db + [:contacts/contacts contact-id] + assoc + :trust-status + constants/contact-trust-status-unknown) + :fx [[:dispatch + [:toasts/upsert + {:type :positive + :text (i18n/label :t/untrusted-mark-removed)}]]]})) + +(rf/reg-event-fx :contact/remove-trust-status + (fn [_ [contact-id name]] + {:json-rpc/call + [{:method "wakuext_removeTrustStatus" + :params [contact-id] + :on-success [:contact/remove-trust-status-success contact-id name] + :on-error #(log/error "failed remove contact trust status" + {:event :contact/remove-trust-status + :contact-id contact-id + :error %})}]})) + +(defn pending-contact-request-from-contact-id + [db contact-id] + (->> (get-in db [:activity-center :contact-requests]) + (filter #(= contact-id (:author %))) + first)) + +(rf/reg-event-fx :contact/mark-as-untrusted-sheet + (fn [{:keys [db]} [{:keys [public-key contact-request-state] :as contact}]] + (let [name (profile.utils/displayed-name contact) + contact? (= contact-request-state + constants/contact-request-state-mutual) + request? (= contact-request-state + constants/contact-request-state-received) + contact-request (when request? + (pending-contact-request-from-contact-id db public-key))] + {:fx [[:dispatch + [:show-bottom-sheet + {:content (fn [] + [confirmation-drawer/confirmation-drawer + (cond-> {:title (i18n/label :t/mark-as-untrusted) + :description (i18n/label :t/mark-as-untrusted-description + {:username (:primary-name contact)}) + :context contact + :accessibility-label :mark-as-untrustworthy + :button-text (i18n/label :t/mark-as-untrusted-button) + :on-press (fn [] + (rf/dispatch [:hide-bottom-sheet]) + (rf/dispatch + [:contact/mark-as-untrusted + public-key name]))} + contact? + (assoc :extra-action (fn [] + (rf/dispatch [:contact.ui/remove-contact-pressed + contact])) + :extra-text (i18n/label :t/remove-contact)) + request? + (assoc :extra-action (fn [] + (rf/dispatch + [:activity-center.contact-requests/decline + (:id contact-request)])) + :extra-text (i18n/label :t/decline-contact-request)))])}]]]}))) diff --git a/src/status_im/contexts/profile/contact/actions/view.cljs b/src/status_im/contexts/profile/contact/actions/view.cljs index 808a158a8f2..e5c2763fafb 100644 --- a/src/status_im/contexts/profile/contact/actions/view.cljs +++ b/src/status_im/contexts/profile/contact/actions/view.cljs @@ -2,8 +2,6 @@ (:require [clojure.string :as string] [quo.core :as quo] [react-native.core :as rn] - [status-im.common.not-implemented :as not-implemented] - [status-im.config :as config] [status-im.constants :as constants] [status-im.contexts.profile.contact.add-nickname.view :as add-nickname] [status-im.contexts.profile.contact.block-contact.view :as block-contact] @@ -11,6 +9,11 @@ [utils.i18n :as i18n] [utils.re-frame :as rf])) +(defn hide-sheet-and-dispatch + [event] + (rf/dispatch [:hide-bottom-sheet]) + (rf/dispatch event)) + (defn on-add-nickname [] (rf/dispatch [:show-bottom-sheet @@ -25,44 +28,51 @@ (defn view [] - (let [{:keys [nickname public-key contact-request-state blocked?] - :as contact} (rf/sub [:contacts/current-contact]) - full-name (profile.utils/displayed-name contact) - on-remove-nickname (rn/use-callback - (fn [] - (rf/dispatch [:hide-bottom-sheet]) - (rf/dispatch [:toasts/upsert - {:id :remove-nickname - :type :positive - :text (i18n/label :t/nickname-removed)}]) - (rf/dispatch [:contacts/update-nickname public-key ""])) - [public-key]) - on-show-qr (rn/use-callback - (fn [] - (rf/dispatch [:universal-links/generate-profile-url - {:public-key public-key - :on-success #(rf/dispatch [:open-modal - :screen/share-contact])}])) - [public-key]) - has-nickname? (rn/use-memo (fn [] (not (string/blank? nickname))) [nickname]) - on-share-profile (rn/use-callback - (fn [] - (rf/dispatch [:universal-links/generate-profile-url - {:public-key public-key - :on-success #(rf/dispatch [:open-share - {:options {:message %}}])}])) - [public-key]) - on-remove-contact (rn/use-callback - (fn [] - (rf/dispatch [:hide-bottom-sheet]) - (rf/dispatch [:toasts/upsert - {:id :remove-contact - :type :positive - :text (->> (i18n/label :t/removed-from-contacts) - (string/lower-case) - (str full-name " "))}]) - (rf/dispatch [:contact.ui/remove-contact-pressed contact])) - [public-key full-name])] + (let [{:keys [nickname public-key contact-request-state blocked? trust-status] + :as contact} (rf/sub [:contacts/current-contact]) + full-name (profile.utils/displayed-name contact) + on-remove-nickname (rn/use-callback + (fn [] + (rf/dispatch [:hide-bottom-sheet]) + (rf/dispatch [:toasts/upsert + {:id :remove-nickname + :type :positive + :text (i18n/label :t/nickname-removed)}]) + (rf/dispatch [:contacts/update-nickname public-key ""])) + [public-key]) + on-show-qr (rn/use-callback + (fn [] + (rf/dispatch [:universal-links/generate-profile-url + {:public-key public-key + :on-success #(rf/dispatch [:open-modal + :screen/share-contact])}])) + [public-key]) + has-nickname? (rn/use-memo (fn [] (not (string/blank? nickname))) [nickname]) + on-share-profile (rn/use-callback + (fn [] + (rf/dispatch [:universal-links/generate-profile-url + {:public-key public-key + :on-success #(rf/dispatch [:open-share + {:options {:message %}}])}])) + [public-key]) + untrustworthy? (= trust-status + constants/contact-trust-status-untrustworthy) + on-trust-mark-press (fn [] + (hide-sheet-and-dispatch + (if untrustworthy? + [:contact/remove-trust-status public-key full-name] + [:contact/mark-as-untrusted-sheet contact]))) + on-remove-contact (rn/use-callback + (fn [] + (rf/dispatch [:hide-bottom-sheet]) + (rf/dispatch [:toasts/upsert + {:id :remove-contact + :type :positive + :text (->> (i18n/label :t/removed-from-contacts) + (string/lower-case) + (str full-name " "))}]) + (rf/dispatch [:contact.ui/remove-contact-pressed contact])) + [public-key full-name])] [quo/action-drawer [(concat [{:icon :i/edit @@ -87,13 +97,14 @@ :add-divider? true :accessibility-label :remove-nickname :danger? true}) - (when config/show-not-implemented-features? - {:icon :i/untrustworthy - :label (i18n/label :t/mark-untrustworthy) - :on-press not-implemented/alert - :accessibility-label :mark-untrustworthy - :add-divider? (when-not has-nickname? true) - :danger? true}) + {:icon :i/untrustworthy + :label (i18n/label (if untrustworthy? + :t/remove-untrusted-mark + :t/mark-as-untrusted)) + :on-press on-trust-mark-press + :accessibility-label :mark-as-untrusted + :add-divider? (when-not has-nickname? true) + :danger? true} (when (= constants/contact-request-state-mutual contact-request-state) {:icon :i/remove-user :label (i18n/label :t/remove-contact) diff --git a/src/status_im/contexts/profile/contact/header/view.cljs b/src/status_im/contexts/profile/contact/header/view.cljs index 462ee764b7f..2360b5a23fd 100644 --- a/src/status_im/contexts/profile/contact/header/view.cljs +++ b/src/status_im/contexts/profile/contact/header/view.cljs @@ -26,7 +26,7 @@ (defn view [{:keys [scroll-y]}] (let [{:keys [public-key customization-color ens-name nickname secondary-name - emoji-hash bio blocked? contact-request-state] + emoji-hash bio blocked? contact-request-state trust-status] :as contact} (rf/sub [:contacts/current-contact]) customization-color (or customization-color constants/profile-default-color) full-name (profile.utils/displayed-name contact) @@ -35,12 +35,18 @@ theme (quo.context/use-theme) contact-status (rn/use-memo (fn [] - (cond - (= contact-request-state - constants/contact-request-state-mutual) :contact - blocked? :blocked - :else nil)) - [blocked? contact-request-state]) + (let [contact? (= contact-request-state + constants/contact-request-state-mutual) + untrustworthy? (= trust-status + constants/contact-trust-status-untrustworthy)] + (cond + (and contact? + untrustworthy?) :untrustworthy-contact + blocked? :blocked + contact? :contact + untrustworthy? :untrustworthy + :else nil))) + [blocked? contact-request-state trust-status]) on-start-chat (rn/use-callback #(rf/dispatch [:chat.ui/start-chat public-key ens-name]) [ens-name public-key]) diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 4d0dd09e4e9..bd432b70c2e 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -27,6 +27,7 @@ status-im.contexts.communities.overview.events status-im.contexts.communities.sharing.events status-im.contexts.contact.blocking.events + status-im.contexts.contact.trust.events status-im.contexts.keycard.change-pin.events status-im.contexts.keycard.effects status-im.contexts.keycard.events diff --git a/src/status_im/subs/contact.cljs b/src/status_im/subs/contact.cljs index 093a4630674..a65131e644d 100644 --- a/src/status_im/subs/contact.cljs +++ b/src/status_im/subs/contact.cljs @@ -302,3 +302,10 @@ (seq admins) (assoc :owner {:title (i18n/label :t/owner) :data admins}) (seq online) (assoc :online {:title (i18n/label :t/online) :data online}) (seq offline) (assoc :offline {:title (i18n/label :t/offline) :data offline})))))) + +(re-frame/reg-sub + :contacts/contact-trust-status + (fn [[_ chat-id]] + [(re-frame/subscribe [:contacts/contact-by-identity chat-id])]) + (fn [[contact]] + (get contact :trust-status constants/contact-trust-status-unknown))) diff --git a/translations/en.json b/translations/en.json index 352d8137d00..adb0b03142d 100644 --- a/translations/en.json +++ b/translations/en.json @@ -760,6 +760,7 @@ "dec": "Dec", "decimals": "Decimals", "decline": "Decline", + "decline-contact-request": "Decline contact request", "declined": "Declined", "decryption-failed-content": "An error occured decrypting your data. You might need to erase your old data and generate a new account. Tap “Apply” to erase or “Cancel” to try again", "default": "Default", @@ -1623,8 +1624,11 @@ "mark-all-notifications-as-read": "Mark all notifications as read", "mark-all-read": "Mark all read", "mark-as-read": "Mark as read", - "mark-untrustworthy": "Mark as Untrustworthy", + "mark-as-untrusted": "Mark as untrusted", + "mark-as-untrusted-button": "Mark", + "mark-as-untrusted-description": "{{username}} will be marked as untrusted. This mark will only be visible to you.", "mark-user-untrustworthy": "Mark {{username}} as untrustworthy", + "marked-as-untrusted": "Marked as untrusted", "market": "Market", "market-cap": "Market cap", "master-account": "Master account", @@ -2257,6 +2261,7 @@ "remove-saved-address": "Remove saved address", "remove-saved-address-description": "Transaction history relating to this address will no longer be labelled ‘{{name}}’.", "remove-token": "Remove token", + "remove-untrusted-mark": "Remove untrusted mark", "remove-user-from-group": "Remove {{username}} from the group", "remove-watched-address-desc": "The watched address will be removed from all of your synced devices.", "remove-watched-address-title": "Remove watched address", @@ -2845,6 +2850,7 @@ "unsupported-file": "Unsupported file", "until": "until", "until-you-turn-it-back-on": "you turn it back on", + "untrusted-mark-removed": "Untrusted mark removed", "untrustworthy": "Untrustworthy", "update": "Update", "update-account-name": "Update account name",