Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] MVP Images #9455

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion src/status_im/chat/models.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
[status-im.utils.gfycat.core :as gfycat]
[status-im.utils.platform :as platform]
[status-im.utils.utils :as utils]
[taoensso.timbre :as log]))
[taoensso.timbre :as log]
[status-im.utils.image-processing :as image-processing]
[status-im.ipfs.core :as ipfs]))

(defn- get-chat [cofx chat-id]
(get-in cofx [:db :chats chat-id]))
Expand Down Expand Up @@ -305,3 +307,31 @@
(fx/merge (assoc-in cofx [:db :contacts/identity] identity)
(contact.core/create-contact identity)
(navigation/navigate-to-cofx :profile nil)))

(re-frame/reg-fx
:chat-open-image-picker
(fn []
(react/show-image-picker
(fn [image]
(image-processing/resize
(aget image "path")
400 400
(fn [resized-image]
(re-frame/dispatch [:chat.ui/set-chat-ui-props {:send-image (aget resized-image "path")}]))
#()))
"photo")))

(fx/defn chat-open-image-picker
{:events [:chat.ui/open-image-picker]}
[cofx]
{:chat-open-image-picker nil})

(fx/defn send-image
{:events [:chat.ui/send-image]}
[{:keys [db] :as cofx} send-image]
(fx/merge cofx
{:db (set-chat-ui-props db {:send-image-loading? true})}
(ipfs/add {:value #js {:uri send-image :name "image"}
:opts {:headers {"Content-Type" "multipart/form-data"}}
:on-failure #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:send-image-loading? false}])
:on-success #(re-frame/dispatch [:chat-image-added-to-ipfs %])})))
15 changes: 15 additions & 0 deletions src/status_im/chat/models/input.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@
:pack pack}
:text "Update to latest version to see a nice sticker here!"})))

(fx/defn send-image
[{:keys [db] :as cofx} hash]
(when-not (string/blank? hash)
(chat.message/send-message cofx {:chat-id (:current-chat-id db)
:content-type constants/content-type-image
:content {:chat-id (:current-chat-id db)
:hash hash}})))

(fx/defn send-current-message
"Sends message from current chat input"
[{{:keys [current-chat-id] :as db} :db :as cofx}]
Expand All @@ -127,6 +135,13 @@
;;see https://github.com/status-im/team-core/blob/6c3d67d8e8bd8500abe52dab06a59e976ec942d2/rfc-001.md#status-gostatus-react-interface
)

(fx/defn chat-image-added-to-ipfs
{:events [:chat-image-added-to-ipfs]}
[{:keys [db] :as cofx} {:keys [hash]}]
(fx/merge cofx
{:db (chat/set-chat-ui-props db {:send-image-loading? false :show-image? nil :send-image nil})}
(send-image hash)))

;; effects

(re-frame/reg-fx
Expand Down
1 change: 1 addition & 0 deletions src/status_im/constants.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
(def ^:const content-type-emoji 4)
(def ^:const content-type-command 5)
(def ^:const content-type-system-text 6)
(def ^:const content-type-image 7)

(def ^:const message-type-one-to-one 1)
(def ^:const message-type-public-group 2)
Expand Down
24 changes: 12 additions & 12 deletions src/status_im/ipfs/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,18 @@

(fx/defn add
"Add `value` on ipfs, and returns its b58 encoded CID"
[cofx {:keys [value on-success on-failure]}]
[cofx {:keys [value on-success on-failure opts timeout-ms]}]
(let [formdata (doto (js/FormData.)
;; the key is ignored so there is no need to provide one
(.append "file" value))]
{:http-raw-post (cond-> {:url ipfs-add-url
:body formdata
:timeout-ms 5000
:success-event-creator
(fn [{:keys [status body]}]
(if (= 200 status)
(on-success (parse-ipfs-add-response body))
(when on-failure
(on-failure status))))}
on-failure
(assoc :failure-event-creator on-failure))}))
{:http-raw-post {:url ipfs-add-url
:body formdata
:opts opts
:timeout-ms (or 25000 timeout-ms)
:on-failure on-failure
:on-success
(fn [{:keys [status body]}]
(if (= 200 status)
(on-success (parse-ipfs-add-response body))
(when on-failure
(on-failure status))))}}))
8 changes: 7 additions & 1 deletion src/status_im/transport/db.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@

(spec/def ::content-type #{constants/content-type-text
constants/content-type-emoji
constants/content-type-sticker})
constants/content-type-sticker
constants/content-type-image})
(spec/def ::message-type #{constants/message-type-private-group constants/message-type-public-group constants/message-type-one-to-one})
(spec/def ::clock-value (spec/and pos-int?
utils.clocks/safe-timestamp?))
Expand Down Expand Up @@ -92,13 +93,18 @@
:req-opt [:message.content/response-to]))

