From 1034d542db43e1d2cd29227f56e83d008406d66f Mon Sep 17 00:00:00 2001 From: Parvesh Monu Date: Fri, 29 Oct 2021 21:55:01 +0530 Subject: [PATCH] Online Status Indicator (#12716) --- src/quo/design_system/colors.cljs | 5 +- src/status_im/constants.cljs | 6 + src/status_im/data_store/settings.cljs | 6 +- .../data_store/visibility_status_updates.cljs | 24 +++ src/status_im/db.cljs | 1 + src/status_im/ethereum/json_rpc.cljs | 2 + src/status_im/events.cljs | 2 + src/status_im/multiaccounts/core.cljs | 11 + src/status_im/multiaccounts/login/core.cljs | 6 +- src/status_im/navigation/core.cljs | 19 +- src/status_im/signals/core.cljs | 4 +- src/status_im/subs.cljs | 65 +++++- src/status_im/transport/message/core.cljs | 21 +- .../ui/components/chat_icon/screen.cljs | 34 ++- .../ui/components/chat_icon/styles.cljs | 45 ---- .../ui/components/profile_header/view.cljs | 8 +- .../ui/screens/communities/members.cljs | 17 +- .../ui/screens/communities/profile.cljs | 3 +- .../ui/screens/contacts_list/views.cljs | 11 +- .../ui/screens/profile/contact/views.cljs | 3 +- .../ui/screens/profile/user/views.cljs | 7 +- .../profile/visibility_status/styles.cljs | 76 +++++++ .../profile/visibility_status/utils.cljs | 95 +++++++++ .../profile/visibility_status/views.cljs | 200 ++++++++++++++++++ src/status_im/ui/screens/views.cljs | 11 + src/status_im/utils/datetime.cljs | 8 + .../visibility_status_popover/core.cljs | 21 ++ .../visibility_status_updates/core.cljs | 179 ++++++++++++++++ status-go-version.json | 6 +- translations/en.json | 9 +- 30 files changed, 815 insertions(+), 90 deletions(-) create mode 100644 src/status_im/data_store/visibility_status_updates.cljs create mode 100644 src/status_im/ui/screens/profile/visibility_status/styles.cljs create mode 100644 src/status_im/ui/screens/profile/visibility_status/utils.cljs create mode 100644 src/status_im/ui/screens/profile/visibility_status/views.cljs create mode 100644 src/status_im/visibility_status_popover/core.cljs create mode 100644 src/status_im/visibility_status_updates/core.cljs diff --git a/src/quo/design_system/colors.cljs b/src/quo/design_system/colors.cljs index 4e24c146ca..f84761e5ed 100644 --- a/src/quo/design_system/colors.cljs +++ b/src/quo/design_system/colors.cljs @@ -218,4 +218,7 @@ (set! pin-background (:pin-background old-colors-mapping-colors))) (reset! theme-type type))) - +;; Colors related to Visibility Status +(def color-online "#7CDA00") +(def color-dnd "#FA6565") +(def color-inactive "#939BA1") diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index f7aff55977..520d272302 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -162,3 +162,9 @@ (def ^:const activity-center-notification-type-private-group-chat 2) (def ^:const activity-center-notification-type-mention 3) (def ^:const activity-center-notification-type-reply 4) + +(def ^:const visibility-status-unknown 0) +(def ^:const visibility-status-automatic 1) +(def ^:const visibility-status-dnd 2) +(def ^:const visibility-status-always-online 3) +(def ^:const visibility-status-inactive 4) diff --git a/src/status_im/data_store/settings.cljs b/src/status_im/data_store/settings.cljs index 942d7edeb9..e6d9c5bf3d 100644 --- a/src/status_im/data_store/settings.cljs +++ b/src/status_im/data_store/settings.cljs @@ -1,7 +1,8 @@ (ns status-im.data-store.settings (:require [status-im.utils.config :as config] - [status-im.ethereum.eip55 :as eip55])) + [status-im.ethereum.eip55 :as eip55] + [status-im.data-store.visibility-status-updates :as visibility-status-updates])) (defn rpc->networks [networks] (reduce (fn [acc {:keys [id] :as network}] @@ -50,4 +51,5 @@ (update :link-previews-enabled-sites set) (update :custom-bootnodes rpc->custom-bootnodes) (update :custom-bootnodes-enabled? rpc->custom-bootnodes) - (update :currency keyword))) + (update :currency keyword) + (visibility-status-updates/<-rpc-settings))) diff --git a/src/status_im/data_store/visibility_status_updates.cljs b/src/status_im/data_store/visibility_status_updates.cljs new file mode 100644 index 0000000000..3ae6720870 --- /dev/null +++ b/src/status_im/data_store/visibility_status_updates.cljs @@ -0,0 +1,24 @@ +(ns status-im.data-store.visibility-status-updates + (:require [clojure.set :as clojure.set] + [re-frame.core :as re-frame] + [status-im.ethereum.json-rpc :as json-rpc] + [status-im.utils.fx :as fx] + [taoensso.timbre :as log])) + +(defn <-rpc [visibility-status-update] + (clojure.set/rename-keys visibility-status-update {:publicKey :public-key + :statusType :status-type})) +(defn <-rpc-settings [settings] + (-> settings + (clojure.set/rename-keys + {:current-user-status :current-user-visibility-status}) + (update :current-user-visibility-status <-rpc))) + +(fx/defn fetch-visibility-status-updates-rpc [_] + {::json-rpc/call [{:method "wakuext_statusUpdates" + :params [] + :on-success #(re-frame/dispatch + [:visibility-status-updates/visibility-status-updates-loaded + (:statusUpdates ^js %)]) + :on-failure #(log/error + "failed to fetch visibility-status-updates" %)}]}) diff --git a/src/status_im/db.cljs b/src/status_im/db.cljs index 9ffb6e3b0d..9b9d4958d8 100644 --- a/src/status_im/db.cljs +++ b/src/status_im/db.cljs @@ -32,6 +32,7 @@ :tooltips {} :dimensions/window (dimensions/window) :registry {} + :visibility-status-updates {} :stickers/packs-owned #{} :stickers/packs-pending #{} :keycard {:nfc-enabled? false diff --git a/src/status_im/ethereum/json_rpc.cljs b/src/status_im/ethereum/json_rpc.cljs index 929bb74c0c..a95df82c4d 100644 --- a/src/status_im/ethereum/json_rpc.cljs +++ b/src/status_im/ethereum/json_rpc.cljs @@ -131,6 +131,8 @@ "wakuext_enablePushNotificationsBlockMentions" {} "wakuext_disablePushNotificationsBlockMentions" {} "wakuext_unreadActivityCenterNotificationsCount" {} + "wakuext_setUserStatus" {} + "wakuext_statusUpdates" {} "multiaccounts_getIdentityImages" {} "multiaccounts_getIdentityImage" {} "multiaccounts_storeIdentityImage" {} diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 01ef46f914..e677f47a1c 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -27,6 +27,8 @@ status-im.wallet.choose-recipient.core status-im.wallet.accounts.core status-im.popover.core + status-im.visibility-status-popover.core + status-im.visibility-status-updates.core status-im.bottom-sheet.core status-im.add-new.core status-im.search.core diff --git a/src/status_im/multiaccounts/core.cljs b/src/status_im/multiaccounts/core.cljs index 12e0b79719..d7cb1ac24e 100644 --- a/src/status_im/multiaccounts/core.cljs +++ b/src/status_im/multiaccounts/core.cljs @@ -64,6 +64,17 @@ (str "@" (or username ens-name))) (or alias (gfycat/generate-gfy public-key))))) +(defn contact-by-identity [contacts identity] + (or (get contacts identity) + (contact-with-names {:public-key identity}))) + +(defn contact-two-names-by-identity [contact current-multiaccount identity] + (let [me? (= (:public-key current-multiaccount) identity)] + (if me? + [(or (:preferred-name current-multiaccount) + (gfycat/generate-gfy identity))] + (contact-two-names contact false)))) + (def photo-quality-thumbnail :thumbnail) (def photo-quality-large :large) diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index b0164367b9..f6a371b7ed 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -37,7 +37,8 @@ [status-im.notifications-center.core :as notifications-center] [status-im.navigation :as navigation] [status-im.signing.eip1559 :as eip1559] - [status-im.data-store.chats :as data-store.chats])) + [status-im.data-store.chats :as data-store.chats] + [status-im.data-store.visibility-status-updates :as visibility-status-updates-store])) (re-frame/reg-fx ::initialize-communities-enabled @@ -338,7 +339,8 @@ (get-group-chat-invitations) (multiaccounts/get-profile-picture) (multiaccounts/switch-preview-privacy-mode-flag) - (link-preview/request-link-preview-whitelist)))) + (link-preview/request-link-preview-whitelist) + (visibility-status-updates-store/fetch-visibility-status-updates-rpc)))) (defn get-new-auth-method [auth-method save-password?] (when save-password? diff --git a/src/status_im/navigation/core.cljs b/src/status_im/navigation/core.cljs index 805e4247ea..f1abf281c9 100644 --- a/src/status_im/navigation/core.cljs +++ b/src/status_im/navigation/core.cljs @@ -136,7 +136,9 @@ (fn [^js evn] (let [view-id (keyword (.-componentName evn))] (when (get views/screens view-id) - (when (and (not= view-id :bottom-sheet) (not= view-id :popover)) + (when (and (not= view-id :bottom-sheet) + (not= view-id :popover) + (not= view-id :visibility-status-popover)) (set-view-id view-id) (when-not @curr-modal (reset! pushed-screen-id view-id)))))))) @@ -146,7 +148,8 @@ (.registerComponentDidDisappearListener (.events Navigation) (fn [^js evn] - (when-not (#{"popover" "bottom-sheet" "signing-sheet"} (.-componentName evn)) + (when-not (#{"popover" "bottom-sheet" "signing-sheet" "visibility-status-popover"} + (.-componentName evn)) (doseq [[_ {:keys [ref value]}] @quo.text-input/text-input-refs] (.setNativeProps ^js ref (clj->js {:text value}))) (doseq [[^js text-input default-value] @react/text-input-refs] @@ -290,6 +293,18 @@ (re-frame/reg-fx :show-popover (fn [] (show-overlay "popover"))) (re-frame/reg-fx :hide-popover (fn [] (dissmiss-overlay "popover"))) +;; VISIBILITY STATUS POPOVER +(defonce visibility-status-popover-reg + (.registerComponent Navigation + "visibility-status-popover" + (fn [] (gestureHandlerRootHOC views/visibility-status-popover-comp)) + (fn [] views/visibility-status-popover-comp))) + +(re-frame/reg-fx :show-visibility-status-popover + (fn [] (show-overlay "visibility-status-popover"))) +(re-frame/reg-fx :hide-visibility-status-popover + (fn [] (dissmiss-overlay "visibility-status-popover"))) + ;; BOTTOM SHEETS (defonce bottom-sheet-reg (.registerComponent Navigation diff --git a/src/status_im/signals/core.cljs b/src/status_im/signals/core.cljs index e299c50958..9a68d9778f 100644 --- a/src/status_im/signals/core.cljs +++ b/src/status_im/signals/core.cljs @@ -7,6 +7,7 @@ [status-im.notifications.local :as local-notifications] [status-im.chat.models.message :as models.message] [status-im.chat.models.link-preview :as link.preview] + [status-im.visibility-status-updates.core :as visibility-status-updates] [status-im.utils.fx :as fx] [taoensso.timbre :as log])) @@ -41,7 +42,8 @@ {:db (assoc db :peers-summary peers-summary :peers-count peers-count)} - (mailserver/peers-summary-change previous-summary)))) + (mailserver/peers-summary-change previous-summary) + (visibility-status-updates/peers-summary-change peers-count)))) (fx/defn wakuv2-peer-stats [{:keys [db]} peer-stats] diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index df525ac8f3..70edbfb770 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -40,7 +40,8 @@ [status-im.notifications.core :as notifications] [status-im.utils.currency :as currency] [clojure.set :as clojure.set] - [quo.design-system.colors :as colors])) + [quo.design-system.colors :as colors] + [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils])) ;; TOP LEVEL =========================================================================================================== @@ -86,6 +87,7 @@ (reg-root-key-sub :app-state :app-state) (reg-root-key-sub :home-items-show-number :home-items-show-number) (reg-root-key-sub :waku/v2-peer-stats :peer-stats) +(reg-root-key-sub :visibility-status-updates :visibility-status-updates) ;;NOTE this one is not related to ethereum network ;; it is about cellular network/ wifi network @@ -200,6 +202,7 @@ (reg-root-key-sub :intro-wizard-state :intro-wizard) (reg-root-key-sub :popover/popover :popover/popover) +(reg-root-key-sub :visibility-status-popover/popover :visibility-status-popover/popover) (reg-root-key-sub :add-account :add-account) (reg-root-key-sub :keycard :keycard) @@ -292,6 +295,36 @@ (fn [communities [_ id]] (get-in communities [id :chats]))) +(re-frame/reg-sub + :communities/community-members + :<- [:communities] + (fn [communities [_ id]] + (get-in communities [id :members]))) + +(re-frame/reg-sub + :communities/sorted-community-members + (fn [[_ community-id]] + (let [contacts (re-frame/subscribe [:contacts/contacts]) + multiaccount (re-frame/subscribe [:multiaccount]) + members (re-frame/subscribe + [:communities/community-members community-id])] + [contacts multiaccount members])) + (fn [[contacts multiaccount members] _] + (let [names (reduce + (fn [acc identity] + (let [me? (= (:public-key multiaccount) + identity) + contact (when-not me? + (multiaccounts/contact-by-identity + contacts identity)) + name (first + (multiaccounts/contact-two-names-by-identity + contact multiaccount identity))] + (assoc acc identity name))) {} (keys members))] + (->> members + (sort-by #(get names (get % 0))) + (sort-by #(visibility-status-utils/visibility-status-order (get % 0))))))) + (re-frame/reg-sub :communities/communities :<- [:communities/enabled?] @@ -355,6 +388,11 @@ ;;GENERAL ============================================================================================================== +(re-frame/reg-sub + :visibility-status-updates/visibility-status-update + :<- [:visibility-status-updates] + (fn [visibility-status-updates [_ public-key]] + (get visibility-status-updates public-key))) (re-frame/reg-sub :multiaccount/logged-in? @@ -784,6 +822,12 @@ (string/blank? (security/safe-unmask-data seed)) false)))) +(re-frame/reg-sub + :multiaccount/current-user-visibility-status + :<- [:multiaccount] + (fn [{:keys [current-user-visibility-status]}] + current-user-visibility-status)) + ;;CHAT ============================================================================================================== (re-frame/reg-sub @@ -2208,6 +2252,15 @@ (fn [contacts] (contact.db/get-active-contacts contacts))) +(re-frame/reg-sub + :contacts/sorted-contacts + :<- [:contacts/active] + (fn [active-contacts] + (->> active-contacts + (sort-by :alias) + (sort-by + #(visibility-status-utils/visibility-status-order (:public-key %)))))) + (re-frame/reg-sub :contacts/active-count :<- [:contacts/active] @@ -2265,8 +2318,7 @@ :contacts/contact-by-identity :<- [:contacts/contacts] (fn [contacts [_ identity]] - (or (get contacts identity) - (multiaccounts/contact-with-names {:public-key identity})))) + (multiaccounts/contact-by-identity contacts identity))) (re-frame/reg-sub :contacts/contact-added? @@ -2281,11 +2333,8 @@ [(re-frame/subscribe [:contacts/contact-by-identity identity]) (re-frame/subscribe [:multiaccount])]) (fn [[contact current-multiaccount] [_ identity]] - (let [me? (= (:public-key current-multiaccount) identity)] - (if me? - [(or (:preferred-name current-multiaccount) - (gfycat/generate-gfy identity))] - (multiaccounts/contact-two-names contact false))))) + (multiaccounts/contact-two-names-by-identity contact current-multiaccount + identity))) (re-frame/reg-sub :contacts/contact-name-by-identity diff --git a/src/status_im/transport/message/core.cljs b/src/status_im/transport/message/core.cljs index 8beabc6d2d..d53ed6cbe6 100644 --- a/src/status_im/transport/message/core.cljs +++ b/src/status_im/transport/message/core.cljs @@ -19,6 +19,7 @@ [status-im.constants :as constants] [status-im.multiaccounts.model :as multiaccounts.model] [status-im.notifications-center.core :as notifications-center] + [status-im.visibility-status-updates.core :as models.visibility-status-updates] [clojure.string :as string])) (fx/defn process-next @@ -42,6 +43,8 @@ ^js activity-notifications (.-activityCenterNotifications response-js) ^js pin-messages (.-pinMessages response-js) ^js removed-messages (.-removedMessages response-js) + ^js visibility-status-updates (.-statusUpdates response-js) + ^js current-visibility-status (.-currentStatus response-js) sync-handler (when-not process-async process-response)] (cond @@ -149,7 +152,23 @@ (js-delete response-js "removedMessages") (fx/merge cofx (process-next response-js sync-handler) - (models.message/handle-removed-messages removed-messages-clj)))))) + (models.message/handle-removed-messages removed-messages-clj))) + + (seq visibility-status-updates) + (let [visibility-status-updates-clj (types/js->clj visibility-status-updates)] + (js-delete response-js "statusUpdates") + (fx/merge cofx + (process-next response-js sync-handler) + (models.visibility-status-updates/handle-visibility-status-updates + visibility-status-updates-clj))) + + (some? current-visibility-status) + (let [current-visibility-status-clj (types/js->clj current-visibility-status)] + (js-delete response-js "currentStatus") + (fx/merge cofx + (process-next response-js sync-handler) + (models.visibility-status-updates/sync-visibility-status-update + current-visibility-status-clj)))))) (defn group-by-and-update-unviewed-counts "group messages by current chat, profile updates, transactions and update unviewed counters in db for not curent chats" diff --git a/src/status_im/ui/components/chat_icon/screen.cljs b/src/status_im/ui/components/chat_icon/screen.cljs index 0965f1ffa5..e0f9c327e5 100644 --- a/src/status_im/ui/components/chat_icon/screen.cljs +++ b/src/status_im/ui/components/chat_icon/screen.cljs @@ -6,7 +6,9 @@ [status-im.ui.components.chat-icon.styles :as styles] [quo.design-system.colors :as colors] [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.photos :as photos])) + [status-im.ui.screens.chat.photos :as photos] + [status-im.profile.db :as profile.db] + [status-im.ui.screens.profile.visibility-status.utils :as visibility-status-utils])) ;;TODO REWORK THIS NAMESPACE @@ -42,6 +44,24 @@ [react/view (:default-chat-icon styles) [react/text {:style (:default-chat-icon-text styles)} emoji]])) +(defn profile-photo-plus-dot-view + [{:keys [public-key photo-container photo-path community?]}] + (let [photo-path (if (nil? photo-path) + @(re-frame.core/subscribe [:chats/photo-path public-key]) + photo-path) + photo-container (if (nil? photo-container) + styles/container-chat-list photo-container) + size (:width photo-container) + identicon? (when photo-path (profile.db/base64-png? photo-path)) + dot-styles (visibility-status-utils/icon-visibility-status-dot + public-key size identicon?)] + [react/view {:style photo-container + :accessibility-label :profile-photo} + [photos/photo photo-path {:size size}] + (when-not community? + [react/view {:style dot-styles + :accessibility-label :profile-photo-dot}])])) + (defn emoji-chat-icon-view [chat-id group-chat name emoji styles] [react/view (:container styles) @@ -49,8 +69,8 @@ (if (string/blank? emoji) [default-chat-icon name styles] [emoji-chat-icon emoji styles]) - (let [photo-path @(re-frame.core/subscribe [:chats/photo-path chat-id])] - [photos/photo photo-path styles]))]) + [profile-photo-plus-dot-view {:public-key chat-id + :photo-container (:default-chat-icon styles)}])]) (defn chat-icon-view-toolbar [chat-id group-chat name color emoji] @@ -131,7 +151,8 @@ (if-not (string/blank? photo-path) [photos/photo photo-path styles])))) -(defn profile-icon-view [photo-path name color emoji edit? size override-styles] +(defn profile-icon-view + [photo-path name color emoji edit? size override-styles public-key community?] (let [styles (merge {:container {:width size :height size} :size size :chat-icon styles/chat-icon-profile @@ -141,7 +162,10 @@ (styles/emoji-chat-icon-text size))} override-styles)] [react/view (:container styles) (if (and photo-path (seq photo-path)) - [photos/photo photo-path styles] + [profile-photo-plus-dot-view {:photo-path photo-path + :public-key public-key + :photo-container (:container styles) + :community? community?}] (if (string/blank? emoji) [default-chat-icon name styles] [emoji-chat-icon emoji styles])) diff --git a/src/status_im/ui/components/chat_icon/styles.cljs b/src/status_im/ui/components/chat_icon/styles.cljs index 18ead670ea..789e8f8b44 100644 --- a/src/status_im/ui/components/chat_icon/styles.cljs +++ b/src/status_im/ui/components/chat_icon/styles.cljs @@ -70,51 +70,6 @@ :height 64 :border-radius 32})) -(def online-view-wrapper - {:position :absolute - :bottom -2 - :right -2 - :width 17 - :height 17 - :border-radius 11 - :background-color colors/white}) - -(def online-view - {:position :absolute - :bottom 2 - :right 2 - :width 13 - :height 13 - :border-radius 9 - :background-color colors/blue}) - -(def online-view-profile - (merge online-view - {:width 24 - :height 24 - :border-radius 12})) - -(def online-dot - {:position :absolute - :top 5 - :width 3 - :height 3 - :border-radius 2 - :background-color colors/white}) -(def online-dot-left (merge online-dot {:left 2.8})) -(def online-dot-right (merge online-dot {:left 7.2})) - -(def online-dot-profile - (merge online-dot - {:top 8 - :width 4 - :height 4})) - -(def online-dot-left-profile - (merge online-dot-profile {:left 5})) -(def online-dot-right-profile - (merge online-dot-profile {:left 11})) - (def container-chat-list {:width 40 :height 40}) diff --git a/src/status_im/ui/components/profile_header/view.cljs b/src/status_im/ui/components/profile_header/view.cljs index f1fb0cedda..411de6d9ee 100644 --- a/src/status_im/ui/components/profile_header/view.cljs +++ b/src/status_im/ui/components/profile_header/view.cljs @@ -37,8 +37,10 @@ (when-not minimized {:padding-top subtitle-margin}))) -(defn extended-header [{:keys [title photo color subtitle subtitle-icon on-edit on-press monospace bottom-separator emoji] - :or {bottom-separator true}}] +(defn extended-header + [{:keys [title photo color subtitle subtitle-icon on-edit on-press monospace + bottom-separator emoji public-key community?] + :or {bottom-separator true}}] (fn [{:keys [animation minimized]}] (let [wrapper (if on-press [rn/touchable-opacity {:on-press on-press}] @@ -57,7 +59,7 @@ [chat-icon.screen/profile-icon-view photo title color emoji (and (not minimized) on-edit) (if minimized avatar-minimized-size avatar-extended-size) - nil]]]) + nil public-key community?]]]) [animated/view {:style (header-text) :pointer-events :box-none} [quo/text {:animated? true diff --git a/src/status_im/ui/screens/communities/members.cljs b/src/status_im/ui/screens/communities/members.cljs index 02e738c0b3..371daa4121 100644 --- a/src/status_im/ui/screens/communities/members.cljs +++ b/src/status_im/ui/screens/communities/members.cljs @@ -51,8 +51,9 @@ {:title first-name :subtitle second-name :accessibility-label :member-item - :icon [chat-icon/contact-icon-contacts-tab - (multiaccounts/displayed-photo member)] + :icon [chat-icon/profile-photo-plus-dot-view + {:public-key public-key + :photo-path (multiaccounts/displayed-photo member)}] :accessory (when (not= public-key my-public-key) [quo/button {:on-press #(>evt [:bottom-sheet/show-sheet @@ -89,17 +90,17 @@ (let [{:keys [community-id]} ( [topbar/topbar {:title (i18n/label :t/community-members-title) - - :subtitle (str (count members))}] + :subtitle (str (count sorted-members))}] [header community-id] (when (and can-manage-users? (= constants/community-on-request-access (:access permissions))) [requests-to-join community-id]) - [rn/flat-list {:data (keys members) + [rn/flat-list {:data (keys sorted-members) :render-data {:community-id community-id :my-public-key my-public-key :can-kick-users? (and can-manage-users? diff --git a/src/status_im/ui/screens/communities/profile.cljs b/src/status_im/ui/screens/communities/profile.cljs index 463a2e6c43..dbf9c4a919 100644 --- a/src/status_im/ui/screens/communities/profile.cljs +++ b/src/status_im/ui/screens/communities/profile.cljs @@ -41,7 +41,8 @@ (get-in community [:images :large :uri])) :subtitle (if show-members-count? (i18n/label-pluralize members-count :t/community-members {:count members-count}) - (i18n/label :t/open-membership))}) + (i18n/label :t/open-membership)) + :community? true}) :use-insets true} [:<> (when-not (string/blank? description) diff --git a/src/status_im/ui/screens/contacts_list/views.cljs b/src/status_im/ui/screens/contacts_list/views.cljs index a0696967d7..3b68804f5f 100644 --- a/src/status_im/ui/screens/contacts_list/views.cljs +++ b/src/status_im/ui/screens/contacts_list/views.cljs @@ -15,8 +15,9 @@ [quo/list-item {:title first-name :subtitle second-name - :icon [chat-icon.screen/contact-icon-contacts-tab - (multiaccounts/displayed-photo contact)] + :icon [chat-icon.screen/profile-photo-plus-dot-view + {:public-key public-key + :photo-path (multiaccounts/displayed-photo contact)}] :chevron true :on-press #(re-frame/dispatch [:chat.ui/show-profile public-key])}])) @@ -30,7 +31,7 @@ (defview contacts-list [] (letsubs [blocked-contacts-count [:contacts/blocked-count] - contacts [:contacts/active]] + sorted-contacts [:contacts/sorted-contacts]] [react/scroll-view {:flex 1} [add-new-contact] (when (pos? blocked-contacts-count) @@ -44,9 +45,9 @@ :accessory :text :accessory-text blocked-contacts-count :on-press #(re-frame/dispatch [:navigate-to :blocked-users-list])}]]) - (if (seq contacts) + (if (seq sorted-contacts) [list.views/flat-list - {:data contacts + {:data sorted-contacts :key-fn :address :render-fn contacts-list-item}] [react/view {:align-items :center diff --git a/src/status_im/ui/screens/profile/contact/views.cljs b/src/status_im/ui/screens/profile/contact/views.cljs index e3512d2a99..e268d2c0cc 100644 --- a/src/status_im/ui/screens/profile/contact/views.cljs +++ b/src/status_im/ui/screens/profile/contact/views.cljs @@ -205,7 +205,8 @@ :title first-name :photo (multiaccounts/displayed-photo contact) :monospace (not ens-verified) - :subtitle second-name})] + :subtitle second-name + :public-key public-key})] [react/view {:height 1 :background-color colors/gray-lighter :margin-top 8}] [nickname-settings contact] [pin-settings public-key (count pinned-messages)] diff --git a/src/status_im/ui/screens/profile/user/views.cljs b/src/status_im/ui/screens/profile/user/views.cljs index 34ad77fb5e..dfae3fe481 100644 --- a/src/status_im/ui/screens/profile/user/views.cljs +++ b/src/status_im/ui/screens/profile/user/views.cljs @@ -17,7 +17,9 @@ [status-im.ui.components.profile-header.view :as profile-header] [status-im.ui.screens.profile.user.edit-picture :as edit] [status-im.utils.utils :as utils] - [status-im.ethereum.stateofus :as stateofus]) + [status-im.ethereum.stateofus :as stateofus] + [quo.design-system.spacing :as spacing] + [status-im.ui.screens.profile.visibility-status.views :as visibility-status]) (:require-macros [status-im.utils.views :as views])) (views/defview share-chat-key [] @@ -64,6 +66,9 @@ chain @(re-frame/subscribe [:chain-keyword]) registrar (stateofus/get-cached-registrar chain)] [:<> + [visibility-status/visibility-status-button + visibility-status/calculate-button-height-and-dispatch-popover] + [quo/separator {:style {:margin-top (:tiny spacing/spacing)}}] [quo/list-item (cond-> {:title (or (when registrar preferred-name) (i18n/label :t/ens-usernames)) diff --git a/src/status_im/ui/screens/profile/visibility_status/styles.cljs b/src/status_im/ui/screens/profile/visibility_status/styles.cljs new file mode 100644 index 0000000000..4143286087 --- /dev/null +++ b/src/status_im/ui/screens/profile/visibility_status/styles.cljs @@ -0,0 +1,76 @@ +(ns status-im.ui.screens.profile.visibility-status.styles + (:require [quo.design-system.colors :as colors])) + +(defn visibility-status-button-container [] + {:background-color (:interactive-02 @colors/theme) + :margin-left 16 + :border-radius 16 + :border-top-left-radius 4 + :align-self :flex-start + :flex-direction :row + :align-items :center + :justify-content :center + :padding 6 + :padding-right 12}) + +(defn visibility-status-dot [dot-color size] + {:background-color dot-color + :width size + :height size + :border-radius (/ size 2) + :border-width 1 + :border-color colors/white}) + +(defn visibility-status-profile-dot [color size border-width margin-left] + (merge (visibility-status-dot color size) + {:margin-right 6 + :margin-left margin-left + :border-width border-width})) + +(defn visibility-status-text [] + {:color (:text-01 @colors/theme) + :font-size 16 + :text-align :center}) + +(defn visibility-status-subtitle [] + {:color (:text-02 @colors/theme) + :font-size 16 + :margin-left 22 + :margin-right 6}) + +(defn visibility-status-option [] + {:flex-direction :row + :align-items :center}) + +(defn visibility-status-options [scale position] + {:background-color (if (colors/dark?) + (:interactive-02 @colors/theme) + colors/white) + :border-radius 16 + :border-top-left-radius 4 + :justify-content :center + :align-self :flex-start + :left 16 + :top -76 + :padding-bottom 6 + :padding-top 8 + :transform [{:scaleY scale} + {:translateY position}]}) + +(defn visibility-status-popover-container [] + {:position :absolute + :top 0 + :bottom 0 + :left 0 + :right 0}) + +(defn visibility-status-popover-ios-backdrop [alpha-value] + {:flex 1 + :background-color colors/black-persist + :opacity alpha-value}) + +(defn visibility-status-popover-child-container [window-height] + {:position :absolute + :height window-height + :left 0 + :right 0}) diff --git a/src/status_im/ui/screens/profile/visibility_status/utils.cljs b/src/status_im/ui/screens/profile/visibility_status/utils.cljs new file mode 100644 index 0000000000..a06c2f354c --- /dev/null +++ b/src/status_im/ui/screens/profile/visibility_status/utils.cljs @@ -0,0 +1,95 @@ +(ns status-im.ui.screens.profile.visibility-status.utils + (:require [status-im.constants :as constants] + [status-im.i18n.i18n :as i18n] + [status-im.ui.screens.profile.visibility-status.styles :as styles] + [status-im.utils.handlers :refer [ + (when automatic? + [rn/view {:style (styles/visibility-status-profile-dot + colors/color-inactive size border-width 6)}]) + [rn/view {:style (styles/visibility-status-profile-dot + color size border-width margin-left)}]])) + +(defn visibility-status-button [on-press props] + (let [logged-in? (year-month-day-date [ms] (unparse (:year-month-day formatters) (to-date ms))) @@ -167,3 +172,6 @@ (str day (or (s (mod (- m 20) 10)) (s m) (s 0))))) + +(defn to-ms [sec] + (* 1000 sec)) diff --git a/src/status_im/visibility_status_popover/core.cljs b/src/status_im/visibility_status_popover/core.cljs new file mode 100644 index 0000000000..b85ca05ff8 --- /dev/null +++ b/src/status_im/visibility_status_popover/core.cljs @@ -0,0 +1,21 @@ +(ns status-im.visibility-status-popover.core + (:require [status-im.utils.fx :as fx])) + +(fx/defn show-visibility-status-popover + {:events [:show-visibility-status-popover]} + [_ value] + {:show-visibility-status-popover nil + :dispatch-later [{:ms 250 + :dispatch [:show-visibility-status-popover-db value]}] + :dismiss-keyboard nil}) + +(fx/defn show-visibility-status-popover-db + {:events [:show-visibility-status-popover-db]} + [{:keys [db]} value] + {:db (assoc db :visibility-status-popover/popover value)}) + +(fx/defn hide-visibility-status-popover + {:events [:hide-visibility-status-popover]} + [{:keys [db]}] + {:db (dissoc db :visibility-status-popover/popover) + :hide-visibility-status-popover nil}) diff --git a/src/status_im/visibility_status_updates/core.cljs b/src/status_im/visibility_status_updates/core.cljs new file mode 100644 index 0000000000..e5a3a061a3 --- /dev/null +++ b/src/status_im/visibility_status_updates/core.cljs @@ -0,0 +1,179 @@ +(ns status-im.visibility-status-updates.core + (:require [status-im.data-store.visibility-status-updates :as visibility-status-updates-store] + [status-im.ethereum.json-rpc :as json-rpc] + [status-im.utils.fx :as fx] + [status-im.constants :as constants] + [status-im.multiaccounts.update.core :as multiaccounts.update] + [status-im.utils.datetime :as datetime] + [status-im.ui.screens.profile.visibility-status.utils :as utils])) + +(defn valid-status-type? [status-type] + (some #(= status-type %) + (list constants/visibility-status-always-online + constants/visibility-status-inactive + constants/visibility-status-automatic))) + +(defn process-visibility-status-update + [acc {:keys [public-key clock] :as visibility-status-update}] + (let [{:keys [real-status-type time-left]} + (utils/calculate-real-status-type-and-time-left visibility-status-update)] + (cond-> (assoc-in acc + [:visibility-status-updates public-key] + visibility-status-update) + (= real-status-type constants/visibility-status-automatic) + (update :dispatch-later + #(conj % {:ms time-left + :dispatch [:visibility-status-updates/timeout-user-online-status + public-key clock]}))))) + +(fx/defn load-visibility-status-updates + {:events [:visibility-status-updates/visibility-status-updates-loaded]} + [{:keys [db]} visibility-status-updates-loaded] + (let [{:keys [visibility-status-updates dispatch-later]} + (reduce (fn [acc visibility-status-update-loaded] + (let [{:keys [public-key] :as visibility-status-update} + (visibility-status-updates-store/<-rpc + visibility-status-update-loaded)] + (process-visibility-status-update acc visibility-status-update))) + {} visibility-status-updates-loaded)] + (merge {:db (assoc db :visibility-status-updates visibility-status-updates)} + (when dispatch-later {:utils/dispatch-later dispatch-later})))) + +(defn handle-my-visibility-status-updates + [acc my-current-status clock visibility-status-update] + (let [status-type (:status-type visibility-status-update)] + (if (and (valid-status-type? status-type) + (or + (nil? my-current-status) + (> clock (:clock my-current-status)))) + (-> acc + (update :current-user-visibility-status + merge {:clock clock :status-type status-type}) + (assoc :dispatch [:visibility-status-updates/send-visibility-status-updates? + (not= status-type constants/visibility-status-inactive)])) + acc))) + +(defn handle-other-visibility-status-updates + [acc public-key clock visibility-status-update] + (let [status-type (:status-type visibility-status-update) + visibility-status-update-old + (get-in acc [:visibility-status-updates public-key])] + (if (and (valid-status-type? status-type) + (or + (nil? visibility-status-update-old) + (> clock (:clock visibility-status-update-old)))) + (process-visibility-status-update acc visibility-status-update) + acc))) + +(fx/defn handle-visibility-status-updates + [{:keys [db]} visibility-status-updates-received] + (let [visibility-status-updates-old (get db :visibility-status-updates {}) + my-public-key (get-in + db [:multiaccount :public-key]) + my-current-status (get-in + db [:multiaccount :current-user-visibility-status]) + {:keys [visibility-status-updates + current-user-visibility-status + dispatch dispatch-later]} + (reduce (fn [acc visibility-status-update-received] + (let [{:keys [public-key clock] :as visibility-status-update} + (visibility-status-updates-store/<-rpc + visibility-status-update-received)] + (if (= public-key my-public-key) + (handle-my-visibility-status-updates + acc my-current-status clock visibility-status-update) + (handle-other-visibility-status-updates + acc public-key clock visibility-status-update)))) + {:visibility-status-updates visibility-status-updates-old + :current-user-visibility-status my-current-status} + visibility-status-updates-received)] + (merge {:db (-> db + (update-in [:visibility-status-updates] + merge visibility-status-updates) + (update-in [:multiaccount :current-user-visibility-status] + merge current-user-visibility-status))} + (when dispatch {:dispatch dispatch}) + (when dispatch-later {:utils/dispatch-later dispatch-later})))) + +(fx/defn update-visibility-status + {:events [:visibility-status-updates/update-visibility-status]} + [{:keys [db] :as cofx} status-type] + {:db (update-in db [:multiaccount :current-user-visibility-status] + merge {:status-type status-type + :clock (datetime/timestamp-sec)}) + ::json-rpc/call [{:method "wakuext_setUserStatus" + :params [status-type ""] + :on-success #()}]}) + +(fx/defn send-visibility-status-updates? + {:events [:visibility-status-updates/send-visibility-status-updates?]} + [cofx val] + (multiaccounts.update/multiaccount-update cofx + :send-status-updates? val + {})) + +(fx/defn visibility-status-option-pressed + {:events [:visibility-status-updates/visibility-status-option-pressed]} + [{:keys [db] :as cofx} status-type] + (let [events-to-dispatch-later + (cond-> [{:ms 10 :dispatch + [:visibility-status-updates/update-visibility-status + status-type]}] + (and + (= status-type constants/visibility-status-inactive) + (> (:peers-count db) 0)) + ;; Disable broadcasting further updates + (conj {:ms 1000 + :dispatch + [:visibility-status-updates/send-visibility-status-updates? false]}))] + (fx/merge cofx + {:dispatch-later events-to-dispatch-later} + ;; Enable broadcasting for current broadcast + (send-visibility-status-updates? true)))) + +(fx/defn timeout-user-online-status + {:events [:visibility-status-updates/timeout-user-online-status]} + [{:keys [db]} public-key clock] + (let [current-clock (get-in db [:visibility-status-updates public-key :clock] 0)] + (when (= current-clock clock) + {:db (update-in db [:visibility-status-updates public-key] + merge {:status-type constants/visibility-status-inactive})}))) + +(fx/defn delayed-visibility-status-update + {:events [:visibility-status-updates/delayed-visibility-status-update]} + [{:keys [db]} status-type] + {:dispatch-later + [{:ms 200 + :dispatch + [:visibility-status-updates/visibility-status-option-pressed status-type]}]}) + +(fx/defn peers-summary-change + [{:keys [db] :as cofx} peers-count] + (let [send-visibility-status-updates? + (get-in db [:multiaccount :send-status-updates?]) + status-type + (get-in db [:multiaccount :current-user-visibility-status :status-type])] + (when (and + (> peers-count 0) + send-visibility-status-updates? + (= status-type constants/visibility-status-inactive)) + (fx/merge cofx + {:dispatch-later [{:ms 1000 :dispatch + [:visibility-status-updates/send-visibility-status-updates? false]}] + :db (assoc-in db [:multiaccount :send-status-updates?] false)} + (update-visibility-status status-type))))) + +(fx/defn sync-visibility-status-update + [{:keys [db] :as cofx} visibility-status-update-received] + (let [my-current-status (get-in db [:multiaccount :current-user-visibility-status]) + {:keys [status-type clock]} (visibility-status-updates-store/<-rpc + visibility-status-update-received)] + (when (and (valid-status-type? status-type) + (or + (nil? my-current-status) + (> clock (:clock my-current-status)))) + (fx/merge cofx + {:db (update-in db [:multiaccount :current-user-visibility-status] + merge {:clock clock :status-type status-type})} + (send-visibility-status-updates? + (not= status-type constants/visibility-status-inactive)))))) diff --git a/status-go-version.json b/status-go-version.json index 284688b568..4aa9e73c8e 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -3,7 +3,7 @@ "_comment": "Instead use: scripts/update-status-go.sh ", "owner": "status-im", "repo": "status-go", - "version": "v0.89.14", - "commit-sha1": "c20e8ebdeffd14a881d8092ee64e5180ad53449e", - "src-sha256": "0jbmgj0m1hv1nx3frbzs7lsn8nqspsir5kpzn8lldfgkfgpv96h7" + "version": "v0.89.15", + "commit-sha1": "9693d59e614899557e8b2f4f19ad541bbad3be39", + "src-sha256": "19nl5g5vhrsm510mi0nddi5n67h6z27nx6vixk72bwyvlpz22sij" } diff --git a/translations/en.json b/translations/en.json index c185989262..ad8b2bddc5 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1674,5 +1674,12 @@ "disable-later-in-settings": "You can disable this later in Settings", "use-as-profile-picture": "Use as profile picture", "view-on-opensea": "View on OpenSea", - "profile-picture-updated": "Profile picture updated" + "profile-picture-updated": "Profile picture updated", + "status-automatic": "Automatic", + "status-automatic-subtitle": "Set status automatically", + "status-dnd": "Do not disturb", + "status-dnd-subtitle": "Mutes all notifications", + "status-always-online": "Always Online", + "status-inactive": "Inactive", + "status-inactive-subtitle": "Hides your online status" }