Skip to content

Commit faa29a2

Browse files
authored
feat: reimplement composer (#15639)
* feat: reimplement composer (1)
1 parent 5bf58bb commit faa29a2

35 files changed

+1284
-26
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
# Xcode
99
#
10+
/ios/.xcode.env.local
1011
/component-spec
1112
result/
1213
build/

Diff for: resources/images/icons2/20x20/[email protected]

-19 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

34 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

91 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

167 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

103 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

22 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

69 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

-75 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

-48 Bytes
Loading

Diff for: resources/images/icons2/20x20/[email protected]

-25 Bytes
Loading

Diff for: src/quo2/foundations/colors.cljs

+2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
(def neutral-90-opa-0 (alpha neutral-90 0))
7777

7878
;;95 with transparency
79+
(def neutral-95-opa-0 (alpha neutral-95 0))
7980
(def neutral-95-opa-60 (alpha neutral-95 0.6))
8081
(def neutral-95-opa-70 (alpha neutral-95 0.7))
8182
(def neutral-95-opa-80 (alpha neutral-95 0.8))
@@ -84,6 +85,7 @@
8485

8586
;;100 with transparency
8687
(def neutral-100-opa-0 (alpha neutral-100 0))
88+
(def neutral-100-opa-5 (alpha neutral-100 0.05))
8789
(def neutral-100-opa-10 (alpha neutral-100 0.1))
8890
(def neutral-100-opa-30 (alpha neutral-100 0.3))
8991
(def neutral-100-opa-60 (alpha neutral-100 0.6))

Diff for: src/react_native/reanimated.cljs

+9
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,15 @@
171171
(with-decay (clj->js {:velocity velocity
172172
:clamp clamp}))))
173173