(spec/def :message.sticker/content (spec/keys :req-un [:message.content/hash]))
(spec/def :message.image/content (spec/keys :req-un [:message.content/hash]))

(defmulti content-type :content-type)

(defmethod content-type constants/content-type-sticker [_]
(spec/merge :message/message-common
(spec/keys :req-un [:message.sticker/content])))

(defmethod content-type constants/content-type-image [_]
(spec/merge :message/message-common
(spec/keys :req-un [:message.image/content])))

(defmethod content-type :default [_]
(spec/merge :message/message-common
(spec/keys :req-un [:message.text/content])))
Expand Down
3 changes: 3 additions & 0 deletions src/status_im/ui/screens/chat/image/styles.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(ns status-im.ui.screens.chat.image.styles)

(def image-panel-height 263)
84 changes: 84 additions & 0 deletions src/status_im/ui/screens/chat/image/views.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
(ns status-im.ui.screens.chat.image.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.components.react :as react]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.utils.platform :as platform]
[re-frame.core :as re-frame]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.animation :as anim]
[status-im.ui.screens.chat.image.styles :as styles]
[status-im.utils.utils :as utils]
[status-im.i18n :as i18n]
[status-im.ui.components.icons.vector-icons :as icons]))

(defn button [images-showing?]
[react/touchable-highlight
{:on-press
(fn [_]
(re-frame/dispatch [:chat.ui/set-chat-ui-props
{:input-bottom-sheet (when-not images-showing? :images)}])
(when-not platform/desktop? (js/setTimeout #(react/dismiss-keyboard!) 100)))
:accessibility-label :show-photo-icon}
[vector-icons/icon
:main-icons/photo
{:container-style {:margin 14 :margin-right 6}
:color (if images-showing? colors/blue colors/gray)}]])

(defn show-panel-anim
[bottom-anim-value alpha-value]
(anim/start
(anim/parallel
[(anim/spring bottom-anim-value {:toValue 0
:useNativeDriver true})
(anim/timing alpha-value {:toValue 1
:duration 500
:useNativeDriver true})])))

(defn select-button [title icon on-press]
[react/touchable-highlight {:on-press on-press :style {:flex 1}}
[react/view {:background-color colors/black :max-height 223 :flex 1 :border-radius 16
:align-items :center :justify-content :center}
[react/view {:height 48 :width 48 :align-items :center :justify-content :center :border-radius 24
:border-width 2 :border-color colors/gray}
[icons/icon icon {:color :white}]]
[react/text {:style {:margin-top 9 :typography :caption :color colors/gray}} title]]])

(defn take-picture []
(re-frame/dispatch [:request-permissions
{:permissions [:camera]
:on-allowed #(re-frame/dispatch [:navigate-to :profile-photo-capture])
:on-denied (fn []
(utils/set-timeout
#(utils/show-popup (i18n/label :t/error)
(i18n/label :t/camera-access-error))
50))}]))

(defn round-button [on-press cancel? loading?]
[react/touchable-highlight {:on-press (when-not loading? on-press)}
[react/view {:width 32 :height 32 :border-radius 16 :background-color (if cancel? colors/gray colors/blue)
:align-items :center :justify-content :center}
(if loading?
[react/activity-indicator {:color :white
:animating true}]
[icons/icon (if cancel? :main-icons/close :main-icons/arrow-up) {:color :white}])]])

(defview image-view []
(letsubs [send-image [:chats/current-chat-ui-prop :send-image]
loading? [:chats/current-chat-ui-prop :send-image-loading?]
bottom-anim-value (anim/create-value styles/image-panel-height)
alpha-value (anim/create-value 0)]
{:component-did-mount #(show-panel-anim bottom-anim-value alpha-value)}
[react/animated-view {:style {:background-color :white
:height styles/image-panel-height
:transform [{:translateY bottom-anim-value}]
:opacity alpha-value}}
(if send-image
[react/view {:align-items :center :flex-direction :row :flex 1 :justify-content :space-between
:padding-horizontal 20}
[round-button #(re-frame/dispatch [:chat.ui/set-chat-ui-props {:send-image nil}]) true false]
[react/image {:source {:uri send-image} :style {:width 150 :height 150 :border-radius 8}}]
[round-button #(re-frame/dispatch [:chat.ui/send-image send-image]) false loading?]]
[react/view {:flex-direction :row :padding-horizontal 16 :padding-top 12 :flex 1}
[select-button "Take a picture" :main-icons/camera take-picture]
[react/view {:width 16}]
[select-button "Choose photo" :main-icons/photo #(re-frame/dispatch [:chat.ui/open-image-picker])]])]))
3 changes: 3 additions & 0 deletions src/status_im/ui/screens/chat/input/input.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.utils.platform :as platform]
[status-im.utils.config :as config]
[status-im.ui.screens.chat.image.views :as image]
[status-im.ui.screens.chat.stickers.views :as stickers]
[status-im.ui.screens.chat.extensions.views :as extensions]))

Expand Down Expand Up @@ -156,6 +157,8 @@
[reply-message-view]
[react/view {:style style/input-container}
[input-view {:single-line-input? single-line-input? :set-text set-text :state-text state-text}]
(when input-text-empty?
[image/button (= :images input-bottom-sheet)])
(when (and input-text-empty? mainnet?)
[stickers/button (= :stickers input-bottom-sheet)])
(when (and one-to-one-chat? input-text-empty? (or config/commands-enabled? mainnet?))
Expand Down
7 changes: 5 additions & 2 deletions src/status_im/ui/screens/chat/message/message.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -289,5 +289,8 @@
[react/image {:style {:margin 10 :width 140 :height 140}
;;TODO (perf) move to event
:source {:uri (contenthash/url (-> content :sticker :hash))}}]
[message-bubble-wrapper message
[react/text (str "Unhandled content-type " content-type)]]))))]])))
(if (= content-type constants/content-type-image)
[react/image {:style {:margin-vertical 10 :width 140 :height 140 :border-radius 8}
:source {:uri (str "https://ipfs.infura.io/ipfs/" (:hash content))}}]
[message-bubble-wrapper message
[react/text (str "Unhandled content-type " content-type)]])))))]])))
3 changes: 3 additions & 0 deletions src/status_im/ui/screens/chat/views.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
[status-im.ui.screens.chat.stickers.views :as stickers]
[status-im.ui.screens.chat.styles.main :as style]
[status-im.ui.screens.chat.toolbar-content :as toolbar-content]
[status-im.ui.screens.chat.image.views :as image]
[status-im.ui.screens.chat.state :as state]
[status-im.utils.debounce :as debounce]
[status-im.ui.screens.chat.extensions.views :as extensions]
Expand Down Expand Up @@ -143,6 +144,8 @@
[stickers/stickers-view]
:extensions
[extensions/extensions-view]
:images
[image/image-view]
nil)))

