diff --git a/src/quo/components/controls/view.cljs b/src/quo/components/controls/view.cljs index 480c9bcb23..0a3daeeb0b 100644 --- a/src/quo/components/controls/view.cljs +++ b/src/quo/components/controls/view.cljs @@ -64,8 +64,8 @@ (defn checkbox-view [props] (let [{:keys [value onChange disabled]} (bean/bean props)] (reagent/as-element - [rn/touchable-highlight - {:on-press (when onChange onChange)} + [rn/touchable-without-feedback + {:on-press (when (and onChange (not disabled)) onChange)} [rn/view {:style (styles/checkbox-style value disabled) :accessibility-label :checkbox :accessibility-role :checkbox} diff --git a/src/status_im/contact/block.cljs b/src/status_im/contact/block.cljs index 9098d9c144..075e427d03 100644 --- a/src/status_im/contact/block.cljs +++ b/src/status_im/contact/block.cljs @@ -69,4 +69,4 @@ {:db (-> db (update :contacts/blocked disj public-key) (assoc-in [:contacts/contacts public-key] contact))} - (contacts-store/save-contact contact)))) + (contacts-store/save-contact contact nil)))) diff --git a/src/status_im/contact/core.cljs b/src/status_im/contact/core.cljs index 35e3ec4a34..d814c14a3a 100644 --- a/src/status_im/contact/core.cljs +++ b/src/status_im/contact/core.cljs @@ -58,10 +58,14 @@ {:db (-> db (update-in [:contacts/contacts public-key] merge contact))} (transport.filters/load-contact contact) - #(contacts-store/save-contact % (get-in % [:db :contacts/contacts public-key])))) + (fn [cf] + (contacts-store/save-contact cf + (get-in cf [:db :contacts/contacts public-key]) + #(re-frame/dispatch [::send-contact-request public-key]))))) (fx/defn send-contact-request - [{:keys [db] :as cofx} {:keys [public-key] :as contact}] + {:events [::send-contact-request]} + [{:keys [db] :as cofx} public-key] (let [{:keys [name profile-image]} (own-info db)] {::json-rpc/call [{:method (json-rpc/call-ext-method "sendContactUpdate") :params [public-key name profile-image] @@ -85,7 +89,6 @@ :dispatch-n [[:start-profile-chat public-key] [:offload-messages constants/timeline-chat-id]]} (upsert-contact contact) - (send-contact-request contact) (mailserver/process-next-messages-request))))) (fx/defn remove-contact @@ -98,7 +101,7 @@ (fx/merge cofx {:db (assoc-in db [:contacts/contacts public-key] new-contact) :dispatch [:offload-messages constants/timeline-chat-id]} - (contacts-store/save-contact new-contact)))) + (contacts-store/save-contact new-contact nil)))) (fx/defn create-contact "Create entry in contacts" diff --git a/src/status_im/data_store/activities.cljs b/src/status_im/data_store/activities.cljs new file mode 100644 index 0000000000..23238b2bdf --- /dev/null +++ b/src/status_im/data_store/activities.cljs @@ -0,0 +1,27 @@ +(ns status-im.data-store.activities + (:require [status-im.data-store.messages :as messages] + [status-im.constants :as constants] + [status-im.ui.components.colors :as colors] + clojure.set)) + +(defn rpc->type [{:keys [type name] :as chat}] + (cond + (= 2 type) (assoc chat + :chat-type constants/private-group-chat-type + :chat-name name + :public? false + :group-chat true) + (= 1 type) (assoc chat + :chat-type constants/one-to-one-chat-type + :chat-name name + :public? false + :group-chat false))) + +(defn <-rpc [item] + (-> item + rpc->type + (clojure.set/rename-keys {:lastMessage :last-message + :chatId :chat-id}) + (assoc :color (rand-nth colors/chat-colors)) + (update :last-message #(when % (messages/<-rpc %))) + (dissoc :chatId))) diff --git a/src/status_im/data_store/contacts.cljs b/src/status_im/data_store/contacts.cljs index 7ec9d201e7..44f5a05ef4 100644 --- a/src/status_im/data_store/contacts.cljs +++ b/src/status_im/data_store/contacts.cljs @@ -40,10 +40,13 @@ :on-failure #(log/error "failed to fetch contacts" %)}]}) (fx/defn save-contact - [cofx {:keys [public-key] :as contact}] + [cofx {:keys [public-key] :as contact} on-success] {::json-rpc/call [{:method (json-rpc/call-ext-method "saveContact") :params [(->rpc contact)] - :on-success #(log/debug "saved contact" public-key "successfuly") + :on-success #(do + (log/debug "saved contact" public-key "successfuly") + (when on-success + (on-success))) :on-failure #(log/error "failed to save contact" public-key %)}]}) (fx/defn block [cofx contact on-success] diff --git a/src/status_im/ethereum/json_rpc.cljs b/src/status_im/ethereum/json_rpc.cljs index d94fe9f363..79cf8a200d 100644 --- a/src/status_im/ethereum/json_rpc.cljs +++ b/src/status_im/ethereum/json_rpc.cljs @@ -111,6 +111,7 @@ "wakuext_getPushNotificationsServers" {} "wakuext_enablePushNotificationsBlockMentions" {} "wakuext_disablePushNotificationsBlockMentions" {} + "wakuext_unreadActivityCenterNotificationsCount" {} "multiaccounts_getIdentityImages" {} "multiaccounts_getIdentityImage" {} "multiaccounts_storeIdentityImage" {} @@ -131,6 +132,12 @@ "wakuext_importCommunity" {} "wakuext_exportCommunity" {} "wakuext_ensVerified" {} + "wakuext_dismissActivityCenterNotifications" {} + "wakuext_acceptActivityCenterNotifications" {} + "wakuext_dismissAllActivityCenterNotifications" {} + "wakuext_acceptAllActivityCenterNotifications" {} + "wakuext_markAllActivityCenterNotificationsRead" {} + "wakuext_activityCenterNotifications" {} "status_chats" {} "rpcstats_getStats" {} "rpcstats_reset" {} diff --git a/src/status_im/events.cljs b/src/status_im/events.cljs index 7fe0a3b941..6eb22b438b 100644 --- a/src/status_im/events.cljs +++ b/src/status_im/events.cljs @@ -55,6 +55,7 @@ status-im.browser.core status-im.browser.permissions status-im.chat.models.transport + status-im.notifications-center.core [status-im.navigation :as navigation])) (re-frame/reg-fx diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index 7424bc60e3..a4c8622d97 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -34,7 +34,8 @@ [status-im.chat.models.link-preview :as link-preview] [status-im.utils.mobile-sync :as utils.mobile-sync] [status-im.async-storage.core :as async-storage] - [status-im.chat.models :as chat.models])) + [status-im.chat.models :as chat.models] + [status-im.notifications-center.core :as notifications-center])) (re-frame/reg-fx ::login @@ -276,7 +277,8 @@ (logging/set-log-level (:log-level multiaccount)) (multiaccounts/get-profile-picture) (multiaccounts/switch-preview-privacy-mode-flag) - (link-preview/request-link-preview-whitelist)))) + (link-preview/request-link-preview-whitelist) + (notifications-center/get-activity-center-notifications-count)))) (defn get-new-auth-method [auth-method save-password?] (when save-password? diff --git a/src/status_im/notifications_center/core.cljs b/src/status_im/notifications_center/core.cljs new file mode 100644 index 0000000000..a026045beb --- /dev/null +++ b/src/status_im/notifications_center/core.cljs @@ -0,0 +1,118 @@ +(ns status-im.notifications-center.core + (:require [status-im.utils.fx :as fx] + [status-im.ethereum.json-rpc :as json-rpc] + [taoensso.timbre :as log] + [re-frame.core :as re-frame] + [status-im.data-store.activities :as data-store.activities])) + +(fx/defn handle-activities [{:keys [db]} activities] + (if (= (:view-id db) :notifications-center) + {:db (-> db + (update-in [:activity.center/notifications :notifications] #(concat activities %))) + :dispatch [:mark-all-activity-center-notifications-as-read]} + {:db (-> db + (update :activity.center/notifications-count + (count activities)))})) + +(fx/defn get-activity-center-notifications-count + {:events [:get-activity-center-notifications-count]} + [_] + {::json-rpc/call [{:method (json-rpc/call-ext-method "unreadActivityCenterNotificationsCount") + :params [] + :on-success #(re-frame/dispatch [:get-activity-center-notifications-count-success %]) + :on-error #()}]}) + +(fx/defn get-activity-center-notifications-count-success + {:events [:get-activity-center-notifications-count-success]} + [{:keys [db]} result] + {:db (assoc db :activity.center/notifications-count result)}) + +(fx/defn dismiss-activity-center-notifications + {:events [:dismiss-activity-center-notifications]} + [{:keys [db]} ids] + {:db (update-in db [:activity.center/notifications :notifications] + (fn [items] (remove #(get ids (:id %)) items))) + ::json-rpc/call [{:method (json-rpc/call-ext-method "dismissActivityCenterNotifications") + :params [ids] + :on-success #() + :on-error #()}]}) + +(fx/defn accept-activity-center-notifications + {:events [:accept-activity-center-notifications]} + [{:keys [db]} ids] + {:db (update-in db [:activity.center/notifications :notifications] + (fn [items] (remove #(get ids (:id %)) items))) + ::json-rpc/call [{:method (json-rpc/call-ext-method "acceptActivityCenterNotifications") + :params [ids] + :js-response true + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) + :on-error #()}]}) + +(fx/defn accept-activity-center-notification-and-open-chat + {:events [:accept-activity-center-notification-and-open-chat]} + [{:keys [db]} id] + {:db (update-in db [:activity.center/notifications :notifications] + (fn [items] (remove #(= id (:id %)) items))) + ::json-rpc/call [{:method (json-rpc/call-ext-method "acceptActivityCenterNotifications") + :params [[id]] + :js-response true + :on-success #(re-frame/dispatch [:ensure-and-open-chat %]) + :on-error #()}]}) + +(fx/defn ensure-and-open-chat + {:events [:ensure-and-open-chat]} + [{:keys [db]} response-js] + {:db (update db :activity.center/notifications dissoc :cursor) + :dispatch-n [[:sanitize-messages-and-process-response response-js] + [:chat.ui/navigate-to-chat (.-id (aget (.-chats response-js) 0))]]}) + +(fx/defn dismiss-all-activity-center-notifications + {:events [:dismiss-all-activity-center-notifications]} + [{:keys [db]}] + {:db (assoc-in db [:activity.center/notifications :notifications] []) + ::json-rpc/call [{:method (json-rpc/call-ext-method "dismissAllActivityCenterNotifications") + :params [] + :on-success #() + :on-error #()}]}) + +(fx/defn accept-all-activity-center-notifications + {:events [:accept-all-activity-center-notifications]} + [{:keys [db]}] + {:db (assoc-in db [:activity.center/notifications :notifications] []) + ::json-rpc/call [{:method (json-rpc/call-ext-method "acceptAllActivityCenterNotifications") + :params [] + :js-response true + :on-success #(re-frame/dispatch [:sanitize-messages-and-process-response %]) + :on-error #()}]}) + +(fx/defn mark-all-activity-center-notifications-as-read + {:events [:mark-all-activity-center-notifications-as-read]} + [{:keys [db]}] + {:db (assoc db :activity.center/notifications-count 0) + ::json-rpc/call [{:method (json-rpc/call-ext-method "markAllActivityCenterNotificationsRead") + :params [] + :on-success #() + :on-error #()}]}) + +(fx/defn get-activity-center-notifications + {:events [:get-activity-center-notifications]} + [{:keys [db]}] + (let [{:keys [cursor]} (:activity.center/notifications db)] + (when (not= cursor "") + {::json-rpc/call [{:method (json-rpc/call-ext-method "activityCenterNotifications") + :params [cursor 20] + :on-success #(re-frame/dispatch [:activity-center-notifications-success %]) + :on-error #(log/warn "failed to get notification center activities" %)}]}))) + +(fx/defn activity-center-notifications-success + {:events [:activity-center-notifications-success]} + [{:keys [db]} {:keys [cursor notifications]}] + {:db (-> db + (assoc-in [:activity.center/notifications :cursor] cursor) + (update-in [:activity.center/notifications :notifications] + concat + (map data-store.activities/<-rpc notifications)))}) + +(fx/defn close-center + {:events [:close-notifications-center]} + [{:keys [db]}] + {:db (dissoc db :activity.center/notifications)}) diff --git a/src/status_im/subs.cljs b/src/status_im/subs.cljs index c49b3bf7e0..9192fe1e76 100644 --- a/src/status_im/subs.cljs +++ b/src/status_im/subs.cljs @@ -217,6 +217,9 @@ (reg-root-key-sub :communities/requests-to-join :communities/requests-to-join) (reg-root-key-sub :communities/community-id-input :communities/community-id-input) +(reg-root-key-sub :activity.center/notifications :activity.center/notifications) +(reg-root-key-sub :activity.center/notifications-count :activity.center/notifications-count) + (re-frame/reg-sub :communities (fn [db] diff --git a/src/status_im/transport/message/core.cljs b/src/status_im/transport/message/core.cljs index c9a071386b..757cc3fe3c 100644 --- a/src/status_im/transport/message/core.cljs +++ b/src/status_im/transport/message/core.cljs @@ -11,11 +11,13 @@ [status-im.data-store.contacts :as data-store.contacts] [status-im.data-store.chats :as data-store.chats] [status-im.data-store.invitations :as data-store.invitations] + [status-im.data-store.activities :as data-store.activities] [status-im.group-chats.core :as models.group] [status-im.utils.fx :as fx] [status-im.utils.types :as types] [status-im.constants :as constants] [status-im.multiaccounts.model :as multiaccounts.model] + [status-im.notifications-center.core :as notifications-center] [clojure.string :as string])) (fx/defn process-next @@ -26,7 +28,7 @@ (fx/defn process-response {:events [:process-response]} - [cofx ^js response-js process-async] + [{:keys [db] :as cofx} ^js response-js process-async] (let [^js communities (.-communities response-js) ^js requests-to-join-community (.-requestsToJoinCommunity response-js) ^js chats (.-chats response-js) @@ -38,6 +40,7 @@ ^js removed-filters (.-removedFilters response-js) ^js invitations (.-invitations response-js) ^js removed-chats (.-removedChats response-js) + ^js activity-notifications (.-activityCenterNotifications response-js) sync-handler (when-not process-async process-response)] (cond @@ -56,6 +59,14 @@ (seq messages) (models.message/receive-many cofx response-js) + (seq activity-notifications) + (do + (js-delete response-js "activityCenterNotifications") + (fx/merge cofx + (notifications-center/handle-activities (map data-store.activities/<-rpc + (types/js->clj activity-notifications))) + (process-next response-js sync-handler))) + (seq installations) (let [installations-clj (types/js->clj installations)] (js-delete response-js "installations") diff --git a/src/status_im/ui/components/connectivity/view.cljs b/src/status_im/ui/components/connectivity/view.cljs index 964f46ad8d..e0cc2138d6 100644 --- a/src/status_im/ui/components/connectivity/view.cljs +++ b/src/status_im/ui/components/connectivity/view.cljs @@ -164,7 +164,6 @@ (when-let [icon (get-icon state)] [quo/button {:type :icon :accessibility-label (str "conn-button-" (name icon)) - :style {:margin-right 16} :on-press #(re-frame/dispatch [:bottom-sheet/show-sheet {:content connectivity-sheet}]) :theme (if (= (:peers state) :offline) :negative :secondary)} icon]))) \ No newline at end of file diff --git a/src/status_im/ui/components/invite/events.cljs b/src/status_im/ui/components/invite/events.cljs index 7acca59061..a8bc6b3f3f 100644 --- a/src/status_im/ui/components/invite/events.cljs +++ b/src/status_im/ui/components/invite/events.cljs @@ -199,6 +199,7 @@ :attribution {:db (assoc-in db [:acquisition :accounts account :bonuses] data)})) +;;TODO this one rerender whole root many times FIX IT (re-frame/reg-sub-raw ::default-reward (fn [db] diff --git a/src/status_im/ui/screens/chat/views.cljs b/src/status_im/ui/screens/chat/views.cljs index aa679bc47b..f2aae10301 100644 --- a/src/status_im/ui/screens/chat/views.cljs +++ b/src/status_im/ui/screens/chat/views.cljs @@ -240,7 +240,6 @@ (let [loading-messages? @(re-frame/subscribe [:chats/loading-messages? chat-id]) no-messages? @(re-frame/subscribe [:chats/chat-no-messages? chat-id]) all-loaded? @(re-frame/subscribe [:chats/all-loaded? chat-id])] - (println "LIST FOOTER" (or loading-messages? (not chat-id) (not all-loaded?))) [react/view {:style (when platform/android? {:scaleY -1})} (if (or loading-messages? (not chat-id) (not all-loaded?)) [react/view {:height 324 :align-items :center :justify-content :center} diff --git a/src/status_im/ui/screens/communities/community.cljs b/src/status_im/ui/screens/communities/community.cljs index eec3e668d2..f28332f9cd 100644 --- a/src/status_im/ui/screens/communities/community.cljs +++ b/src/status_im/ui/screens/communities/community.cljs @@ -17,7 +17,9 @@ [status-im.ui.components.icons.icons :as icons] [status-im.utils.core :as utils] [status-im.ui.components.plus-button :as components.plus-button] - [status-im.utils.config :as config])) + [status-im.utils.config :as config] + [re-frame.core :as re-frame] + [status-im.ui.screens.chat.sheets :as sheets])) (def request-cooldown-ms (* 60 1000)) @@ -121,8 +123,16 @@ :color :secondary} (i18n/label :t/welcome-community-blank-message)]]) -(defn community-chat-item [home-item] - [inner-item/home-list-item home-item]) +(defn community-chat-item [{:keys [chat-id] :as home-item}] + [inner-item/home-list-item + home-item + {:on-press (fn [] + (re-frame/dispatch [:dismiss-keyboard]) + (re-frame/dispatch [:chat.ui/navigate-to-chat chat-id]) + (re-frame/dispatch [:search/home-filter-changed nil])) + :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet + {:content (fn [] + [sheets/actions home-item])}])}]) (defn community-chat-list [chats] (if (empty? chats) diff --git a/src/status_im/ui/screens/home/styles.cljs b/src/status_im/ui/screens/home/styles.cljs index e32e80c6e2..537dedef94 100644 --- a/src/status_im/ui/screens/home/styles.cljs +++ b/src/status_im/ui/screens/home/styles.cljs @@ -89,7 +89,3 @@ :background-color colors/gray :align-items :center :justify-content :center}) - -(def home-container - (merge - {:flex 1})) diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index cf147c2183..4347679af6 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -25,7 +25,9 @@ [status-im.multiaccounts.login.core :as multiaccounts.login] [status-im.ui.components.invite.views :as invite] [status-im.ui.components.topbar :as topbar] - [status-im.ui.components.plus-button :as components.plus-button]) + [status-im.ui.components.plus-button :as components.plus-button] + [status-im.ui.components.tabbar.styles :as tabs.styles] + [status-im.ui.screens.chat.sheets :as sheets]) (:require-macros [status-im.utils.views :as views])) (defn welcome-image-wrapper [] @@ -148,7 +150,15 @@ (defn render-fn [{:keys [chat-id] :as home-item}] ;; We use `chat-id` to distinguish communities from chats (if chat-id - [inner-item/home-list-item home-item] + [inner-item/home-list-item + home-item + {:on-press (fn [] + (re-frame/dispatch [:dismiss-keyboard]) + (re-frame/dispatch [:chat.ui/navigate-to-chat chat-id]) + (re-frame/dispatch [:search/home-filter-changed nil])) + :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet + {:content (fn [] + [sheets/actions home-item])}])}] [communities.views/community-home-list-item home-item])) (defn chat-list-key-fn [item] @@ -195,10 +205,30 @@ :loading logging-in? :accessibility-label :new-chat-button}])) +(views/defview notifications-button [] + (views/letsubs [notif-count [:activity.center/notifications-count]] + [react/view + [quo/button {:type :icon + :style {:margin-left 10} + :accessibility-label "notifications-button" + :on-press #(do + (re-frame/dispatch [:get-activity-center-notifications]) + (re-frame/dispatch [:mark-all-activity-center-notifications-as-read]) + (re-frame/dispatch [:navigate-to :notifications-center])) + :theme :icon} + :main-icons/notification] + (when (pos? notif-count) + [react/view {:style (merge (tabs.styles/counter-public-container) {:top 5 :right 5}) + :pointer-events :none} + [react/view {:style tabs.styles/counter-public + :accessibility-label :notifications-unread-badge}]])])) + (defn home [] - [react/keyboard-avoiding-view {:style styles/home-container} + [react/keyboard-avoiding-view {:style {:flex 1}} [topbar/topbar {:title (i18n/label :t/chat) :navigation :none - :right-component [connectivity/connectivity-button]}] + :right-component [react/view {:flex-direction :row :margin-right 16} + [connectivity/connectivity-button] + [notifications-button]]}] [chats-list] [plus-button]]) diff --git a/src/status_im/ui/screens/home/views/inner_item.cljs b/src/status_im/ui/screens/home/views/inner_item.cljs index 2e83dd8009..6a54ab13c8 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -8,7 +8,6 @@ [status-im.ui.components.chat-icon.screen :as chat-icon.screen] [quo.core :as quo] [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.sheets :as sheets] [status-im.ui.screens.home.styles :as styles] [status-im.ui.components.icons.icons :as icons] [status-im.utils.contenthash :as contenthash] @@ -137,55 +136,49 @@ :height 15 :margin-right 2}}) -(defn home-list-item [home-item] +(defn home-list-item [home-item opts] (let [{:keys [chat-id chat-name color online group-chat public? timestamp last-message muted]} home-item private-group? (and group-chat (not public?)) public-group? (and group-chat public?)] [quo/list-item - {:icon [chat-icon.screen/chat-icon-view-chat-list - chat-id group-chat chat-name color online false] - :title [react/view {:flex-direction :row - :flex 1} - [react/view {:flex-direction :row - :flex 1 - :padding-right 16 - :align-items :center} - (cond - muted - [icons/icon :main-icons/tiny-muted (assoc (icon-style) :color colors/gray)] - private-group? - [icons/icon :main-icons/tiny-group (icon-style)] - public-group? - [icons/icon :main-icons/tiny-public (icon-style)] - :else - [icons/icon :main-icons/tiny-new-contact (icon-style)]) - [quo/text {:weight :medium - :color (when muted :secondary) - :accessibility-label :chat-name-text - :ellipsize-mode :tail - :number-of-lines 1} - (if group-chat - (utils/truncate-str chat-name 30) - ;; This looks a bit odd, but I would like only to subscribe - ;; if it's a one-to-one. If wrapped in a component styling - ;; won't be applied correctly. - (first @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])))]] - [message-timestamp (if (pos? (:whisper-timestamp last-message)) - (:whisper-timestamp last-message) - timestamp)]] - :title-accessibility-label :chat-name-text - :subtitle [react/view {:flex-direction :row} - [react/view {:flex 1} - [message-content-text (select-keys last-message [:content - :content-type - :community-id])]] - [unviewed-indicator home-item]] - :on-press (fn [] - (re-frame/dispatch [:dismiss-keyboard]) - (re-frame/dispatch [:chat.ui/navigate-to-chat chat-id]) - (re-frame/dispatch [:search/home-filter-changed nil])) - :on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet - {:content (fn [] - [sheets/actions home-item])}])}])) + (merge {:icon [chat-icon.screen/chat-icon-view-chat-list + chat-id group-chat chat-name color online false] + :title [react/view {:flex-direction :row + :flex 1} + [react/view {:flex-direction :row + :flex 1 + :padding-right 16 + :align-items :center} + (cond + muted + [icons/icon :main-icons/tiny-muted (assoc (icon-style) :color colors/gray)] + private-group? + [icons/icon :main-icons/tiny-group (icon-style)] + public-group? + [icons/icon :main-icons/tiny-public (icon-style)] + :else + [icons/icon :main-icons/tiny-new-contact (icon-style)]) + [quo/text {:weight :medium + :color (when muted :secondary) + :accessibility-label :chat-name-text + :ellipsize-mode :tail + :number-of-lines 1} + (if group-chat + (utils/truncate-str chat-name 30) + ;; This looks a bit odd, but I would like only to subscribe + ;; if it's a one-to-one. If wrapped in a component styling + ;; won't be applied correctly. + (first @(re-frame/subscribe [:contacts/contact-two-names-by-identity chat-id])))]] + [message-timestamp (if (pos? (:whisper-timestamp last-message)) + (:whisper-timestamp last-message) + timestamp)]] + :title-accessibility-label :chat-name-text + :subtitle [react/view {:flex-direction :row} + [react/view {:flex 1} + [message-content-text (select-keys last-message [:content + :content-type + :community-id])]] + [unviewed-indicator home-item]]} + opts)])) diff --git a/src/status_im/ui/screens/notifications_center/views.cljs b/src/status_im/ui/screens/notifications_center/views.cljs new file mode 100644 index 0000000000..9d8b0487ac --- /dev/null +++ b/src/status_im/ui/screens/notifications_center/views.cljs @@ -0,0 +1,100 @@ +(ns status-im.ui.screens.notifications-center.views + (:require [status-im.ui.components.react :as react] + [status-im.ui.components.topbar :as topbar] + [status-im.i18n.i18n :as i18n] + [status-im.ui.components.list.views :as list] + [status-im.ui.screens.home.views.inner-item :as inner-item] + [re-frame.core :as re-frame] + [quo.core :as quo] + [status-im.ui.components.colors :as colors] + [reagent.core :as reagent] + [status-im.ui.components.toolbar :as toolbar] + [clojure.string :as string])) + +(def selecting (reagent/atom nil)) +(def select-all (reagent/atom nil)) +(def selected-items (reagent/atom #{})) + +(defn render-fn [{:keys [id] :as home-item}] + (when id + (let [selected (get @selected-items id) + on-change (fn [] + (swap! selected-items #(if selected (disj % id) (conj % id))))] + [react/view {:flex-direction :row :flex 1 :align-items :center} + (when @selecting + [react/view {:padding-left 16} + [quo/checkbox {:value (or @select-all selected) + :disabled @select-all + :on-change on-change}]]) + [react/view {:flex 1} + [inner-item/home-list-item + home-item + {:on-press (fn [] + (if @selecting + (on-change) + (re-frame/dispatch [:accept-activity-center-notification-and-open-chat id]))) + :on-long-press #(do (reset! selecting true) + (swap! selected-items conj id))}]]]))) + +(defn filter-item [] + [react/view {:padding-vertical 8 :border-bottom-width 1 :border-bottom-color colors/gray-lighter} + [react/view {:align-items :center :justify-content :space-between :padding-horizontal 16 :flex-direction :row} + (if @selecting + [react/view {:flex-direction :row} + [quo/checkbox {:value @select-all + :on-change #(do + (reset! selected-items #{}) + (swap! select-all not))}] + [react/text {:style {:color colors/gray :margin-left 20}} + (str (if @select-all (i18n/label :t/all) (count @selected-items)) + " " (string/lower-case (i18n/label :t/selected)))]] + [quo/button {:type :secondary + :on-press #(reset! selecting true)} + (i18n/label :t/select)]) + (when @selecting + [quo/button {:type :secondary + :on-press #(do (reset! selecting false) + (reset! select-all false) + (reset! selected-items #{}))} + (i18n/label :t/cancel)])]]) + +(defn reset-state [] + (reset! selecting nil) + (reset! select-all nil) + (reset! selected-items #{})) + +(defn toolbar-action [accept] + (if accept + (if @select-all + (re-frame/dispatch [:accept-all-activity-center-notifications]) + (re-frame/dispatch [:accept-activity-center-notifications @selected-items])) + (if @select-all + (re-frame/dispatch [:dismiss-all-activity-center-notifications]) + (re-frame/dispatch [:dismiss-activity-center-notifications @selected-items]))) + (reset-state)) + +(defn center [] + (let [{:keys [notifications]} @(re-frame/subscribe [:activity.center/notifications])] + [react/keyboard-avoiding-view {:style {:flex 1}} + [topbar/topbar {:navigation {:on-press #(do + (reset-state) + (re-frame/dispatch [:close-notifications-center]) + (re-frame/dispatch [:navigate-back]))} + :title (i18n/label :t/activity)}] + [filter-item] + [list/flat-list + {:key-fn #(or (:chat-id %) (:id %)) + :on-end-reached #(re-frame/dispatch [:get-activity-center-notifications]) + :keyboard-should-persist-taps :always + :data notifications + :render-fn render-fn}] + (when (or @select-all (> (count @selected-items) 0)) + [toolbar/toolbar + {:show-border? true + :left [quo/button {:type :secondary + :theme :negative + :on-press #(toolbar-action false)} + (i18n/label :t/reject-and-delete)] + :right [quo/button {:type :secondary + :on-press #(toolbar-action true)} + (i18n/label :t/accept-and-add)]}])])) \ No newline at end of file diff --git a/src/status_im/ui/screens/privacy_and_security_settings/messages_from_contacts_only.cljs b/src/status_im/ui/screens/privacy_and_security_settings/messages_from_contacts_only.cljs index 7f8fb17e72..6767b77ec5 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/messages_from_contacts_only.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/messages_from_contacts_only.cljs @@ -16,18 +16,18 @@ (views/defview messages-from-contacts-only [] (views/letsubs [{:keys [messages-from-contacts-only]} [:multiaccount]] - [react/view {:flex 1} - [topbar/topbar {:title (i18n/label :t/accept-new-chats-from)}] - [react/view {:margin-top 8} - [quo/list-item - {:active (not messages-from-contacts-only) - :accessory :radio - :title (i18n/label :t/anyone) - :on-press #(re-frame/dispatch [::messages-from-contacts-only-switched false])}] - [quo/list-item - {:active messages-from-contacts-only - :accessory :radio - :title (i18n/label :t/contacts) - :subtitle (i18n/label :t/messages-from-contacts-only-subtitle) - :subtitle-max-lines 4 - :on-press #(re-frame/dispatch [::messages-from-contacts-only-switched true])}]]])) + [react/view {:flex 1} + [topbar/topbar {:title (i18n/label :t/accept-new-chats-from)}] + [react/view {:margin-top 8} + [quo/list-item + {:active (not messages-from-contacts-only) + :accessory :radio + :title (i18n/label :t/anyone) + :on-press #(re-frame/dispatch [::messages-from-contacts-only-switched false])}] + [quo/list-item + {:active messages-from-contacts-only + :accessory :radio + :title (i18n/label :t/contacts) + :subtitle (i18n/label :t/messages-from-contacts-only-subtitle) + :subtitle-max-lines 4 + :on-press #(re-frame/dispatch [::messages-from-contacts-only-switched true])}]]])) diff --git a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs index e96e95455d..7fba3bf636 100644 --- a/src/status_im/ui/screens/privacy_and_security_settings/views.cljs +++ b/src/status_im/ui/screens/privacy_and_security_settings/views.cljs @@ -20,27 +20,27 @@ webview-allow-permission-requests?]} [:multiaccount] supported-biometric-auth [:supported-biometric-auth] auth-method [:auth-method]] - [react/view {:flex 1 :background-color colors/white} - [topbar/topbar {:title (i18n/label :t/privacy-and-security)}] - [react/scroll-view {:padding-vertical 8} - [quo/list-header (i18n/label :t/security)] - [quo/list-item {:size :small - :title (i18n/label :t/back-up-seed-phrase) - :accessibility-label :back-up-recovery-phrase-button - :disabled (not mnemonic) - :chevron (boolean mnemonic) - :accessory (when mnemonic [components.common/counter {:size 22} 1]) - :on-press #(re-frame/dispatch [:navigate-to :backup-seed])}] - (when supported-biometric-auth - [quo/list-item - {:size :small - :title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth)) - :active (= auth-method "biometric") - :accessibility-label :biometric-auth-settings-switch - :accessory :switch - :on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched - ((complement boolean) (= auth-method "biometric"))])}]) - [separator] + [react/view {:flex 1 :background-color colors/white} + [topbar/topbar {:title (i18n/label :t/privacy-and-security)}] + [react/scroll-view {:padding-vertical 8} + [quo/list-header (i18n/label :t/security)] + [quo/list-item {:size :small + :title (i18n/label :t/back-up-seed-phrase) + :accessibility-label :back-up-recovery-phrase-button + :disabled (not mnemonic) + :chevron (boolean mnemonic) + :accessory (when mnemonic [components.common/counter {:size 22} 1]) + :on-press #(re-frame/dispatch [:navigate-to :backup-seed])}] + (when supported-biometric-auth + [quo/list-item + {:size :small + :title (str (i18n/label :t/lock-app-with) " " (biometric/get-label supported-biometric-auth)) + :active (= auth-method "biometric") + :accessibility-label :biometric-auth-settings-switch + :accessory :switch + :on-press #(re-frame/dispatch [:multiaccounts.ui/biometric-auth-switched + ((complement boolean) (= auth-method "biometric"))])}]) + [separator] ;; TODO - uncomment when implemented ;; {:size :small ;; :title (i18n/label :t/change-password) @@ -49,51 +49,51 @@ ;; :title (i18n/label :t/change-passcode) ;; :chevron true} - [quo/list-header (i18n/label :t/privacy)] - [quo/list-item {:size :small - :title (i18n/label :t/set-dapp-access-permissions) - :on-press #(re-frame/dispatch [:navigate-to :dapps-permissions]) - :accessibility-label :dapps-permissions-button - :chevron true}] - [quo/list-item {:size :small - :title (if platform/android? - (i18n/label :t/hide-content-when-switching-apps) - (i18n/label :t/hide-content-when-switching-apps-ios)) - :container-margin-bottom 8 - :active preview-privacy? - :accessory :switch - :on-press #(re-frame/dispatch - [:multiaccounts.ui/preview-privacy-mode-switched - ((complement boolean) preview-privacy?)])}] - [quo/list-item {:size :small - :title (i18n/label :t/chat-link-previews) - :chevron true - :on-press #(re-frame/dispatch [:navigate-to :link-previews-settings]) - :accessibility-label :chat-link-previews}] - [quo/list-item {:size :small - :title (i18n/label :t/accept-new-chats-from) - :chevron true - :accessory :text - :accessory-text (i18n/label (if messages-from-contacts-only - :t/contacts - :t/anyone)) - :on-press #(re-frame/dispatch [:navigate-to :messages-from-contacts-only]) - :accessibility-label :accept-new-chats-from}] - (when platform/android? - [quo/list-item {:size :small - :title (i18n/label :t/webview-camera-permission-requests) - :active webview-allow-permission-requests? - :accessory :switch - :subtitle (i18n/label :t/webview-camera-permission-requests-subtitle) - :subtitle-max-lines 2 - :on-press #(re-frame/dispatch - [:multiaccounts.ui/webview-permission-requests-switched - ((complement boolean) webview-allow-permission-requests?)])}]) - [separator] - [quo/list-item - {:size :small - :theme :negative - :title (i18n/label :t/delete-my-profile) - :on-press #(re-frame/dispatch [:navigate-to :delete-profile]) - :accessibility-label :dapps-permissions-button - :chevron true}]]])) + [quo/list-header (i18n/label :t/privacy)] + [quo/list-item {:size :small + :title (i18n/label :t/set-dapp-access-permissions) + :on-press #(re-frame/dispatch [:navigate-to :dapps-permissions]) + :accessibility-label :dapps-permissions-button + :chevron true}] + [quo/list-item {:size :small + :title (if platform/android? + (i18n/label :t/hide-content-when-switching-apps) + (i18n/label :t/hide-content-when-switching-apps-ios)) + :container-margin-bottom 8 + :active preview-privacy? + :accessory :switch + :on-press #(re-frame/dispatch + [:multiaccounts.ui/preview-privacy-mode-switched + ((complement boolean) preview-privacy?)])}] + [quo/list-item {:size :small + :title (i18n/label :t/chat-link-previews) + :chevron true + :on-press #(re-frame/dispatch [:navigate-to :link-previews-settings]) + :accessibility-label :chat-link-previews}] + [quo/list-item {:size :small + :title (i18n/label :t/accept-new-chats-from) + :chevron true + :accessory :text + :accessory-text (i18n/label (if messages-from-contacts-only + :t/contacts + :t/anyone)) + :on-press #(re-frame/dispatch [:navigate-to :messages-from-contacts-only]) + :accessibility-label :accept-new-chats-from}] + (when platform/android? + [quo/list-item {:size :small + :title (i18n/label :t/webview-camera-permission-requests) + :active webview-allow-permission-requests? + :accessory :switch + :subtitle (i18n/label :t/webview-camera-permission-requests-subtitle) + :subtitle-max-lines 2 + :on-press #(re-frame/dispatch + [:multiaccounts.ui/webview-permission-requests-switched + ((complement boolean) webview-allow-permission-requests?)])}]) + [separator] + [quo/list-item + {:size :small + :theme :negative + :title (i18n/label :t/delete-my-profile) + :on-press #(re-frame/dispatch [:navigate-to :delete-profile]) + :accessibility-label :dapps-permissions-button + :chevron true}]]])) diff --git a/src/status_im/ui/screens/routing/chat_stack.cljs b/src/status_im/ui/screens/routing/chat_stack.cljs index 184bc3e3f2..baacf09b6d 100644 --- a/src/status_im/ui/screens/routing/chat_stack.cljs +++ b/src/status_im/ui/screens/routing/chat_stack.cljs @@ -17,6 +17,7 @@ [status-im.ui.screens.profile.group-chat.views :as profile.group-chat] [status-im.ui.components.tabbar.styles :as tabbar.styles] [status-im.ui.screens.stickers.views :as stickers] + [status-im.ui.screens.notifications-center.views :as notifications-center] [status-im.utils.config :as config])) (defonce stack (navigation/create-stack)) @@ -42,6 +43,8 @@ :component stickers/packs} {:name :stickers-pack :component stickers/pack} + {:name :notifications-center + :component notifications-center/center} ;; Community {:name :community :component community/community} diff --git a/status-go-version.json b/status-go-version.json index b41d3ac616..ec992a2196 100644 --- a/status-go-version.json +++ b/status-go-version.json @@ -2,7 +2,7 @@ "_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh ' instead", "owner": "status-im", "repo": "status-go", - "version": "feature/notification-center-2", - "commit-sha1": "6322d0c1ed9cee5f00eba53ad60daa47fc388944", - "src-sha256": "1ljcds0qfx2n2fm4in5ja12dfbkr0xi5y1h30hkzh1wr104d03gb" + "version": "v0.76.0", + "commit-sha1": "c739f73f497b2cc1f22be0a176683193766d5193", + "src-sha256": "1db355sqsaj9g2hdzs9db29azrf8s1mzp0x62a6sny9gyiwwl5vd" } diff --git a/translations/en.json b/translations/en.json index aea37929df..ab95c161af 100644 --- a/translations/en.json +++ b/translations/en.json @@ -1102,7 +1102,6 @@ "sharing-share": "Share", "show-less": "Show less", "show-more": "Show more", - "show-notifications": "Show notifications", "show-qr": "Show QR code", "show-transaction-data": "Show transaction data", "sign-and-send": "Sign and send", @@ -1509,5 +1508,8 @@ "rpc-usage-filter": "Filter methods", "rpc-usage-copy": "Copy", "community-message-preview": "Invitation to join {{community-name}}", - "non-contacts": "Non contacts" + "non-contacts": "Non contacts", + "activity": "Activity", + "reject-and-delete": "Reject and delete", + "accept-and-add": "Accept and add" }