diff --git a/resources/icons/delete.svg b/resources/icons/delete.svg new file mode 100644 index 0000000000..d8a33d565b --- /dev/null +++ b/resources/icons/delete.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/status_im/data_store/browser.cljs b/src/status_im/data_store/browser.cljs index fa16f8e664..a6288eeeea 100644 --- a/src/status_im/data_store/browser.cljs +++ b/src/status_im/data_store/browser.cljs @@ -16,4 +16,8 @@ (defn save [{:keys [browser-id] :as browser}] - (data-store/save browser (exists? browser-id))) \ No newline at end of file + (data-store/save browser (exists? browser-id))) + +(defn delete + [browser-id] + (data-store/delete (exists? browser-id))) \ No newline at end of file diff --git a/src/status_im/data_store/realm/browser.cljs b/src/status_im/data_store/realm/browser.cljs index aa92397093..3d4e0e534b 100644 --- a/src/status_im/data_store/realm/browser.cljs +++ b/src/status_im/data_store/realm/browser.cljs @@ -14,6 +14,11 @@ [browser update?] (realm/save @realm/account-realm :browser browser update?)) +(defn delete + [browser-id] + (when-let [browser (realm/get-one-by-field-clj @realm/account-realm :browser :browser-id browser-id)] + (realm/delete @realm/account-realm browser))) + (defn exists? [browser-id] (realm/exists? @realm/account-realm :browser {:browser-id browser-id})) diff --git a/src/status_im/ui/components/animation.cljs b/src/status_im/ui/components/animation.cljs index eabc7d2ae2..270e7e21f1 100644 --- a/src/status_im/ui/components/animation.cljs +++ b/src/status_im/ui/components/animation.cljs @@ -35,9 +35,6 @@ (defn stop-animation [anim-value] (.stopAnimation anim-value)) -(defn value [anim-value] - (.-value anim-value)) - (defn set-value [anim-value value] (.setValue anim-value value)) diff --git a/src/status_im/ui/components/colors.cljs b/src/status_im/ui/components/colors.cljs index 4d536b38ce..e153fc67e1 100644 --- a/src/status_im/ui/components/colors.cljs +++ b/src/status_im/ui/components/colors.cljs @@ -11,4 +11,5 @@ (def gray-lighter "#eef2f5") ;; Used as a background or shadow (def blue "#4360df") ;; Used as main wallet color (def red "#ff2d55") ;; Used to highlight errors or "dangerous" actions +(def light-red "#ffe6e6") ;; Used as a background for delete icon (def text-light-gray "#212121") ;; Used for labels (home items) diff --git a/src/status_im/ui/components/icons/vector_icons.cljs b/src/status_im/ui/components/icons/vector_icons.cljs index 9f22eb890e..d18c75a164 100644 --- a/src/status_im/ui/components/icons/vector_icons.cljs +++ b/src/status_im/ui/components/icons/vector_icons.cljs @@ -47,6 +47,7 @@ :icons/browse (slurp/slurp-svg "./resources/icons/browse.svg") :icons/close (slurp/slurp-svg "./resources/icons/close.svg") :icons/copy-from (slurp/slurp-svg "./resources/icons/copy_from.svg") + :icons/delete (slurp/slurp-svg "./resources/icons/delete.svg") :icons/dots-horizontal (slurp/slurp-svg "./resources/icons/dots_horizontal.svg") :icons/dots-vertical (slurp/slurp-svg "./resources/icons/dots_vertical.svg") :icons/exclamation_mark (slurp/slurp-svg "./resources/icons/exclamation_mark.svg") diff --git a/src/status_im/ui/screens/browser/events.cljs b/src/status_im/ui/screens/browser/events.cljs index 9239870fd9..3a8a8ed901 100644 --- a/src/status_im/ui/screens/browser/events.cljs +++ b/src/status_im/ui/screens/browser/events.cljs @@ -25,6 +25,11 @@ (fn [browser] (browser-store/save browser))) +(re-frame/reg-fx + :remove-browser + (fn [browser-id] + (browser-store/delete browser-id))) + (defn match-url [url] (str (when (and url (not (re-find #"^[a-zA-Z-_]+:/" url))) "http://") url)) @@ -77,3 +82,10 @@ [re-frame/trim-v] (fn [{:keys [db now] :as cofx} [options]] {:db (update db :browser/options merge options)})) + +(handlers/register-handler-fx + :remove-browser + [re-frame/trim-v] + (fn [{:keys [db]} [browser-id]] + {:db (update-in db [:browser/browsers] dissoc browser-id) + :remove-browser browser-id})) diff --git a/src/status_im/ui/screens/events.cljs b/src/status_im/ui/screens/events.cljs index 5c7acce225..761f74bb7d 100644 --- a/src/status_im/ui/screens/events.cljs +++ b/src/status_im/ui/screens/events.cljs @@ -404,3 +404,10 @@ :request-permissions (fn [_ [_ permissions then else]] {::request-permissions-fx [permissions then else]})) + +(handlers/register-handler-db + :set-swipe-position + [re-frame/trim-v] + (fn [db [item-id value]] + (-> db + (assoc-in [:chat-animations item-id :delete-swiped] value)))) diff --git a/src/status_im/ui/screens/home/animations/responder.cljs b/src/status_im/ui/screens/home/animations/responder.cljs new file mode 100644 index 0000000000..da606eb548 --- /dev/null +++ b/src/status_im/ui/screens/home/animations/responder.cljs @@ -0,0 +1,35 @@ +(ns status-im.ui.screens.home.animations.responder + (:require [status-im.ui.components.react :as react] + [re-frame.core :as re-frame] + [status-im.ui.components.animation :as animation])) + +(defn get-updated-value [gesture end-offset-x swiped?] + (let [base-value (if swiped? end-offset-x 0)] + (- base-value (.-dx gesture)))) + +(defn on-start [_ gesture] + (> (js/Math.abs (.-dx gesture)) 10)) + +(defn on-move [animated-offset-x end-offset-x swiped?] + (fn [_ gesture] + (let [to-value (get-updated-value gesture end-offset-x swiped?)] + (animation/start (animation/spring animated-offset-x {:toValue to-value}))))) + +(defn on-release [animated-offset-x end-offset-x chat-id swiped?] + (fn [_ gesture] + (let [updated-value (get-updated-value gesture end-offset-x swiped?) + should-open? (> updated-value (/ end-offset-x 2)) + to-value (if should-open? end-offset-x 0)] + (re-frame/dispatch [:set-swipe-position chat-id should-open?]) + (animation/start (animation/spring animated-offset-x {:toValue to-value}))))) + +(defn swipe-pan-responder [animated-offset-x end-offset-x chat-id swiped?] + (.create react/pan-responder + (clj->js {:onMoveShouldSetPanResponder on-start + :onPanResponderMove (on-move animated-offset-x end-offset-x swiped?) + :onPanResponderRelease (on-release animated-offset-x end-offset-x chat-id swiped?) + :onPanResponderTerminate (on-release animated-offset-x end-offset-x chat-id swiped?)}))) + + +(defn pan-handlers [pan-responder] + (js->clj (.-panHandlers pan-responder))) diff --git a/src/status_im/ui/screens/home/styles.cljs b/src/status_im/ui/screens/home/styles.cljs index 071264c28f..907adb24c6 100644 --- a/src/status_im/ui/screens/home/styles.cljs +++ b/src/status_im/ui/screens/home/styles.cljs @@ -3,6 +3,8 @@ (:require [status-im.ui.components.styles :as component.styles] [status-im.ui.components.colors :as colors])) +(def delete-button-width 100) + (defn toolbar [] {:background-color colors/white}) @@ -155,3 +157,17 @@ :width 14 :height 9 :tint-color :white}) + +(def delete-icon-highlight + {:position :absolute + :top 0 + :bottom 0 + :right -800 + :width 800 + :background-color colors/light-red}) + +(def delete-icon-container + {:flex 1 + :width delete-button-width + :justify-content :center + :align-items :center}) diff --git a/src/status_im/ui/screens/home/views.cljs b/src/status_im/ui/screens/home/views.cljs index 3f706e0257..c037689286 100644 --- a/src/status_im/ui/screens/home/views.cljs +++ b/src/status_im/ui/screens/home/views.cljs @@ -12,6 +12,9 @@ [status-im.ui.components.sync-state.offline :refer [offline-view]] [status-im.ui.screens.home.views.inner-item :as inner-item] [status-im.ui.screens.home.styles :as styles] + [status-im.ui.components.icons.vector-icons :as vector-icons] + [status-im.ui.components.animation :as animation] + [status-im.ui.screens.home.animations.responder :as responder] [status-im.utils.platform :as platform])) (defn- toolbar [] @@ -29,14 +32,24 @@ :accessibility-label :plus-button :on-press #(re-frame/dispatch [:navigate-to :new])}]) -(defn- home-list-item [[home-item-id home-item]] - (if (:chat-id home-item) - [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-chat home-item-id])} - [react/view - [inner-item/home-list-chat-item-inner-view home-item]]] - [react/touchable-highlight {:on-press #(re-frame/dispatch [:open-browser home-item])} - [react/view - [inner-item/home-list-browser-item-inner-view home-item]]])) +(defn- home-list-deletable [[home-item-id home-item]] + (views/letsubs [swiped? [:delete-swipe-position home-item-id]] + (let [delete-action (if (:chat-id home-item) :remove-chat :remove-browser) + inner-view (if (:chat-id home-item) + inner-item/home-list-chat-item-inner-view + inner-item/home-list-browser-item-inner-view) + offset-x (animation/create-value (if swiped? styles/delete-button-width 0)) + swipe-pan-responder (responder/swipe-pan-responder offset-x styles/delete-button-width home-item-id swiped?) + swipe-pan-handler (responder/pan-handlers swipe-pan-responder)] + [react/view swipe-pan-handler + [react/animated-view {:style {:right offset-x}} + [inner-view home-item] + [react/touchable-highlight {:style styles/delete-icon-highlight + :on-press #(do + (re-frame/dispatch [:set-swipe-position home-item-id false]) + (re-frame/dispatch [delete-action home-item-id]))} + [react/view {:style styles/delete-icon-container} + [vector-icons/icon :icons/delete {:color colors/red}]]]]]))) (views/defview home [] (views/letsubs [home-items [:home-items]] @@ -44,7 +57,7 @@ [toolbar] [list/flat-list {:data home-items :render-fn (fn [[home-item-id :as home-item]] - ^{:key home-item-id} [home-list-item home-item])}] + ^{:key home-item-id} [home-list-deletable home-item])}] (when platform/android? [home-action-button]) [offline-view]])) 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 46c91767b5..3b1ff2a144 100644 --- a/src/status_im/ui/screens/home/views/inner_item.cljs +++ b/src/status_im/ui/screens/home/views/inner_item.cljs @@ -1,8 +1,8 @@ (ns status-im.ui.screens.home.views.inner-item (:require-macros [status-im.utils.views :refer [defview letsubs]]) - (:require [clojure.string :as str] + (:require [re-frame.core :as re-frame] + [clojure.string :as str] [status-im.ui.components.react :as react] - [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.ui.screens.home.styles :as styles] [status-im.ui.components.styles :as component.styles] [status-im.utils.core :as utils] @@ -11,6 +11,7 @@ [status-im.utils.datetime :as time] [status-im.utils.gfycat.core :as gfycat] [status-im.constants :as const] + [status-im.ui.components.icons.vector-icons :as vector-icons] [status-im.ui.components.chat-icon.screen :as chat-icon.screen])) (defn message-content-text [{:keys [content] :as message}] @@ -87,38 +88,40 @@ public-key unremovable? :as chat]}] (letsubs [last-message [:get-last-message chat-id]] (let [name (or (i18n/get-contact-translated chat-id :name name) - (gfycat/generate-gfy public-key))] - [react/view styles/chat-container - [react/view styles/chat-icon-container - [chat-icon.screen/chat-icon-view-chat-list chat-id group-chat name color online]] - [react/view styles/chat-info-container - [react/view styles/item-upper-container - [chat-list-item-name name group-chat public? public-key] - (when last-message - [react/view styles/message-status-container - [message-status chat last-message] - [message-timestamp last-message]])] - [react/view styles/item-lower-container - [message-content-text last-message] - [unviewed-indicator chat-id]]]]))) + (gfycat/generate-gfy public-key))] + [react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to-chat chat-id])} + [react/view styles/chat-container + [react/view styles/chat-icon-container + [chat-icon.screen/chat-icon-view-chat-list chat-id group-chat name color online]] + [react/view styles/chat-info-container + [react/view styles/item-upper-container + [chat-list-item-name name group-chat public? public-key] + (when last-message + [react/view styles/message-status-container + [message-status chat last-message] + [message-timestamp last-message]])] + [react/view styles/item-lower-container + [message-content-text last-message] + [unviewed-indicator chat-id]]]]]))) (defview home-list-browser-item-inner-view [{:keys [browser-id name url dapp? contact] :as browser}] (letsubs [contact' [:contact-by-identity contact]] - [react/view styles/chat-container - [react/view styles/chat-icon-container - (if contact' - [chat-icon.screen/dapp-icon-browser contact' 36] - [react/view styles/browser-icon-container - [vector-icons/icon :icons/discover {:color component.styles/color-light-gray6}]])] - [react/view styles/chat-info-container - [react/view styles/item-upper-container - [react/view styles/name-view - [react/view {:flex-shrink 1} - [react/text {:style styles/name-text + [react/touchable-highlight {:on-press #(re-frame/dispatch [:open-browser browser])} + [react/view styles/chat-container + [react/view styles/chat-icon-container + (if contact' + [chat-icon.screen/dapp-icon-browser contact' 36] + [react/view styles/browser-icon-container + [vector-icons/icon :icons/discover {:color component.styles/color-light-gray6}]])] + [react/view styles/chat-info-container + [react/view styles/item-upper-container + [react/view styles/name-view + [react/view {:flex-shrink 1} + [react/text {:style styles/name-text + :number-of-lines 1} + name]]]] + [react/view styles/item-lower-container + [react/view styles/last-message-container + [react/text {:style styles/last-message-text :number-of-lines 1} - name]]]] - [react/view styles/item-lower-container - [react/view styles/last-message-container - [react/text {:style styles/last-message-text - :number-of-lines 1} - (or url (i18n/label :t/dapp))]]]]])) + (or url (i18n/label :t/dapp))]]]]]])) diff --git a/src/status_im/ui/screens/subs.cljs b/src/status_im/ui/screens/subs.cljs index 2155326e90..c19e62792f 100644 --- a/src/status_im/ui/screens/subs.cljs +++ b/src/status_im/ui/screens/subs.cljs @@ -55,3 +55,8 @@ (reg-sub :get-screen-params (fn [db [_ view-id]] (get-in db [:navigation/screen-params (or view-id (:view-id db))]))) + +(reg-sub :delete-swipe-position + (fn [db [_ item-id]] + (let [item-animation (get-in db [:chat-animations item-id])] + (if (some? item-animation) (:delete-swiped item-animation) nil))))