(defview chat []
Expand Down
6 changes: 2 additions & 4 deletions src/status_im/ui/screens/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,8 @@
:http-post
http-post)

(defn- http-raw-post [{:keys [url body response-validator success-event-creator failure-event-creator timeout-ms opts]}]
(let [on-success #(re-frame/dispatch (success-event-creator %))
on-error (when failure-event-creator #(re-frame/dispatch (failure-event-creator %)))
all-opts (assoc opts
(defn- http-raw-post [{:keys [url body response-validator on-success on-error timeout-ms opts]}]
(let [all-opts (assoc opts
:valid-response? response-validator
:timeout-ms timeout-ms)]
(http/raw-post url body on-success on-error all-opts)))
Expand Down
10 changes: 5 additions & 5 deletions src/status_im/utils/http.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
;; Default HTTP request timeout ms
(def http-request-default-timeout-ms 3000)

(defn- headers [response]
(defn- response-headers [response]
(let [entries (es6-iterator-seq (.entries (.-headers response)))]
(reduce #(assoc %1 (string/trim (string/lower-case (first %2))) (string/trim (second %2))) {} entries)))

Expand All @@ -17,19 +17,19 @@
([url body on-success] (raw-post url body on-success nil))
([url body on-success on-error]
(raw-post url body on-success on-error nil))
([url body on-success on-error {:keys [timeout-ms]}]
([url body on-success on-error {:keys [timeout-ms headers]}]
(-> (rn-dependencies/fetch
url
(clj->js {:method "POST"
:headers {"Cache-Control" "no-cache"}
:headers (merge {"Cache-Control" "no-cache"} headers)
:body body
:timeout (or timeout-ms http-request-default-timeout-ms)}))
(.then (fn [response]
(->
(.text response)
(.then (fn [body]
(on-success {:status (.-status response)
:headers (headers response)
:headers (response-headers response)
:body body}))))))
(.catch (or on-error
(fn [error]
Expand Down Expand Up @@ -92,7 +92,7 @@
(.text response)
(.then (fn [body]
(on-success {:status (.-status response)
:headers (headers response)
:headers (response-headers response)
:body body}))))))
(.catch (or on-error
(fn [error]
Expand Down
2 changes: 1 addition & 1 deletion src/status_im/utils/image_processing.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[status-im.utils.fs :as fs]
[status-im.react-native.js-dependencies :as rn-dependencies]))

(defn- resize [path max-width max-height on-resize on-error]
(defn resize [path max-width max-height on-resize on-error]
(let [resize-fn (-> rn-dependencies/image-resizer
(object/get "default")
(object/get "createResizedImage"))]
Expand Down