Implement Emoji Picker (#17195)
This commit adds Emoji Picker in the app for usage in Message Composer and Wallet Account. --------- Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
After Width: | Height: | Size: 984 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 939 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 350 B |
After Width: | Height: | Size: 467 B |
After Width: | Height: | Size: 819 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 622 B |
After Width: | Height: | Size: 745 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 848 B |
After Width: | Height: | Size: 1.1 KiB |
|
@ -420,6 +420,8 @@
|
|||
"react-native-transparent-video" react-native-transparent-video
|
||||
"react-native-orientation-locker" react-native-orientation-locker
|
||||
"react-native-gifted-charts" react-native-gifted-charts
|
||||
"../resources/data/emojis/en.json" (js/JSON.parse (slurp
|
||||
"./resources/data/emojis/en.json"))
|
||||
"../src/js/worklets/core.js" worklet-factory
|
||||
"../src/js/worklets/shell/bottom_tabs.js" #js {}
|
||||
"../src/js/worklets/shell/home_stack.js" #js {}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
(ns quo2.components.dividers.divider-label.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn get-height
|
||||
[tight?]
|
||||
(if tight? 34 42))
|
||||
|
||||
(defn- get-border-color
|
||||
[blur? theme]
|
||||
(colors/theme-colors (if blur? colors/neutral-80-opa-5 colors/neutral-10)
|
||||
|
@ -17,6 +21,7 @@
|
|||
[blur? tight? chevron theme]
|
||||
{:border-top-width 1
|
||||
:border-top-color (get-border-color blur? theme)
|
||||
:height (get-height tight?)
|
||||
:padding-top (if tight? 6 14)
|
||||
:padding-bottom 7
|
||||
:padding-left (if (= :left chevron) 16 20)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
(ns quo2.components.profile.showcase-nav.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def height 56)
|
||||
|
||||
(def root-container
|
||||
{:height 56})
|
||||
{:height height})
|
||||
|
||||
(defn container
|
||||
[state theme]
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
[react-native.core :as rn]))
|
||||
|
||||
(defn- render-button
|
||||
[{:keys [icon id]} _ _ {:keys [state on-press active-id]}]
|
||||
[{:keys [icon id]} index _ {:keys [state on-press active-id]}]
|
||||
(let [active? (= id active-id)
|
||||
button-type (if active? :grey :ghost)
|
||||
scroll-state? (= state :scroll)]
|
||||
|
@ -17,21 +17,21 @@
|
|||
:icon-only? true
|
||||
:on-press (fn []
|
||||
(when on-press
|
||||
(on-press id)))
|
||||
(on-press id index)))
|
||||
:container-style style/button-container}
|
||||
icon]))
|
||||
|
||||
(defn- view-internal
|
||||
[{:keys [theme container-style default-active state data on-press active-id]}]
|
||||
[rn/view
|
||||
{:style style/root-container
|
||||
{:style (merge style/root-container container-style)
|
||||
:accessibility-label :showcase-nav}
|
||||
[rn/flat-list
|
||||
{:data data
|
||||
:key-fn str
|
||||
:key-fn :id
|
||||
:horizontal true
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:content-container-style (merge (style/container state theme) container-style)
|
||||
:content-container-style (style/container state theme)
|
||||
:render-fn render-button
|
||||
:render-data {:state state
|
||||
:on-press on-press
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
|
||||
(defn- wrap-render-fn
|
||||
[f render-data]
|
||||
(fn [data]
|
||||
(reagent/as-element [f (.-item ^js data) (.-index ^js data)
|
||||
(.-separators ^js data) render-data
|
||||
(.-isActive ^js data) (.-drag ^js data)])))
|
||||
(fn [^js data]
|
||||
(reagent/as-element [f (.-item data) (.-index data)
|
||||
(.-separators data) render-data
|
||||
(.-isActive data) (.-drag data)])))
|
||||
|
||||
(defn- wrap-on-drag-end-fn
|
||||
[f]
|
||||
(fn [data]
|
||||
(f (.-from ^js data) (.-to ^js data) (.-data ^js data))))
|
||||
(fn [^js data]
|
||||
(f (.-from data) (.-to data) (.-data data))))
|
||||
|
||||
(defn- wrap-key-fn
|
||||
[f]
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
(ns status-im2.contexts.emoji-picker.constants
|
||||
(:require [react-native.core :as rn]))
|
||||
|
||||
(def ^:const default-category :people)
|
||||
|
||||
(def ^:const search-debounce-ms 600)
|
||||
|
||||
(def ^:const emojis-per-row 7)
|
||||
|
||||
(def ^:const emoji-size 32)
|
||||
|
||||
(def ^:const emoji-row-padding-horizontal 20)
|
||||
|
||||
(def ^:const emoji-section-header-margin-bottom 6)
|
||||
|
||||
(def ^:const emoji-row-separator-height 16)
|
||||
|
||||
(def ^:const emoji-item-margin-right
|
||||
(/ (- (:width (rn/get-window)) (* emoji-row-padding-horizontal 2) (* emoji-size emojis-per-row))
|
||||
(- emojis-per-row 1)))
|
||||
|
||||
(def ^:const item-height (+ emoji-size emoji-row-separator-height))
|
|
@ -0,0 +1,103 @@
|
|||
(ns status-im2.contexts.emoji-picker.data
|
||||
(:require [status-im2.contexts.emoji-picker.constants :as constants]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
;; Emoji data is pulled from the `emojibase` (https://emojibase.dev).
|
||||
;;
|
||||
;; The dataset constains `group` key which holds a number to reduce the file size.
|
||||
;; It is then categorized below:
|
||||
;; - smileys-emotion (0)
|
||||
;; - people-body (1)
|
||||
;; - animals-nature (3)
|
||||
;; - food-drink (4)
|
||||
;; - activity (6)
|
||||
;; - travel-places (5)
|
||||
;; - objects (7)
|
||||
;; - symbols (8)
|
||||
;; - flags (9)
|
||||
;;
|
||||
;; NOTE:
|
||||
;; - smileys & emoticons (0) and people (1) are grouped in one section in the app
|
||||
;; - The emoji components (https://symbl.cc/en/emoji/component) which are group 2 are not used
|
||||
;; in the app and are removed from the dataset.
|
||||
|
||||
(def ^:const emoji-data (transforms/js->clj (js/require "../resources/data/emojis/en.json")))
|
||||
|
||||
(def ^:const group-smileys-emotion 0)
|
||||
(def ^:const group-people-body 1)
|
||||
(def ^:const group-animals-nature 3)
|
||||
(def ^:const group-food-drink 4)
|
||||
(def ^:const group-travel-places 5)
|
||||
(def ^:const group-activity 6)
|
||||
(def ^:const group-objects 7)
|
||||
(def ^:const group-symbols 8)
|
||||
(def ^:const group-flags 9)
|
||||
|
||||
(def ^:const categories
|
||||
[{:title :t/emoji-people ;; 0 and 1
|
||||
:icon :i/faces
|
||||
:id :people
|
||||
:data []}
|
||||
{:title :t/emoji-nature ;; 3
|
||||
:icon :i/nature
|
||||
:id :nature
|
||||
:data []}
|
||||
{:title :t/emoji-food ;; 4
|
||||
:icon :i/food
|
||||
:id :food
|
||||
:data []}
|
||||
{:title :t/emoji-activity ;; 6
|
||||
:icon :i/activity
|
||||
:id :activity
|
||||
:data []}
|
||||
{:title :t/emoji-travel ;; 5
|
||||
:icon :i/travel
|
||||
:id :travel
|
||||
:data []}
|
||||
{:title :t/emoji-objects ;; 7
|
||||
:icon :i/objects
|
||||
:id :objects
|
||||
:data []}
|
||||
{:title :t/emoji-symbols ;; 8
|
||||
:icon :i/symbols
|
||||
:id :symbols
|
||||
:data []}
|
||||
{:title :t/emoji-flags ;; 9
|
||||
:icon :i/flags
|
||||
:id :flags
|
||||
:data []}])
|
||||
|
||||
(defn emoji-group->category
|
||||
[group]
|
||||
(condp = group
|
||||
group-smileys-emotion {:index 0 :id :people}
|
||||
group-people-body {:index 0 :id :people}
|
||||
group-animals-nature {:index 1 :id :nature}
|
||||
group-food-drink {:index 2 :id :food}
|
||||
group-travel-places {:index 4 :id :travel}
|
||||
group-activity {:index 3 :id :activity}
|
||||
group-objects {:index 5 :id :objects}
|
||||
group-symbols {:index 6 :id :symbols}
|
||||
group-flags {:index 7 :id :flags}
|
||||
nil))
|
||||
|
||||
(def ^:private categorized-and-partitioned
|
||||
(->> emoji-data
|
||||
(reduce (fn [acc {:keys [group] :as emoji}]
|
||||
(update-in acc [(-> (emoji-group->category group) :index) :data] conj emoji))
|
||||
categories)
|
||||
(reduce (fn [acc {:keys [data] :as item}]
|
||||
(conj acc (assoc item :data (partition-all constants/emojis-per-row data))))
|
||||
[])))
|
||||
|
||||
(def ^:const flatten-data
|
||||
(mapcat (fn [{:keys [title id data]}]
|
||||
(into [{:title title :id id :header? true}] data))
|
||||
categorized-and-partitioned))
|
||||
|
||||
(def ^:private filter-section-header-index
|
||||
(keep-indexed #(when (:header? %2) %1) flatten-data))
|
||||
|
||||
(defn get-section-header-index-in-data
|
||||
[index]
|
||||
(nth filter-section-header-index index))
|
|
@ -0,0 +1,7 @@
|
|||
(ns status-im2.contexts.emoji-picker.events
|
||||
(:require [utils.re-frame :as rf]))
|
||||
|
||||
(rf/defn open-emoji-picker
|
||||
{:events [:emoji-picker/open]}
|
||||
[_ {:keys [on-select]}]
|
||||
{:dispatch [:open-modal :emoji-picker {:on-select on-select}]})
|
|
@ -0,0 +1,53 @@
|
|||
(ns status-im2.contexts.emoji-picker.style
|
||||
(:require [quo2.components.profile.showcase-nav.style :as showcase-nav.style]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im2.contexts.emoji-picker.constants :as constants]))
|
||||
|
||||
(def flex-spacer {:flex 1})
|
||||
|
||||
(def category-nav-height (+ (safe-area/get-bottom) showcase-nav.style/height))
|
||||
|
||||
(def search-input-container
|
||||
{:padding-horizontal 20
|
||||
:padding-bottom 12})
|
||||
|
||||
(defn section-header
|
||||
[theme]
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-95 theme)
|
||||
:z-index 1
|
||||
:margin-bottom constants/emoji-section-header-margin-bottom})
|
||||
|
||||
(def emoji-row-container
|
||||
{:padding-horizontal constants/emoji-row-padding-horizontal
|
||||
:padding-bottom constants/emoji-row-separator-height
|
||||
:flex-direction :row
|
||||
:overflow :hidden})
|
||||
|
||||
(defn emoji-container
|
||||
[last-item-on-row?]
|
||||
(cond-> {:height constants/emoji-size
|
||||
:width constants/emoji-size
|
||||
:margin-right constants/emoji-item-margin-right}
|
||||
|
||||
last-item-on-row?
|
||||
(dissoc :margin-right)))
|
||||
|
||||
(def list-container
|
||||
{:padding-bottom showcase-nav.style/height})
|
||||
|
||||
(def empty-results
|
||||
{:margin-top 100})
|
||||
|
||||
(def category-container
|
||||
{:height category-nav-height
|
||||
:overflow :hidden
|
||||
:position :absolute
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0
|
||||
:z-index 1})
|
||||
|
||||
(def category-blur-container
|
||||
{:height category-nav-height
|
||||
:overflow :hidden})
|
|
@ -0,0 +1,17 @@
|
|||
(ns status-im2.contexts.emoji-picker.utils
|
||||
(:require [clojure.string :as string]
|
||||
[status-im2.contexts.emoji-picker.constants :as constants]
|
||||
[status-im2.contexts.emoji-picker.data :refer [emoji-data]]))
|
||||
|
||||
(defn search-emoji
|
||||
[search-query]
|
||||
(let [cleaned-query (string/lower-case (string/trim search-query))]
|
||||
(->> (filter (fn [{:keys [label tags emoticon]}]
|
||||
(or (string/includes? label cleaned-query)
|
||||
(when emoticon
|
||||
(if (vector? emoticon)
|
||||
(some #(string/includes? (string/lower-case %) cleaned-query) emoticon)
|
||||
(string/includes? (string/lower-case emoticon) cleaned-query)))
|
||||
(some #(string/includes? % cleaned-query) tags)))
|
||||
emoji-data)
|
||||
(partition-all constants/emojis-per-row))))
|
|
@ -0,0 +1,41 @@
|
|||
(ns status-im2.contexts.emoji-picker.utils-test
|
||||
(:require [cljs.test :refer [deftest is testing]]
|
||||
[status-im2.contexts.emoji-picker.utils :as utils]))
|
||||
|
||||
(deftest emoji-search-test
|
||||
(testing "search for emojis with name"
|
||||
(let [search-input "dolphin"
|
||||
expected `(({:group 3
|
||||
:hexcode "1f42c"
|
||||
:label "dolphin"
|
||||
:tags ["flipper"]
|
||||
:unicode "🐬"}))]
|
||||
(is (= expected (utils/search-emoji search-input)))))
|
||||
|
||||
(testing "search for emojis with emoticon"
|
||||
(let [search-input "<3"
|
||||
expected `(({:group 0
|
||||
:hexcode "2764"
|
||||
:label "red heart"
|
||||
:tags ["heart"]
|
||||
:unicode "❤️"
|
||||
:emoticon "<3"}))]
|
||||
(is (= expected (utils/search-emoji search-input)))))
|
||||
|
||||
(testing "search for emojis with tag"
|
||||
(let [search-input "tada"
|
||||
expected `(({:group 6
|
||||
:hexcode "1f389"
|
||||
:label "party popper"
|
||||
:tags ["celebration" "party" "popper" "tada"]
|
||||
:unicode "🎉"}))]
|
||||
(is (= expected (utils/search-emoji search-input)))))
|
||||
|
||||
(testing "search for emojis with tag"
|
||||
(let [search-input "raven"
|
||||
expected `(({:group 3
|
||||
:hexcode "1f426-200d-2b1b"
|
||||
:label "black bird"
|
||||
:tags ["bird" "black" "crow" "raven" "rook"]
|
||||
:unicode "🐦⬛"}))]
|
||||
(is (= expected (utils/search-emoji search-input))))))
|
|
@ -0,0 +1,208 @@
|
|||
(ns status-im2.contexts.emoji-picker.view
|
||||
(:require [clojure.string :as string]
|
||||
[oops.core :as oops]
|
||||
[quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as quo.theme]
|
||||
[react-native.blur :as blur]
|
||||
[react-native.core :as rn]
|
||||
[react-native.gesture :as gesture]
|
||||
[react-native.platform :as platform]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.emoji-picker.constants :as constants]
|
||||
[status-im2.contexts.emoji-picker.data :as emoji-picker.data]
|
||||
[status-im2.contexts.emoji-picker.style :as style]
|
||||
[status-im2.contexts.emoji-picker.utils :as emoji-picker.utils]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(defn- on-press-category
|
||||
[{:keys [id index active-category scroll-ref]}]
|
||||
(reset! active-category id)
|
||||
(some-> ^js @scroll-ref
|
||||
(.scrollToIndex #js
|
||||
{:index (emoji-picker.data/get-section-header-index-in-data index)
|
||||
:animated false})))
|
||||
|
||||
(defn- handle-on-viewable-items-changed
|
||||
[{:keys [event active-category should-update-active-category?]}]
|
||||
(when should-update-active-category?
|
||||
(let [viewable-item (-> (oops/oget event "viewableItems")
|
||||
transforms/js->clj
|
||||
first
|
||||
:item)
|
||||
header? (and (map? viewable-item) (:header? viewable-item))
|
||||
section-key (if header?
|
||||
(:id viewable-item)
|
||||
(:id (emoji-picker.data/emoji-group->category (-> viewable-item
|
||||
first
|
||||
:group))))]
|
||||
(when (and (some? section-key) (not= @active-category section-key))
|
||||
(reset! active-category section-key)))))
|
||||
|
||||
(defn- get-item-layout
|
||||
[_ index]
|
||||
#js
|
||||
{:length constants/item-height
|
||||
:offset (* constants/item-height index)
|
||||
:index index})
|
||||
|
||||
(defn- section-header
|
||||
[{:keys [title]} {:keys [theme]}]
|
||||
[quo/divider-label
|
||||
{:tight? false
|
||||
:container-style (style/section-header theme)}
|
||||
(i18n/label title)])
|
||||
|
||||
(defn- emoji-item
|
||||
[{:keys [unicode] :as emoji} col-index on-select close]
|
||||
(let [on-press (fn []
|
||||
(when on-select
|
||||
(on-select unicode emoji))
|
||||
(close))
|
||||
last-item-on-row? (= (inc col-index) constants/emojis-per-row)]
|
||||
(fn []
|
||||
[rn/pressable
|
||||
{:style (style/emoji-container last-item-on-row?)
|
||||
:on-press on-press}
|
||||
[rn/text
|
||||
{:style {:font-size constants/emoji-size}
|
||||
:adjusts-font-size-to-fit true
|
||||
:allow-font-scaling false}
|
||||
unicode]])))
|
||||
|
||||
(defn- emoji-row
|
||||
[row-data {:keys [on-select close]}]
|
||||
(into [rn/view {:style style/emoji-row-container}]
|
||||
(map-indexed
|
||||
(fn [col-index {:keys [hexcode] :as emoji}]
|
||||
^{:key hexcode}
|
||||
[emoji-item emoji col-index on-select close])
|
||||
row-data)))
|
||||
|
||||
(defn- render-item
|
||||
[item _ _ render-data]
|
||||
(if (:header? item)
|
||||
[section-header item render-data]
|
||||
[emoji-row item render-data]))
|
||||
|
||||
(defn- empty-result
|
||||
[]
|
||||
[quo/empty-state
|
||||
{:title (i18n/label :t/emoji-no-results-title)
|
||||
:description (i18n/label :t/emoji-no-results-description)
|
||||
:placeholder? true
|
||||
:container-style style/empty-results}])
|
||||
|
||||
(defn- render-list
|
||||
[{:keys [theme filtered-data on-viewable-items-changed scroll-enabled on-scroll on-select
|
||||
set-scroll-ref close]}]
|
||||
(let [data (if filtered-data filtered-data emoji-picker.data/flatten-data)]
|
||||
[gesture/flat-list
|
||||
{:ref set-scroll-ref
|
||||
:scroll-enabled @scroll-enabled
|
||||
:data data
|
||||
:initial-num-to-render 20
|
||||
:max-to-render-per-batch 20
|
||||
:render-fn render-item
|
||||
:get-item-layout get-item-layout
|
||||
:keyboard-dismiss-mode :on-drag
|
||||
:keyboard-should-persist-taps :handled
|
||||
:shows-vertical-scroll-indicator false
|
||||
:on-scroll-to-index-failed identity
|
||||
:empty-component [empty-result]
|
||||
:on-scroll on-scroll
|
||||
:render-data {:close close
|
||||
:theme theme
|
||||
:on-select on-select}
|
||||
:content-container-style style/list-container
|
||||
:viewability-config {:item-visible-percent-threshold 100
|
||||
:minimum-view-time 200}
|
||||
:on-viewable-items-changed on-viewable-items-changed}]))
|
||||
|
||||
(defn- footer
|
||||
[{:keys [theme active-category scroll-ref]}]
|
||||
(let [on-press (fn [id index]
|
||||
(on-press-category
|
||||
{:id id
|
||||
:index index
|
||||
:active-category active-category
|
||||
:scroll-ref scroll-ref}))]
|
||||
(fn []
|
||||
[rn/view {:style style/category-container}
|
||||
[blur/view
|
||||
{:style style/category-blur-container
|
||||
:blur-radius (if platform/android? 20 10)
|
||||
:blur-amount (if platform/ios? 20 10)
|
||||
:blur-type (quo.theme/theme-value (if platform/ios? :light :xlight) :dark theme)
|
||||
:overlay-color (quo.theme/theme-value colors/white-70-blur colors/neutral-95-opa-70-blur theme)}
|
||||
[quo/showcase-nav
|
||||
{:state :scroll
|
||||
:active-id @active-category
|
||||
:data emoji-picker.data/categories
|
||||
:on-press on-press}]]])))
|
||||
|
||||
(defn- clear
|
||||
[{:keys [active-category filtered-data search-text]}]
|
||||
(reset! active-category constants/default-category)
|
||||
(reset! filtered-data nil)
|
||||
(reset! search-text ""))
|
||||
|
||||
(defn- view-internal
|
||||
[_]
|
||||
(let [{:keys [on-select]} (rf/sub [:get-screen-params])
|
||||
scroll-ref (atom nil)
|
||||
set-scroll-ref #(reset! scroll-ref %)
|
||||
search-text (reagent/atom "")
|
||||
filtered-data (reagent/atom nil)
|
||||
active-category (reagent/atom constants/default-category)
|
||||
clear-states #(clear {:active-category active-category
|
||||
:filtered-data filtered-data
|
||||
:search-text search-text})
|
||||
search-emojis (debounce/debounce
|
||||
(fn []
|
||||
(when (pos? (count @search-text))
|
||||
(reset! filtered-data (emoji-picker.utils/search-emoji
|
||||
@search-text))))
|
||||
constants/search-debounce-ms)
|
||||
on-change-text (fn [text]
|
||||
(if (string/blank? text)
|
||||
(clear-states)
|
||||
(do
|
||||
(reset! search-text text)
|
||||
(search-emojis))))
|
||||
on-viewable-items-changed (fn [event]
|
||||
(handle-on-viewable-items-changed
|
||||
{:event event
|
||||
:active-category active-category
|
||||
:should-update-active-category? (nil? @filtered-data)}))]
|
||||
(fn [{:keys [theme] :as sheet-opts}]
|
||||
(let [search-active? (pos? (count @search-text))]
|
||||
[rn/keyboard-avoiding-view
|
||||
{:style style/flex-spacer
|
||||
:keyboard-vertical-offset 8}
|
||||
[rn/view {:style style/flex-spacer}
|
||||
[rn/view {:style style/search-input-container}
|
||||
[quo/input
|
||||
{:small? true
|
||||
:placeholder (i18n/label :t/emoji-search-placeholder)
|
||||
:icon-name :i/search
|
||||
:value @search-text
|
||||
:on-change-text on-change-text
|
||||
:clearable? search-active?
|
||||
:on-clear clear-states}]]
|
||||
[render-list
|
||||
(merge {:filtered-data @filtered-data
|
||||
:set-scroll-ref set-scroll-ref
|
||||
:on-select on-select
|
||||
:on-viewable-items-changed on-viewable-items-changed}
|
||||
sheet-opts)]
|
||||
(when-not search-active?
|
||||
[footer
|
||||
{:theme theme
|
||||
:active-category active-category
|
||||
:scroll-ref scroll-ref}])]]))))
|
||||
|
||||
(def view (quo.theme/with-theme view-internal))
|
|
@ -1,7 +1,8 @@
|
|||
(ns status-im2.contexts.quo-preview.avatars.account-avatar
|
||||
(:require [quo2.core :as quo]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]))
|
||||
[status-im2.contexts.quo-preview.preview :as preview]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(def descriptor
|
||||
[{:key :type
|
||||
|
@ -36,5 +37,16 @@
|
|||
:emoji "🍑"
|
||||
:type :default})]
|
||||
(fn []
|
||||
[preview/preview-container {:state state :descriptor descriptor}
|
||||
[quo/account-avatar @state]])))
|
||||
[preview/preview-container
|
||||
{:state state
|
||||
:descriptor descriptor
|
||||
:component-container-style {:align-items :center
|
||||
:justify-content :center}}
|
||||
[quo/account-avatar @state]
|
||||
[quo/button
|
||||
{:type :grey
|
||||
:container-style {:margin-top 30}
|
||||
:on-press #(rf/dispatch [:emoji-picker/open
|
||||
{:on-select (fn [emoji]
|
||||
(swap! state assoc :emoji emoji))}])}
|
||||
"Open emoji picker"]])))
|
||||
|
|
|
@ -45,7 +45,9 @@
|
|||
:type :grey
|
||||
:background :photo
|
||||
:icon-only? true
|
||||
:on-press #(js/alert "pressed")
|
||||
:on-press #(rf/dispatch [:emoji-picker/open
|
||||
{:on-select (fn [selected-emoji]
|
||||
(reset! emoji selected-emoji))}])
|
||||
:container-style style/reaction-button-container} :i/reaction]]
|
||||
[quo/title-input
|
||||
{:color :red
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
status-im2.contexts.chat.events
|
||||
status-im2.contexts.chat.photo-selector.events
|
||||
status-im2.contexts.communities.overview.events
|
||||
status-im2.contexts.emoji-picker.events
|
||||
status-im2.contexts.onboarding.events
|
||||
status-im2.contexts.profile.events
|
||||
status-im2.contexts.shell.share.events
|
||||
|
|
|
@ -1,37 +1,38 @@
|
|||
(ns status-im2.navigation.screens
|
||||
(:require
|
||||
[status-im.ui.screens.screens :as old-screens]
|
||||
[status-im2.config :as config]
|
||||
[status-im2.contexts.add-new-contact.views :as add-new-contact]
|
||||
[status-im2.contexts.chat.camera.view :as camera-screen]
|
||||
[status-im2.contexts.chat.group-details.view :as group-details]
|
||||
[status-im2.contexts.chat.lightbox.view :as lightbox]
|
||||
[status-im2.contexts.chat.messages.view :as chat]
|
||||
[status-im2.contexts.chat.new-chat.view :as new-chat]
|
||||
[status-im2.contexts.chat.photo-selector.view :as photo-selector]
|
||||
[status-im2.contexts.chat.camera.view :as camera-screen]
|
||||
[status-im2.contexts.communities.actions.request-to-join.view :as join-menu]
|
||||
[status-im2.contexts.communities.discover.view :as communities.discover]
|
||||
[status-im2.contexts.communities.overview.view :as communities.overview]
|
||||
[status-im2.contexts.onboarding.intro.view :as intro]
|
||||
[status-im2.contexts.emoji-picker.view :as emoji-picker]
|
||||
[status-im2.contexts.onboarding.create-password.view :as create-password]
|
||||
[status-im2.contexts.onboarding.create-profile.view :as create-profile]
|
||||
[status-im2.contexts.onboarding.enable-biometrics.view :as enable-biometrics]
|
||||
[status-im2.contexts.onboarding.enable-notifications.view :as enable-notifications]
|
||||
[status-im2.contexts.onboarding.enter-seed-phrase.view :as enter-seed-phrase]
|
||||
[status-im2.contexts.onboarding.generating-keys.view :as generating-keys]
|
||||
[status-im2.contexts.onboarding.identifiers.view :as identifiers]
|
||||
[status-im2.contexts.onboarding.welcome.view :as welcome]
|
||||
[status-im2.contexts.onboarding.intro.view :as intro]
|
||||
[status-im2.contexts.onboarding.new-to-status.view :as new-to-status]
|
||||
[status-im2.contexts.onboarding.sign-in.view :as sign-in]
|
||||
[status-im2.contexts.onboarding.generating-keys.view :as generating-keys]
|
||||
[status-im2.contexts.onboarding.enter-seed-phrase.view :as enter-seed-phrase]
|
||||
[status-im2.contexts.onboarding.syncing.results.view :as syncing-results]
|
||||
[status-im2.contexts.onboarding.syncing.progress.view :as syncing-devices]
|
||||
[status-im2.navigation.transitions :as transitions]
|
||||
[status-im2.contexts.onboarding.syncing.results.view :as syncing-results]
|
||||
[status-im2.contexts.onboarding.welcome.view :as welcome]
|
||||
[status-im2.contexts.profile.profiles.view :as profiles]
|
||||
[status-im2.contexts.quo-preview.main :as quo.preview]
|
||||
[status-im2.contexts.shell.activity-center.view :as activity-center]
|
||||
[status-im2.contexts.shell.jump-to.view :as shell]
|
||||
[status-im2.contexts.shell.share.view :as share]
|
||||
[status-im2.contexts.syncing.how-to-pair.view :as how-to-pair]
|
||||
[status-im2.contexts.syncing.find-sync-code.view :as find-sync-code]
|
||||
[status-im2.contexts.syncing.how-to-pair.view :as how-to-pair]
|
||||
[status-im2.contexts.syncing.scan-sync-code-page.view :as scan-sync-code-page]
|
||||
[status-im2.contexts.syncing.setup-syncing.view :as settings-setup-syncing]
|
||||
[status-im2.contexts.syncing.syncing-devices-list.view :as settings-syncing]
|
||||
|
@ -41,8 +42,8 @@
|
|||
[status-im2.contexts.wallet.saved-address.view :as wallet-saved-address]
|
||||
[status-im2.contexts.wallet.saved-addresses.view :as wallet-saved-addresses]
|
||||
[status-im2.contexts.wallet.send.view :as wallet-send]
|
||||
[status-im.ui.screens.screens :as old-screens]
|
||||
[status-im2.navigation.options :as options]))
|
||||
[status-im2.navigation.options :as options]
|
||||
[status-im2.navigation.transitions :as transitions]))
|
||||
|
||||
(defn screens
|
||||
[]
|
||||
|
@ -233,6 +234,10 @@
|
|||
:animations transitions/push-animations-for-transparent-background}
|
||||
:component welcome/view}
|
||||
|
||||
{:name :emoji-picker
|
||||
:options {:sheet? true}
|
||||
:component emoji-picker/view}
|
||||
|
||||
{:name :wallet-accounts
|
||||
:component wallet-accounts/view}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
(ns utils.debounce
|
||||
(:require [re-frame.core :as re-frame]))
|
||||
(:require [goog.functions]
|
||||
[re-frame.core :as re-frame]))
|
||||
|
||||
(def timeout (atom {}))
|
||||
|
||||
|
@ -30,3 +31,7 @@
|
|||
(swap! chill assoc event-key true)
|
||||
(js/setTimeout #(swap! chill assoc event-key false) duration-ms)
|
||||
(re-frame/dispatch event))))
|
||||
|
||||
(defn debounce
|
||||
[f duration-ms]
|
||||
(goog.functions/debounce f duration-ms))
|
||||
|
|
|
@ -2306,5 +2306,17 @@
|
|||
"mint": "Mint",
|
||||
"via": "via",
|
||||
"x-counter": "x{{counter}}",
|
||||
"name-ens-or-address": "Name, ENS, or address"
|
||||
"name-ens-or-address": "Name, ENS, or address",
|
||||
"emoji-search-placeholder": "Search emojis",
|
||||
"emoji-recent": "Recent",
|
||||
"emoji-people": "People",
|
||||
"emoji-nature": "Nature",
|
||||
"emoji-food": "Food",
|
||||
"emoji-activity": "Activity",
|
||||
"emoji-travel": "Travel",
|
||||
"emoji-objects": "Objects",
|
||||
"emoji-symbols": "Symbols",
|
||||
"emoji-flags": "Flags",
|
||||
"emoji-no-results-title": "No emojis match your search",
|
||||
"emoji-no-results-description": "Try something like “rainbow”"
|
||||
}
|
||||
|
|