174+
(defn animate
175+
([animation value]
176+
(animate animation value default-duration))
177+
([animation value duration]
178+
(set-shared-value animation
179+
(with-timing value
180+
(clj->js {:duration duration
181+
:easing (default-easing)})))))
182+
174183
(defn with-timing-duration
175184
[val duration]
176185
(with-timing val

Diff for: src/status_im/chat/models/input.cljs

+18
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@
3939
(let [current-chat-id (or chat-id (:current-chat-id db))]
4040
{:db (assoc-in db [:chat/inputs current-chat-id :input-text] (text->emoji new-input))}))
4141

42+
(rf/defn set-input-content-height
43+
{:events [:chat.ui/set-input-content-height]}
44+
[{db :db} content-height chat-id]
45+
(let [current-chat-id (or chat-id (:current-chat-id db))]
46+
{:db (assoc-in db [:chat/inputs current-chat-id :input-content-height] content-height)}))
47+
48+
(rf/defn set-input-maximized
49+
{:events [:chat.ui/set-input-maximized]}
50+
[{db :db} maximized? chat-id]
51+
(let [current-chat-id (or chat-id (:current-chat-id db))]
52+
{:db (assoc-in db [:chat/inputs current-chat-id :input-maximized?] maximized?)}))
53+
54+
(rf/defn set-input-refocus
55+
{:events [:chat.ui/set-input-refocus]}
56+
[{db :db} refocus? chat-id]
57+
(let [current-chat-id (or chat-id (:current-chat-id db))]
58+
{:db (assoc-in db [:chat/inputs current-chat-id :input-refocus?] refocus?)}))
59+
4260
(rf/defn select-mention
4361
{:events [:chat.ui/select-mention]}
4462
[{:keys [db] :as cofx} text-input-ref {:keys [primary-name searched-text match public-key] :as user}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(ns status-im2.contexts.chat.bottom-sheet-composer.actions.style
2+
(:require
3+
[quo2.foundations.colors :as colors]
4+
[react-native.reanimated :as reanimated]
5+
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]))
6+
7+
(def actions-container
8+
{:height constants/actions-container-height
9+
:justify-content :space-between
10+
:align-items :center
11+
:z-index 2
12+
:flex-direction :row})
13+
14+
(defn send-button
15+
[opacity z-index]
16+
(reanimated/apply-animations-to-style
17+
{:opacity opacity}
18+
{:position :absolute
19+
:right 0
20+
:z-index z-index
21+
:background-color (colors/theme-colors colors/white colors/neutral-95)}))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
(ns status-im2.contexts.chat.bottom-sheet-composer.actions.view
2+
(:require
3+
[quo2.core :as quo]
4+
[react-native.core :as rn]
5+
[react-native.permissions :as permissions]
6+
[react-native.platform :as platform]
7+
[react-native.reanimated :as reanimated]
8+
[reagent.core :as reagent]
9+
[status-im2.common.alert.events :as alert]
10+
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
11+
[status-im2.contexts.chat.messages.list.view :as messages.list]
12+
[utils.i18n :as i18n]
13+
[utils.re-frame :as rf]
14+
[status-im2.contexts.chat.bottom-sheet-composer.actions.style :as style]))
15+
16+
(defn send-message
17+
[{:keys [input-ref]}
18+
{:keys [text-value focused? maximized?]}
19+
{:keys [height saved-height last-height opacity background-y container-opacity]}
20+
window-height]
21+
(reanimated/animate height constants/input-height)
22+
(reanimated/set-shared-value saved-height constants/input-height)
23+
(reanimated/set-shared-value last-height constants/input-height)
24+
(reanimated/animate opacity 0)
25+
(when-not @focused?
26+
(js/setTimeout #(reanimated/animate container-opacity constants/empty-opacity) 300))
27+
(js/setTimeout #(reanimated/set-shared-value background-y
28+
(- window-height))
29+
300)
30+
(rf/dispatch [:chat.ui/send-current-message])
31+
(rf/dispatch [:chat.ui/set-input-maximized false])
32+
(rf/dispatch [:chat.ui/set-input-content-height constants/input-height])
33+
(rf/dispatch [:chat.ui/set-chat-input-text nil])
34+
(reset! maximized? false)
35+
(reset! text-value "")
36+
(when @input-ref
37+
(.clear ^js @input-ref))
38+
(messages.list/scroll-to-bottom))
39+
40+
(defn send-button
41+
[props
42+
{:keys [text-value] :as state}
43+
animations
44+
window-height
45+
images?]
46+
[:f>
47+
(fn []
48+
(let [btn-opacity (reanimated/use-shared-value 0)
49+
z-index (reagent/atom 0)]
50+
[:f>
51+
(fn []
52+
(rn/use-effect (fn []
53+
(if (or (not-empty @text-value) images?)
54+
(when-not (= @z-index 1)
55+
(reset! z-index 1)
56+
(js/setTimeout #(reanimated/animate btn-opacity 1) 50))
57+
(when-not (= @z-index 0)
58+
(reanimated/animate btn-opacity 0)
59+
(js/setTimeout #(reset! z-index 0) 300))))
60+
[(and (empty? @text-value) (not images?))])
61+
[reanimated/view
62+
{:style (style/send-button btn-opacity @z-index)}
63+
[quo/button
64+
{:icon true
65+
:size 32
66+
:accessibility-label :send-message-button
67+
:on-press #(send-message props state animations window-height)}
68+
:i/arrow-up]])]))])
69+
70+
(defn audio-button
71+
[]
72+
[quo/button
73+
{:on-press #(js/alert "to be added")
74+
:icon true
75+
:type :outline
76+
:size 32}
77+
:i/audio])
78+
79+
(defn camera-button
80+
[]
81+
[quo/button
82+
{:on-press #(js/alert "to be implemented")
83+
:icon true
84+
:type :outline
85+
:size 32
86+
:style {:margin-right 12}}
87+
:i/camera])
88+
89+
(defn open-photo-selector
90+
[{:keys [input-ref]}
91+
{:keys [focused?]}
92+
{:keys [height]}
93+
insets]
94+
(permissions/request-permissions
95+
{:permissions [:read-external-storage :write-external-storage]
96+
:on-allowed (fn []
97+
(when platform/android?
98+
(when @focused?
99+
(rf/dispatch [:chat.ui/set-input-refocus true]))
100+
(when @input-ref
101+
(.blur ^js @input-ref)))
102+
(rf/dispatch [:chat.ui/set-input-content-height
103+
(reanimated/get-shared-value height)])
104+
(rf/dispatch [:open-modal :photo-selector {:insets insets}]))
105+
:on-denied (fn []
106+
(alert/show-popup (i18n/label :t/error)
107+
(i18n/label
108+
:t/external-storage-denied)))}))
109+
110+
(defn image-button
111+
[props state animations insets]
112+
[quo/button
113+
{:on-press #(open-photo-selector props state animations insets)
114+
:icon true
115+
:type :outline
116+
:size 32
117+
:style {:margin-right 12}}
118+
:i/image])
119+
120+
(defn reaction-button
121+
[]
122+
[quo/button
123+
{:on-press #(js/alert "to be implemented")
124+
:icon true
125+
:type :outline
126+
:size 32
127+
:style {:margin-right 12}}
128+
:i/reaction])
129+
130+
(defn format-button
131+
[]
132+
[quo/button
133+
{:on-press #(js/alert "to be implemented")
134+
:icon true
135+
:type :outline
136+
:size 32}
137+
:i/format])
138+
139+
(defn view
140+
[props state animations window-height insets images?]
141+
[rn/view {:style style/actions-container}
142+
[rn/view {:style {:flex-direction :row}}
143+
[camera-button]
144+
[image-button props state animations insets]
145+
[reaction-button]
146+
[format-button]]
147+
[send-button props state animations window-height images?]
148+
[audio-button]])
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
(ns status-im2.contexts.chat.bottom-sheet-composer.constants
2+
(:require
3+
[quo2.foundations.typography :as typography]
4+
[react-native.platform :as platform]))
5+
6+
(def ^:const bar-container-height 20)
7+
8+
(def ^:const input-height (if platform/ios? 32 42))
9+
10+
(def ^:const actions-container-height 56)
11+
12+
(def ^:const composer-default-height (+ bar-container-height input-height actions-container-height))
13+
14+
(def ^:const multiline-minimized-height (+ input-height 18))
15+
16+
(def ^:const empty-opacity 0.7)
17+
18+
(def ^:const images-container-height 76)
19+
20+
(def ^:const extra-content-offset (if platform/ios? 6 0))
21+
22+
(def ^:const content-change-threshold 10)
23+
24+
(def ^:const drag-threshold 30)
25+
26+
(def ^:const velocity-threshold -1000)
27+
28+
(def ^:const background-threshold 0.75)
29+
30+
(def ^:const line-height (:line-height typography/paragraph-1))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
(ns status-im2.contexts.chat.bottom-sheet-composer.effects
2+
(:require
3+
[status-im.async-storage.core :as async-storage]
4+
[react-native.core :as rn]
5+
[react-native.reanimated :as reanimated]
6+
[status-im2.contexts.chat.bottom-sheet-composer.constants :as constants]
7+
[status-im2.contexts.chat.bottom-sheet-composer.keyboard :as kb]
8+
[utils.re-frame :as rf]
9+
[utils.number :as utils.number]))
10+
11+
(defn reenter-screen-effect
12+
[{:keys [text-value saved-cursor-position maximized?]}
13+
{:keys [content-height]}
14+
{:keys [input-content-height input-text input-maximized?]}]
15+
(when (and (empty? @text-value) (not= input-text nil))
16+
(reset! text-value input-text)
17+
(reset! content-height input-content-height)
18+
(reset! saved-cursor-position (count input-text)))
19+
(when input-maximized?
20+
(reset! maximized? true)))
21+
22+
(defn maximized-effect
23+
[{:keys [maximized?]}
24+
{:keys [height saved-height last-height]}
25+
{:keys [max-height]}
26+
{:keys [input-content-height]}]
27+
(when (or @maximized? (>= input-content-height max-height))
28+
(reanimated/animate height max-height)
29+
(reanimated/set-shared-value saved-height max-height)
30+
(reanimated/set-shared-value last-height max-height)))
31+
32+
(defn refocus-effect
33+
[{:keys [input-ref]}
34+
{:keys [input-refocus?]}]
35+
(when (and input-refocus? @input-ref)
36+
(.focus ^js @input-ref)
37+
(rf/dispatch [:chat.ui/set-input-refocus false])))
38+
39+
(defn layout-effect
40+
[{:keys [lock-layout?]}]
41+
(when-not @lock-layout?
42+
(js/setTimeout #(reset! lock-layout? true) 500)))
43+
44+
(defn kb-default-height-effect
45+
[{:keys [kb-default-height]}]
46+
(when-not @kb-default-height
47+
(async-storage/get-item :kb-default-height
48+
#(reset! kb-default-height (utils.number/parse-int % nil)))))
49+
50+
(defn background-effect
51+
[{:keys [maximized?]}
52+
{:keys [opacity background-y]}
53+
{:keys [max-height]}
54+
{:keys [input-content-height]}]
55+
(when (or @maximized? (>= input-content-height (* max-height constants/background-threshold)))
56+
(reanimated/set-shared-value background-y 0)
57+
(reanimated/animate opacity 1)))
58+
59+
(defn images-effect
60+
[{:keys [container-opacity]}
61+
images?]
62+
(when images?
63+
(reanimated/animate container-opacity 1)))
64+
65+
(defn empty-effect
66+
[{:keys [text-value maximized? focused?]}
67+
{:keys [container-opacity]}
68+
images?]
69+
(when (and (empty? @text-value) (not images?) (not @maximized?) (not @focused?))
70+
(reanimated/animate-delay container-opacity constants/empty-opacity 200)))
71+
72+
(defn component-will-unmount
73+
[{:keys [keyboard-show-listener keyboard-hide-listener keyboard-frame-listener]}]
74+
(.remove ^js @keyboard-show-listener)
75+
(.remove ^js @keyboard-hide-listener)
76+
(.remove ^js @keyboard-frame-listener))
77+
78+
(defn initialize
79+
[props state animations {:keys [max-height] :as dimensions} chat-input keyboard-height images?]
80+
(rn/use-effect
81+
(fn []
82+
(maximized-effect state animations dimensions chat-input)
83+
(refocus-effect props chat-input)
84+
(reenter-screen-effect state dimensions chat-input)
85+
(layout-effect state)
86+
(kb-default-height-effect state)
87+
(background-effect state animations dimensions chat-input)
88+
(images-effect animations images?)
89+
(empty-effect state animations images?)
90+
(kb/add-kb-listeners props state animations dimensions keyboard-height)
91+
#(component-will-unmount props))
92+
[max-height]))

0 commit comments

Comments
 (0)