Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e8ce65167 | ||
|
|
8090f37bdd | ||
|
|
54825c6735 | ||
|
|
030f17fc49 | ||
|
|
2bc7d15683 | ||
|
|
8af7011eb4 | ||
|
|
f6d74df80b | ||
|
|
6c1929096c | ||
|
|
d902fb10d1 | ||
|
|
270f5ab258 | ||
|
|
c3e52dbf47 |
@@ -32,7 +32,10 @@
|
||||
android:theme="@style/Theme.AppSplash"
|
||||
android:name=".MainApplication"
|
||||
android:largeHeap="true"
|
||||
android:usesCleartextTraffic="true">
|
||||
android:usesCleartextTraffic="true"
|
||||
android:extractNativeLibs="true">
|
||||
<!-- After upgrading Android Gradle Plugin to 4.2.0 and above we must get rid of `extractNativeLibs="true"`
|
||||
and use`useLegacyPackaging` flag in our app's `build.gradle`-->
|
||||
<meta-data android:name="commitHash" android:value="${commitHash}"/>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
@@ -544,7 +544,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/usr/bin/env sh";
|
||||
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\" --openssl-legacy-provider --max-old-space-size=16384 \"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
|
||||
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\"${NODE_ARGS:- --max-old-space-size=16384 }\"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
|
||||
};
|
||||
3AAD2AD524A3A60E0075D594 /* Bundle React Native code and images */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
@@ -558,7 +558,7 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = "/usr/bin/env sh";
|
||||
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\" --openssl-legacy-provider --max-old-space-size=16384 \"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
|
||||
shellScript = "set -o errexit\nexport NODE_BINARY=\"${NODE_BINARY:-node}\"\nexport NODE_ARGS=\"${NODE_ARGS:- --max-old-space-size=16384 }\"\n\nbash -x ../node_modules/react-native/scripts/react-native-xcode.sh > ./logs/react-native-xcode.log 2>&1";
|
||||
};
|
||||
3AAD2AD724A3A60E0075D594 /* Run Script */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
|
||||
@@ -5,6 +5,8 @@ mkShell {
|
||||
shellHook = ''
|
||||
# Fix for ERR_OSSL_EVP_UNSUPPORTED error.
|
||||
export NODE_OPTIONS="--openssl-legacy-provider";
|
||||
# Same fix but for Xcode React Native script.
|
||||
export NODE_ARGS="--openssl-legacy-provider --max-old-space-size=16384";
|
||||
|
||||
# Fix Xcode using system Node.js version.
|
||||
export NODE_BINARY="${nodejs}/bin/node";
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
(ns quo2.components.links.url-preview-list.component-spec
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[quo2.components.links.url-preview-list.view :as view]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(def previews
|
||||
(->> (range 3)
|
||||
(map inc)
|
||||
(mapv (fn [index]
|
||||
{:title (str "Title " index)
|
||||
:body (str "status.im." index)
|
||||
:loading? false
|
||||
:url (str "status.im." index)}))))
|
||||
|
||||
(h/describe "Links - URL Preview List"
|
||||
(h/test "default render"
|
||||
(h/render [view/view
|
||||
{:data previews
|
||||
:key-fn :url
|
||||
:horizontal-spacing 10}])
|
||||
(-> (count (h/query-all-by-label-text :url-preview))
|
||||
(h/expect)
|
||||
(.toEqual 3))
|
||||
|
||||
(-> (map #(oops/oget % "props.children")
|
||||
(h/query-all-by-label-text :title))
|
||||
(clj->js)
|
||||
(h/expect)
|
||||
(.toStrictEqual #js ["Title 1" "Title 2" "Title 3"])))
|
||||
|
||||
(h/test "on-clear event is individually handled by each preview"
|
||||
(let [on-clear (h/mock-fn)]
|
||||
(h/render [view/view
|
||||
{:data previews
|
||||
:key-fn :url
|
||||
:on-clear on-clear}])
|
||||
(h/fire-event :press (first (h/get-all-by-label-text :button-clear-preview)))
|
||||
(h/fire-event :press (second (h/get-all-by-label-text :button-clear-preview)))
|
||||
(h/was-called-times on-clear 2)))
|
||||
|
||||
(h/test "previews have separate loading states"
|
||||
(h/render [view/view
|
||||
{:data (assoc-in previews [1 :loading?] true)
|
||||
:key-fn :url}])
|
||||
(h/is-truthy (h/get-by-label-text :url-preview-loading))
|
||||
(-> (count (h/query-all-by-label-text :url-preview))
|
||||
(h/expect)
|
||||
(.toEqual 2))))
|
||||
@@ -0,0 +1,8 @@
|
||||
(ns quo2.components.links.url-preview-list.style)
|
||||
|
||||
(def url-preview-gap 12)
|
||||
|
||||
(def url-preview-separator
|
||||
{:width url-preview-gap
|
||||
:padding-top 12
|
||||
:padding-bottom 8})
|
||||
@@ -0,0 +1,86 @@
|
||||
(ns quo2.components.links.url-preview-list.view
|
||||
(:require
|
||||
[oops.core :as oops]
|
||||
[quo2.components.links.url-preview-list.style :as style]
|
||||
[quo2.components.links.url-preview.view :as url-preview]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn- use-scroll-to-last-item
|
||||
[flat-list-ref item-count item-width]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(when (and (pos? item-count) (pos? item-width))
|
||||
;; We use a delay because calling `scrollToOffset` without a delay does
|
||||
;; nothing while the flatlist is still rendering its children.
|
||||
;; `scrollToEnd` doesn't work because it positions the item off-center
|
||||
;; and there's no argument to offset it.
|
||||
(let [timer-id (js/setTimeout
|
||||
(fn []
|
||||
(when (and @flat-list-ref (pos? item-count))
|
||||
(.scrollToOffset ^js @flat-list-ref
|
||||
#js
|
||||
{:animated true
|
||||
:offset (* (+ item-width style/url-preview-gap)
|
||||
(max 0 (dec item-count)))})))
|
||||
25)]
|
||||
(fn []
|
||||
(js/clearTimeout timer-id)))))
|
||||
[item-count item-width]))
|
||||
|
||||
(defn- separator
|
||||
[]
|
||||
[rn/view {:style style/url-preview-separator}])
|
||||
|
||||
(defn- item-component
|
||||
[{:keys [title body loading? logo]} _ _
|
||||
{:keys [width on-clear loading-message container-style]}]
|
||||
[url-preview/view
|
||||
{:logo logo
|
||||
:title title
|
||||
:body body
|
||||
:loading? loading?
|
||||
:loading-message loading-message
|
||||
:on-clear on-clear
|
||||
:container-style (merge container-style {:width width})}])
|
||||
|
||||
(defn- calculate-width
|
||||
[preview-width horizontal-spacing ^js e]
|
||||
(reset! preview-width
|
||||
(- (oops/oget e "nativeEvent.layout.width")
|
||||
(* 2 horizontal-spacing))))
|
||||
|
||||
(defn- view-component
|
||||
[]
|
||||
(let [preview-width (reagent/atom 0)
|
||||
flat-list-ref (atom nil)]
|
||||
(fn [{:keys [data key-fn horizontal-spacing on-clear loading-message
|
||||
container-style container-style-item]}]
|
||||
(use-scroll-to-last-item flat-list-ref (count data) @preview-width)
|
||||
;; We need to use a wrapping view expanded to 100% instead of "flex 1",
|
||||
;; otherwise `on-layout` will be triggered multiple times as the flat list
|
||||
;; renders its children.
|
||||
[rn/view
|
||||
{:style (merge container-style {:width "100%"})
|
||||
:accessibility-label :url-preview-list}
|
||||
[rn/flat-list
|
||||
{:ref #(reset! flat-list-ref %)
|
||||
:key-fn key-fn
|
||||
:on-layout #(calculate-width preview-width horizontal-spacing %)
|
||||
:horizontal true
|
||||
:deceleration-rate :fast
|
||||
:on-scroll-to-index-failed identity
|
||||
:content-container-style {:padding-horizontal horizontal-spacing}
|
||||
:separator [separator]
|
||||
:snap-to-interval (+ @preview-width style/url-preview-gap)
|
||||
:shows-horizontal-scroll-indicator false
|
||||
:data data
|
||||
:render-fn item-component
|
||||
:render-data {:width @preview-width
|
||||
:on-clear on-clear
|
||||
:loading-message loading-message
|
||||
:container-style container-style-item}}]])))
|
||||
|
||||
(defn view
|
||||
[props]
|
||||
[:f> view-component props])
|
||||
@@ -30,18 +30,18 @@
|
||||
(when on-press
|
||||
{:on-press on-press}))
|
||||
[rn/view
|
||||
{:style {:flex-direction :row
|
||||
:flex-grow 0
|
||||
:flex-shrink 1
|
||||
:padding-horizontal 20
|
||||
:align-items :center}}
|
||||
{:style (cond-> {:flex-direction :row
|
||||
:flex-grow 0
|
||||
:flex-shrink 1
|
||||
:align-items :center}
|
||||
icon (assoc :padding-horizontal 20))}
|
||||
[rn/view
|
||||
{:style {:width 20
|
||||
:height 20
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:margin-right 12}}
|
||||
[icons/icon icon {:color icon-color}]]
|
||||
{:style (cond-> {:width 20
|
||||
:height 20
|
||||
:align-items :center
|
||||
:justify-content :center}
|
||||
icon (assoc :margin-right 12))}
|
||||
(when icon [icons/icon icon {:color icon-color}])]
|
||||
[rn/view
|
||||
[text/text
|
||||
{:weight :medium
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
(ns quo2.components.tags.context-tag.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn base-tag
|
||||
[override-theme blur?]
|
||||
{:border-radius 100
|
||||
:padding-vertical 3
|
||||
:flex-direction :row
|
||||
:padding-right 8
|
||||
:padding-left 8
|
||||
:background-color (if blur?
|
||||
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5 override-theme)
|
||||
(colors/theme-colors colors/neutral-10 colors/neutral-90 override-theme))})
|
||||
|
||||
(def context-tag-image
|
||||
{:width 20
|
||||
:border-radius 10
|
||||
:background-color :white
|
||||
:height 20})
|
||||
|
||||
(defn context-tag-icon-color
|
||||
[blur?]
|
||||
(if blur?
|
||||
(colors/theme-colors colors/neutral-80-opa-40 colors/white-opa-40)
|
||||
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
|
||||
|
||||
(def context-tag-text-container
|
||||
{:align-items :center
|
||||
:flex-direction :row})
|
||||
|
||||
(def audio-tag-container
|
||||
{:padding-left 2
|
||||
:padding-vertical 2})
|
||||
|
||||
(def audio-tag-icon-container
|
||||
{:width 20
|
||||
:height 20
|
||||
:border-radius 10
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:background-color colors/primary-50})
|
||||
|
||||
(def audio-tag-icon-color colors/white)
|
||||
|
||||
(defn audio-tag-text-color
|
||||
[override-theme]
|
||||
(colors/theme-colors colors/neutral-100 colors/white override-theme))
|
||||
|
||||
(def community-tag
|
||||
{:padding-vertical 2})
|
||||
|
||||
(defn community-tag-text
|
||||
[override-theme]
|
||||
{:margin-left 2
|
||||
:color (colors/theme-colors colors/neutral-100 colors/white override-theme)})
|
||||
@@ -0,0 +1,85 @@
|
||||
(ns quo2.components.tags.context-tag.view
|
||||
(:require [quo2.components.avatars.group-avatar :as group-avatar]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.tags.context-tag.style :as style]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn trim-public-key
|
||||
[pk]
|
||||
(str (subs pk 0 6) "..." (subs pk (- (count pk) 3))))
|
||||
|
||||
(defn base-tag
|
||||
[{:keys [override-theme style blur?]} & children]
|
||||
(into
|
||||
[rn/view {:style (merge (style/base-tag override-theme blur?) style)}]
|
||||
children))
|
||||
|
||||
(defn group-avatar-tag
|
||||
[label opts]
|
||||
[base-tag
|
||||
(-> opts
|
||||
(select-keys [:override-theme :style :blur?])
|
||||
(assoc-in [:style :padding-left] 3)
|
||||
(assoc-in [:style :padding-vertical] 2))
|
||||
[group-avatar/group-avatar opts]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (:text-style opts)}
|
||||
(str " " label)]])
|
||||
|
||||
(defn public-key-tag
|
||||
[params public-key]
|
||||
[base-tag params
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
:size :paragraph-2}
|
||||
(trim-public-key public-key)]])
|
||||
|
||||
(defn context-tag
|
||||
[{:keys [text-style blur?] :as params} photo name channel-name]
|
||||
(let [text-params {:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (assoc text-style :justify-content :center)}]
|
||||
[base-tag (assoc-in params [:style :padding-left] 3)
|
||||
[rn/image
|
||||
{:style style/context-tag-image
|
||||
:source photo}]
|
||||
[rn/view {:style style/context-tag-text-container}
|
||||
[text/text text-params (str " " name)]
|
||||
(when channel-name
|
||||
[:<>
|
||||
[icons/icon
|
||||
:i/chevron-right
|
||||
{:color (style/context-tag-icon-color blur?)
|
||||
:size 16}]
|
||||
[text/text text-params (str "# " channel-name)]])]]))
|
||||
|
||||
(defn user-avatar-tag
|
||||
[params username photo]
|
||||
[context-tag params {:uri photo} username])
|
||||
|
||||
(defn audio-tag
|
||||
[duration params]
|
||||
[base-tag (merge {:style style/audio-tag-container} params)
|
||||
[rn/view {:style style/audio-tag-icon-container}
|
||||
[icons/icon
|
||||
:i/play
|
||||
{:color style/audio-tag-icon-color
|
||||
:size 12}]]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:margin-left 4
|
||||
:color (style/audio-tag-text-color (:override-theme params))}}
|
||||
duration]])
|
||||
|
||||
(defn community-tag
|
||||
[avatar community-name {:keys [override-theme] :as params}]
|
||||
[context-tag
|
||||
(merge {:style style/community-tag
|
||||
:text-style (style/community-tag-text override-theme)}
|
||||
params)
|
||||
avatar
|
||||
community-name])
|
||||
@@ -1,126 +0,0 @@
|
||||
(ns quo2.components.tags.context-tags
|
||||
(:require [quo2.components.avatars.group-avatar :as group-avatar]
|
||||
[quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as quo2.theme]
|
||||
[react-native.core :as rn]))
|
||||
|
||||
(defn trim-public-key
|
||||
[pk]
|
||||
(str (subs pk 0 6) "..." (subs pk (- (count pk) 3))))
|
||||
|
||||
(defn base-tag
|
||||
[_ _]
|
||||
(fn [{:keys [override-theme style]} & children]
|
||||
(let [theme (or override-theme (quo2.theme/get-theme))]
|
||||
(into
|
||||
[rn/view
|
||||
(merge
|
||||
{:border-radius 100
|
||||
:padding-vertical 3
|
||||
:flex-direction :row
|
||||
:padding-right 8
|
||||
:padding-left 8
|
||||
:background-color (if (= theme :light)
|
||||
colors/neutral-10
|
||||
colors/neutral-90)}
|
||||
style)]
|
||||
children))))
|
||||
|
||||
(defn group-avatar-tag
|
||||
[_ _]
|
||||
(fn [label opts]
|
||||
[base-tag
|
||||
(-> opts
|
||||
(select-keys [:override-theme :style])
|
||||
(assoc-in [:style :padding-left] 3)
|
||||
(assoc-in [:style :padding-vertical] 2))
|
||||
[group-avatar/group-avatar opts]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (:text-style opts)}
|
||||
(str " " label)]]))
|
||||
|
||||
(defn public-key-tag
|
||||
[_ _]
|
||||
(fn [params public-key]
|
||||
[base-tag params
|
||||
[text/text
|
||||
{:weight :monospace
|
||||
:size :paragraph-2}
|
||||
(trim-public-key public-key)]]))
|
||||
|
||||
(defn context-tag
|
||||
[_ _]
|
||||
(fn [params photo name channel-name]
|
||||
(let [text-style (:text-style params)
|
||||
text-params {:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (assoc text-style :justify-content :center)}
|
||||
icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
|
||||
[base-tag (assoc-in params [:style :padding-left] 3)
|
||||
[rn/image
|
||||
{:style {:width 20
|
||||
:border-radius 10
|
||||
:background-color :white
|
||||
:height 20}
|
||||
:source photo}]
|
||||
[rn/view
|
||||
{:style {:align-items :center
|
||||
:flex-direction :row}}
|
||||
[text/text text-params (str " " name)]
|
||||
(when channel-name
|
||||
[:<>
|
||||
[icons/icon
|
||||
:i/chevron-right
|
||||
{:color icon-color
|
||||
:size 16}]
|
||||
[text/text text-params (str "# " channel-name)]])]])))
|
||||
|
||||
(defn user-avatar-tag
|
||||
[]
|
||||
(fn [params username photo]
|
||||
[context-tag params {:uri photo} username]))
|
||||
|
||||
(defn audio-tag
|
||||
[duration params]
|
||||
[base-tag
|
||||
(merge
|
||||
{:style {:padding-left 2
|
||||
:padding-vertical 2}}
|
||||
params)
|
||||
[rn/view
|
||||
{:width 20
|
||||
:height 20
|
||||
:border-radius 10
|
||||
:align-items :center
|
||||
:justify-content :center
|
||||
:background-color colors/primary-50}
|
||||
[icons/icon
|
||||
:i/play
|
||||
{:color colors/white
|
||||
:size 12}]]
|
||||
[text/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style {:margin-left 4
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-100
|
||||
colors/white
|
||||
(:override-theme params))}}
|
||||
duration]])
|
||||
|
||||
(defn community-tag
|
||||
[avatar community-name params]
|
||||
[context-tag
|
||||
(merge
|
||||
{:style {:padding-vertical 2}
|
||||
:text-style {:margin-left 2
|
||||
:color (colors/theme-colors
|
||||
colors/neutral-100
|
||||
colors/white
|
||||
(:override-theme params))}}
|
||||
params)
|
||||
avatar community-name])
|
||||
+8
-7
@@ -34,6 +34,7 @@
|
||||
quo2.components.inputs.title-input.view
|
||||
quo2.components.inputs.profile-input.view
|
||||
quo2.components.links.url-preview.view
|
||||
quo2.components.links.url-preview-list.view
|
||||
quo2.components.list-items.channel
|
||||
quo2.components.list-items.menu-item
|
||||
quo2.components.list-items.preview-list
|
||||
@@ -66,7 +67,7 @@
|
||||
quo2.components.tabs.segmented-tab
|
||||
quo2.components.tabs.account-selector
|
||||
quo2.components.tabs.tabs
|
||||
quo2.components.tags.context-tags
|
||||
quo2.components.tags.context-tag.view
|
||||
quo2.components.tags.status-tags
|
||||
quo2.components.tags.permission-tag
|
||||
quo2.components.tags.tag
|
||||
@@ -90,12 +91,11 @@
|
||||
(def system-message quo2.components.messages.system-message/system-message)
|
||||
(def reaction quo2.components.reactions.reaction/reaction)
|
||||
(def add-reaction quo2.components.reactions.reaction/add-reaction)
|
||||
(def user-avatar-tag quo2.components.tags.context-tags/user-avatar-tag)
|
||||
(def context-tag quo2.components.tags.context-tags/context-tag)
|
||||
(def group-avatar-tag quo2.components.tags.context-tags/group-avatar-tag)
|
||||
(def audio-tag quo2.components.tags.context-tags/audio-tag)
|
||||
(def community-tag quo2.components.tags.context-tags/community-tag)
|
||||
|
||||
(def user-avatar-tag quo2.components.tags.context-tag.view/user-avatar-tag)
|
||||
(def context-tag quo2.components.tags.context-tag.view/context-tag)
|
||||
(def group-avatar-tag quo2.components.tags.context-tag.view/group-avatar-tag)
|
||||
(def audio-tag quo2.components.tags.context-tag.view/audio-tag)
|
||||
(def community-tag quo2.components.tags.context-tag.view/community-tag)
|
||||
(def floating-shell-button quo2.components.navigation.floating-shell-button/floating-shell-button)
|
||||
(def page-nav quo2.components.navigation.page-nav/page-nav)
|
||||
(def disclaimer quo2.components.selectors.disclaimer.view/view)
|
||||
@@ -199,3 +199,4 @@
|
||||
|
||||
;;;; LINKS
|
||||
(def url-preview quo2.components.links.url-preview.view/view)
|
||||
(def url-preview-list quo2.components.links.url-preview-list.view/view)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
[quo2.components.inputs.input.component-spec]
|
||||
[quo2.components.inputs.profile-input.component-spec]
|
||||
[quo2.components.inputs.title-input.component-spec]
|
||||
[quo2.components.links.url-preview-list.component-spec]
|
||||
[quo2.components.links.url-preview.component-spec]
|
||||
[quo2.components.markdown.--tests--.text-component-spec]
|
||||
[quo2.components.onboarding.small-option-card.component-spec]
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
[quo.react-native :as rn]
|
||||
[re-frame.core :as re-frame]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im.utils.platform :as platform]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.native-module.core :as status]))
|
||||
|
||||
@@ -91,38 +90,29 @@
|
||||
(assoc-in [:chats/mentions chat-id :mentions] state)
|
||||
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments))}))
|
||||
|
||||
(rf/defn on-text-input
|
||||
{:events [:mention/on-text-input]}
|
||||
[{:keys [db]} {:keys [previous-text start end] :as args}]
|
||||
(let [previous-text
|
||||
;; NOTE(rasom): on iOS `previous-text` contains entire input's text. To
|
||||
;; get only removed part of text we have cut it.
|
||||
(if platform/android?
|
||||
previous-text
|
||||
(subs previous-text start end))
|
||||
chat-id (:current-chat-id db)
|
||||
state (merge args {:previous-text previous-text})
|
||||
state (set/rename-keys state
|
||||
{:previous-text :PreviousText
|
||||
:new-text :NewText
|
||||
:start :Start
|
||||
:end :End})
|
||||
params [chat-id state]
|
||||
method "wakuext_chatMentionOnTextInput"]
|
||||
(log/debug "[mentions] on-text-input" {:params params})
|
||||
(rf/defn on-change-text
|
||||
{:events [:mention/on-change-text]}
|
||||
[{:keys [db]} text]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
params [chat-id text]
|
||||
method "wakuext_chatMentionOnChangeText"]
|
||||
(log/debug "[mentions] on-change-text" {:params params})
|
||||
{:json-rpc/call [{:method method
|
||||
:params [chat-id state]
|
||||
:on-success #(rf/dispatch [:mention/on-text-input-success %])
|
||||
:params params
|
||||
:on-success #(rf/dispatch [:mention/on-change-text-success %])
|
||||
:on-error #(rf/dispatch [:mention/on-error
|
||||
{:method method
|
||||
:params params} %])}]}))
|
||||
|
||||
(rf/defn on-text-input-success
|
||||
{:events [:mention/on-text-input-success]}
|
||||
(rf/defn on-change-text-success
|
||||
{:events [:mention/on-change-text-success]}
|
||||
[{:keys [db]} result]
|
||||
(log/debug "[mentions] on-text-input-success" {:result result})
|
||||
(let [{:keys [state chat-id]} (transfer-mention-result result)]
|
||||
{:db (assoc-in db [:chats/mentions chat-id :mentions] state)}))
|
||||
(let [{:keys [state chat-id mentionable-users input-segments]} (transfer-mention-result result)]
|
||||
{:db (-> db
|
||||
(assoc-in [:chats/mention-suggestions chat-id] mentionable-users)
|
||||
(assoc-in [:chats/mentions chat-id :mentions] state)
|
||||
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments))}))
|
||||
|
||||
(rf/defn recheck-at-idxs
|
||||
[{:keys [db]} public-key]
|
||||
@@ -175,16 +165,6 @@
|
||||
;; programmatic change. By calling `reset-text-input-cursor` we force the
|
||||
;; keyboard's cursor position to be changed before the next input.
|
||||
(reset-text-input-cursor text-input-ref cursor)
|
||||
;; NOTE(roman): on-text-input event is not dispatched when we change input
|
||||
;; programmatically, so we have to call `on-text-input` manually
|
||||
(on-text-input
|
||||
(let [match-len (count match)
|
||||
start (inc at-sign-idx)
|
||||
end (+ start match-len)]
|
||||
{:new-text match
|
||||
:previous-text searched-text
|
||||
:start start
|
||||
:end end}))
|
||||
(recheck-at-idxs public-key))))
|
||||
|
||||
(rf/defn clear-suggestions
|
||||
@@ -220,7 +200,7 @@
|
||||
(when text
|
||||
(log/debug "[mentions] check-selection" {:params params})
|
||||
{:json-rpc/call [{:method method
|
||||
:params [chat-id text start end]
|
||||
:params params
|
||||
:on-success #(rf/dispatch [:mention/on-handle-selection-change-success %])
|
||||
:on-error #(rf/dispatch [:mention/on-error
|
||||
{:method method
|
||||
@@ -230,8 +210,11 @@
|
||||
{:events [:mention/on-handle-selection-change-success]}
|
||||
[{:keys [db]} result]
|
||||
(log/debug "[mentions] on-check-selection-success" {:result result})
|
||||
(let [{:keys [state chat-id]} (transfer-mention-result result)]
|
||||
{:db (assoc-in db [:chats/mentions chat-id :mentions] (rename-state state))}))
|
||||
(let [{:keys [state chat-id mentionable-users input-segments]} (transfer-mention-result result)]
|
||||
{:db (-> db
|
||||
(assoc-in [:chats/mention-suggestions chat-id] mentionable-users)
|
||||
(assoc-in [:chats/mentions chat-id :mentions] state)
|
||||
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments))}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::reset-text-input-cursor
|
||||
@@ -240,28 +223,3 @@
|
||||
(status/reset-keyboard-input
|
||||
(rn/find-node-handle (react/current-ref ref))
|
||||
cursor))))
|
||||
|
||||
(rf/defn calculate-suggestions
|
||||
{:events [:mention/calculate-suggestions]}
|
||||
[{:keys [db]}]
|
||||
(let [chat-id (:current-chat-id db)
|
||||
text (get-in db [:chat/inputs chat-id :input-text])
|
||||
params [chat-id text]
|
||||
method "wakuext_chatMentionCalculateSuggestions"]
|
||||
(log/debug "[mentions] calculate-suggestions" {:params params})
|
||||
{:json-rpc/call [{:method method
|
||||
:params [chat-id text]
|
||||
:on-success #(rf/dispatch [:mention/on-calculate-suggestions-success %])
|
||||
:on-error #(rf/dispatch [:mention/on-error
|
||||
{:method method
|
||||
:params params} %])}]}))
|
||||
|
||||
(rf/defn on-calculate-suggestions-success
|
||||
{:events [:mention/on-calculate-suggestions-success]}
|
||||
[{:keys [db]} result]
|
||||
(log/debug "[mentions] on-calculate-suggestions-success" {:result result})
|
||||
(let [{:keys [state chat-id mentionable-users input-segments]} (transfer-mention-result result)]
|
||||
{:db (-> db
|
||||
(assoc-in [:chats/mention-suggestions chat-id] mentionable-users)
|
||||
(assoc-in [:chats/mentions chat-id :mentions] state)
|
||||
(assoc-in [:chat/inputs-with-mentions chat-id] input-segments))}))
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
:identicon (.-identicon chat)
|
||||
:muted (.-muted chat)
|
||||
:joined (.-joined chat)
|
||||
|
||||
:muted-till (.-mutetill chat)
|
||||
:chat-id (.-id chat)
|
||||
:community-id (.-communityId chat)
|
||||
:synced-from (.-syncedFrom chat)
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
status-im2.navigation.core
|
||||
status-im2.subs.root ; so integration tests can run independently
|
||||
[taoensso.timbre :as log]
|
||||
[utils.security.core :as security]))
|
||||
[utils.security.core :as security]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(def password "testabc")
|
||||
|
||||
@@ -295,7 +296,7 @@
|
||||
(rf/dispatch-sync [:chat/navigate-to-chat chat-id])
|
||||
(is (= chat-id @(rf/subscribe [:chats/current-chat-id])))
|
||||
(is @(rf/subscribe [:chats/chat chat-id]))
|
||||
(rf/dispatch-sync [:chat.ui/mute chat-id true])
|
||||
(rf/dispatch-sync [:chat.ui/mute chat-id true constants/mute-till-unmuted])
|
||||
(rf-test/wait-for
|
||||
[:chat/mute-successfully]
|
||||
(is @(rf/subscribe [:chats/muted chat-id]))
|
||||
|
||||
@@ -198,11 +198,7 @@
|
||||
(when platform/android?
|
||||
(reset! last-text-change (js/Date.now)))
|
||||
|
||||
(on-text-change text chat-id)
|
||||
;; NOTE(rasom): on iOS `on-change` is dispatched after `on-text-input`,
|
||||
;; that's why mention suggestions are calculated on `on-change`
|
||||
(when platform/ios?
|
||||
(re-frame/dispatch [:mention/calculate-suggestions]))))
|
||||
(on-text-change text chat-id)))
|
||||
|
||||
(rf/defn set-input-text
|
||||
"Set input text for current-chat. Takes db and input text and cofx
|
||||
@@ -220,26 +216,10 @@
|
||||
|
||||
(defn on-text-input
|
||||
[chat-id args]
|
||||
(let [native-event (.-nativeEvent ^js args)
|
||||
text (.-text ^js native-event)
|
||||
previous-text (.-previousText ^js native-event)
|
||||
range (.-range ^js native-event)
|
||||
start (.-start ^js range)
|
||||
end (.-end ^js range)]
|
||||
(let [native-event (.-nativeEvent ^js args)
|
||||
text (.-text ^js native-event)]
|
||||
(when (and (not (get @mentions-enabled chat-id)) (string/index-of text "@"))
|
||||
(swap! mentions-enabled assoc chat-id true))
|
||||
|
||||
(re-frame/dispatch
|
||||
[:mention/on-text-input
|
||||
{:new-text text
|
||||
:previous-text previous-text
|
||||
:start start
|
||||
:end end}])
|
||||
;; NOTE(rasom): on Android `on-text-input` is dispatched after
|
||||
;; `on-change`, that's why mention suggestions are calculated
|
||||
;; on `on-change`
|
||||
(when platform/android?
|
||||
(re-frame/dispatch [:mention/calculate-suggestions]))))
|
||||
(swap! mentions-enabled assoc chat-id true))))
|
||||
|
||||
(defn text-input
|
||||
[{:keys [set-active-panel refs chat-id sending-image]}]
|
||||
|
||||
@@ -13,7 +13,8 @@
|
||||
[status-im.ui.components.toolbar :as toolbar]
|
||||
[status-im.ui.components.topbar :as topbar]
|
||||
[status-im.ui.screens.profile.components.sheets :as sheets]
|
||||
[utils.re-frame :as rf]))
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(defn actions
|
||||
[{:keys [public-key added? blocked? ens-name mutual?] :as contact} muted?]
|
||||
@@ -43,7 +44,9 @@
|
||||
:selected muted?
|
||||
:disabled blocked?
|
||||
:action (when-not blocked?
|
||||
#(re-frame/dispatch [:chat.ui/mute public-key (not muted?)]))}]
|
||||
#(re-frame/dispatch [:chat.ui/mute public-key (not muted?)
|
||||
(when-not muted?
|
||||
constants/mute-till-unmuted)]))}]
|
||||
[{:label (i18n/label (if blocked? :t/unblock :t/block))
|
||||
:negative true
|
||||
:selected blocked?
|
||||
|
||||
@@ -141,33 +141,15 @@
|
||||
(reset! last-text-change (js/Date.now)))
|
||||
|
||||
(on-text-change text chat-id)
|
||||
;; NOTE(rasom): on iOS `on-change` is dispatched after `on-text-input`,
|
||||
;; that's why mention suggestions are calculated on `on-change`
|
||||
(when platform/ios?
|
||||
(rf/dispatch [:mention/calculate-suggestions]))))
|
||||
|
||||
(rf/dispatch [:mention/on-change-text text])))
|
||||
|
||||
(defn on-text-input
|
||||
[chat-id args]
|
||||
(let [native-event (.-nativeEvent ^js args)
|
||||
text (.-text ^js native-event)
|
||||
previous-text (.-previousText ^js native-event)
|
||||
range (.-range ^js native-event)
|
||||
start (.-start ^js range)
|
||||
end (.-end ^js range)]
|
||||
(let [native-event (.-nativeEvent ^js args)
|
||||
text (.-text ^js native-event)]
|
||||
(when (and (not (get @mentions-enabled? chat-id)) (string/index-of text "@"))
|
||||
(swap! mentions-enabled? assoc chat-id true))
|
||||
|
||||
(rf/dispatch
|
||||
[:mention/on-text-input
|
||||
{:new-text text
|
||||
:previous-text previous-text
|
||||
:start start
|
||||
:end end}])
|
||||
;; NOTE(rasom): on Android `on-text-input` is dispatched after
|
||||
;; `on-change`, that's why mention suggestions are calculated
|
||||
;; on `on-change`
|
||||
(when platform/android?
|
||||
(rf/dispatch [:mention/calculate-suggestions]))))
|
||||
(swap! mentions-enabled? assoc chat-id true))))
|
||||
|
||||
(defn text-input-style
|
||||
[chat-id]
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
|
||||
(defn mute-chat-action
|
||||
[chat-id]
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true]))
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true constants/mute-till-unmuted]))
|
||||
|
||||
(defn unmute-chat-action
|
||||
[chat-id]
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
(ns status-im2.common.mute-chat-drawer.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn header-text
|
||||
[]
|
||||
{:margin-left 20
|
||||
:margin-bottom 10
|
||||
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)})
|
||||
@@ -0,0 +1,65 @@
|
||||
(ns status-im2.common.mute-chat-drawer.view
|
||||
(:require [utils.i18n :as i18n]
|
||||
[quo2.core :as quo]
|
||||
[react-native.core :as rn]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.common.mute-chat-drawer.style :as style]))
|
||||
|
||||
(defn hide-sheet-and-dispatch
|
||||
[event]
|
||||
(rf/dispatch [:bottom-sheet/hide])
|
||||
(rf/dispatch event))
|
||||
|
||||
(defn mute-chat-drawer
|
||||
[chat-id accessibility-label]
|
||||
[rn/view {:accessibility-label accessibility-label}
|
||||
[quo/text
|
||||
{:weight :medium
|
||||
:size :paragraph-2
|
||||
:style (style/header-text)} (i18n/label :t/mute-channel)]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-for-15-mins)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-for-15-mins-type]))}]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-for-1-hour)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-for-1-hour-type]))}]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-for-8-hours)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-for-8-hours-type]))}]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-for-1-week)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-for-1-week]))}]
|
||||
[quo/menu-item
|
||||
{:type :transparent
|
||||
:title (i18n/label :t/mute-till-unmute)
|
||||
:icon-bg-color :transparent
|
||||
:container-padding-vertical 12
|
||||
:title-column-style {:margin-left 2}
|
||||
:on-press (fn []
|
||||
(hide-sheet-and-dispatch [:chat.ui/mute chat-id true
|
||||
constants/mute-till-unmuted]))}]])
|
||||
@@ -36,6 +36,12 @@
|
||||
(def ^:const activity-center-membership-status-accepted 2)
|
||||
(def ^:const activity-center-membership-status-declined 3)
|
||||
|
||||
(def ^:const mute-for-15-mins-type 1)
|
||||
(def ^:const mute-for-1-hour-type 2)
|
||||
(def ^:const mute-for-8-hours-type 3)
|
||||
(def ^:const mute-for-1-week 4)
|
||||
(def ^:const mute-till-unmuted 5)
|
||||
|
||||
(def ^:const activity-center-mark-all-as-read-undo-time-limit-ms 4000)
|
||||
(def ^:const activity-center-max-unread-count 99)
|
||||
|
||||
|
||||
@@ -64,13 +64,15 @@
|
||||
(defn map-chats
|
||||
[{:keys [db] :as cofx}]
|
||||
(fn [val]
|
||||
(assoc
|
||||
(merge
|
||||
(or (get (:chats db) (:chat-id val))
|
||||
(create-new-chat (:chat-id val) cofx))
|
||||
val)
|
||||
:invitation-admin
|
||||
(:invitation-admin val))))
|
||||
(let [chat (or (get (:chats db) (:chat-id val))
|
||||
(create-new-chat (:chat-id val) cofx))]
|
||||
(assoc
|
||||
(merge
|
||||
(cond-> chat
|
||||
(comp not :muted) (dissoc chat :muted-till))
|
||||
val)
|
||||
:invitation-admin
|
||||
(:invitation-admin val)))))
|
||||
|
||||
(rf/defn leave-removed-chat
|
||||
[{{:keys [view-id current-chat-id chats]} :db
|
||||
@@ -292,18 +294,20 @@
|
||||
|
||||
(rf/defn mute-chat-toggled-successfully
|
||||
{:events [:chat/mute-successfully]}
|
||||
[_ chat-id]
|
||||
(log/debug "muted chat successfully" chat-id))
|
||||
[{:keys [db]} chat-id muted-till]
|
||||
(log/debug "muted chat successfully" chat-id " for" muted-till)
|
||||
{:db (assoc-in db [:chats chat-id :muted-till] muted-till)})
|
||||
|
||||
(rf/defn mute-chat
|
||||
{:events [:chat.ui/mute]}
|
||||
[{:keys [db]} chat-id muted?]
|
||||
(let [method (if muted? "wakuext_muteChat" "wakuext_unmuteChat")]
|
||||
[{:keys [db]} chat-id muted? mute-type]
|
||||
(let [method (if muted? "wakuext_muteChatV2" "wakuext_unmuteChat")
|
||||
params (if muted? [{:chatId chat-id :mutedType mute-type}] [chat-id])]
|
||||
{:db (assoc-in db [:chats chat-id :muted] muted?)
|
||||
:json-rpc/call [{:method method
|
||||
:params [chat-id]
|
||||
:params params
|
||||
:on-error #(rf/dispatch [:chat/mute-failed chat-id muted? %])
|
||||
:on-success #(rf/dispatch [:chat/mute-successfully chat-id])}]}))
|
||||
:on-success #(rf/dispatch [:chat/mute-successfully chat-id %])}]}))
|
||||
|
||||
(rf/defn show-clear-history-confirmation
|
||||
{:events [:chat.ui/show-clear-history-confirmation]}
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
[status-im2.common.contact-list-item.view :as contact-list-item]
|
||||
[status-im2.common.home.actions.view :as actions]
|
||||
[utils.re-frame :as rf]
|
||||
[reagent.core :as reagent]))
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(defn back-button
|
||||
[]
|
||||
@@ -191,7 +192,8 @@
|
||||
[rn/touchable-opacity
|
||||
{:style (style/action-container color)
|
||||
:accessibility-label :toggle-mute
|
||||
:on-press #(rf/dispatch [:chat.ui/mute chat-id (not muted)])}
|
||||
:on-press #(rf/dispatch [:chat.ui/mute chat-id (not muted)
|
||||
(when-not muted constants/mute-till-unmuted)])}
|
||||
[quo/icon (if muted :i/muted :i/activity-center)
|
||||
{:size 20 :color (colors/theme-colors colors/neutral-100 colors/white)}]
|
||||
[quo/text {:style {:margin-top 16} :size :paragraph-1 :weight :medium}
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
|
||||
(defn chat-list-item
|
||||
[{:keys [chat-id group-chat color name unviewed-messages-count unviewed-mentions-count
|
||||
timestamp last-message]
|
||||
timestamp last-message muted]
|
||||
:as item}]
|
||||
(let [display-name (if group-chat
|
||||
name
|
||||
@@ -143,8 +143,9 @@
|
||||
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}}
|
||||
(get-in last-message [:content :text])]
|
||||
[render-subheader (get-in last-message [:content :parsed-text])])]
|
||||
(if (> unviewed-mentions-count 0)
|
||||
[quo/info-count {:style {:top 16}}
|
||||
unviewed-mentions-count]
|
||||
(when (> unviewed-messages-count 0)
|
||||
[rn/view {:style (style/count-container)}]))]))
|
||||
(when-not muted
|
||||
(if (> unviewed-mentions-count 0)
|
||||
[quo/info-count {:style {:top 16}}
|
||||
unviewed-mentions-count]
|
||||
(when (> unviewed-messages-count 0)
|
||||
[rn/view {:style (style/count-container)}])))]))
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
(ns status-im2.contexts.quo-preview.links.url-preview-list
|
||||
(:require
|
||||
[quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]
|
||||
utils.number))
|
||||
|
||||
(def descriptor
|
||||
[{:label "Number of previews"
|
||||
:key :previews-length
|
||||
:type :text}])
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
(let [state (reagent/atom {:previews-length "3"})]
|
||||
(fn []
|
||||
(let [previews-length (min 6 (utils.number/parse-int (:previews-length @state)))]
|
||||
[rn/view {:style {:padding-bottom 150}}
|
||||
[preview/customizer state descriptor]
|
||||
[rn/view
|
||||
{:style {:align-items :center
|
||||
:margin-top 50}}
|
||||
[quo/url-preview-list
|
||||
{:horizontal-spacing 20
|
||||
:on-clear #(js/alert "Clear button pressed")
|
||||
:key-fn :url
|
||||
:data (for [index (range previews-length)
|
||||
:let [index (inc index)]]
|
||||
{:title (str "Title " index)
|
||||
:body (str "status.im." index)
|
||||
:logo (resources/get-mock-image :status-logo)
|
||||
:loading? false
|
||||
:url (str "status.im." index)})}]]]))))
|
||||
|
||||
(defn preview
|
||||
[]
|
||||
[rn/view
|
||||
{:style {:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:flex 1}}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
||||
@@ -40,6 +40,7 @@
|
||||
[status-im2.contexts.quo-preview.inputs.profile-input :as profile-input]
|
||||
[status-im2.contexts.quo-preview.inputs.title-input :as title-input]
|
||||
[status-im2.contexts.quo-preview.links.url-preview :as url-preview]
|
||||
[status-im2.contexts.quo-preview.links.url-preview-list :as url-preview-list]
|
||||
[status-im2.contexts.quo-preview.list-items.channel :as channel]
|
||||
[status-im2.contexts.quo-preview.list-items.preview-lists :as preview-lists]
|
||||
[status-im2.contexts.quo-preview.markdown.text :as text]
|
||||
@@ -183,7 +184,10 @@
|
||||
:component title-input/preview-title-input}]
|
||||
:links [{:name :url-preview
|
||||
:options {:insets {:top? true}}
|
||||
:component url-preview/preview}]
|
||||
:component url-preview/preview}
|
||||
{:name :url-preview-list
|
||||
:options {:insets {:top? true}}
|
||||
:component url-preview-list/preview}]
|
||||
:list-items [{:name :channel
|
||||
:insets {:top false}
|
||||
:component channel/preview-channel}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
(ns status-im2.contexts.quo-preview.tags.context-tags
|
||||
(:require [quo2.components.tags.context-tags :as quo2]
|
||||
(:require [quo2.components.tags.context-tag.view :as quo2]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im.multiaccounts.core :as multiaccounts]
|
||||
[status-im2.common.resources :as resources]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]))
|
||||
|
||||
(def group-avatar-default-params
|
||||
@@ -20,7 +20,7 @@
|
||||
(def example-photo2
|
||||
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=")
|
||||
|
||||
(def coinbase-community (resources/get-mock-image :coinbase))
|
||||
(def coinbase-community-image (resources/get-mock-image :coinbase))
|
||||
|
||||
(def main-descriptor
|
||||
[{:label "Type"
|
||||
@@ -37,7 +37,10 @@
|
||||
{:key :audio
|
||||
:value "Audio"}
|
||||
{:key :community
|
||||
:value "Community"}]}])
|
||||
:value "Community"}]}
|
||||
{:label "Blur"
|
||||
:key :blur?
|
||||
:type :boolean}])
|
||||
|
||||
(def context-tag-descriptor
|
||||
[{:label "Label"
|
||||
@@ -51,73 +54,113 @@
|
||||
[]
|
||||
(let [state (reagent/atom {:label "Name"
|
||||
:channel-name "Channel"
|
||||
:type :group-avatar})]
|
||||
:type :group-avatar
|
||||
:duration "00:32"})]
|
||||
(fn []
|
||||
(let [contacts {example-pk {:public-key example-pk
|
||||
:primary-name "Automatic incompatible Coati"
|
||||
:photo example-photo}
|
||||
example-pk2 {:public-key example-pk2
|
||||
:primary-name "Clearcut Flickering Rattlesnake"
|
||||
:photo example-photo2}}
|
||||
(let [contacts {example-pk {:public-key example-pk
|
||||
:primary-name "Automatic incompatible Coati"
|
||||
:photo example-photo}
|
||||
example-pk2 {:public-key example-pk2
|
||||
:primary-name "Clearcut Flickering Rattlesnake"
|
||||
:photo example-photo2}}
|
||||
contacts-public-keys (map (fn [{:keys [public-key]}]
|
||||
{:key public-key
|
||||
:value (get-in contacts [public-key :primary-name])})
|
||||
(vals contacts))
|
||||
current-username (if (seq (:contact @state))
|
||||
(->> @state
|
||||
:contact
|
||||
contacts
|
||||
multiaccounts/displayed-name)
|
||||
"Please select a user")
|
||||
descriptor
|
||||
(cond
|
||||
(= (:type @state) :context-tag) (into main-descriptor context-tag-descriptor)
|
||||
(= (:type @state) :group-avatar) (conj main-descriptor
|
||||
{:label "Label"
|
||||
:key :label
|
||||
:type :text})
|
||||
(= (:type @state) :avatar) (let [photo (-> @state :contact contacts :photo)]
|
||||
(when-not (contains? @state :contacts)
|
||||
(swap! state assoc :contacts contacts-public-keys))
|
||||
(when-not (= (:photo @state)
|
||||
photo)
|
||||
(swap! state assoc :photo photo))
|
||||
(conj main-descriptor
|
||||
{:label "Contacts"
|
||||
:key :contact
|
||||
:type :select
|
||||
:options contacts-public-keys})))]
|
||||
current-username (if (seq (:contact @state))
|
||||
(->> @state
|
||||
:contact
|
||||
contacts
|
||||
multiaccounts/displayed-name)
|
||||
"Please select a user")
|
||||
descriptor (cond
|
||||
(= (:type @state) :context-tag)
|
||||
(into main-descriptor context-tag-descriptor)
|
||||
|
||||
(= (:type @state) :group-avatar)
|
||||
(conj main-descriptor
|
||||
{:label "Label"
|
||||
:key :label
|
||||
:type :text})
|
||||
|
||||
(= (:type @state) :avatar)
|
||||
(let [photo (-> @state :contact contacts :photo)]
|
||||
(when-not (contains? @state :contacts)
|
||||
(swap! state assoc :contacts contacts-public-keys))
|
||||
(when-not (= (:photo @state)
|
||||
photo)
|
||||
(swap! state assoc :photo photo))
|
||||
(conj main-descriptor
|
||||
{:label "Contacts"
|
||||
:key :contact
|
||||
:type :select
|
||||
:options contacts-public-keys}))
|
||||
|
||||
(= (:type @state) :community)
|
||||
(conj main-descriptor
|
||||
{:label "Community name"
|
||||
:key :label
|
||||
:type :text})
|
||||
|
||||
(= (:type @state) :audio)
|
||||
(conj main-descriptor
|
||||
{:label "Duration"
|
||||
:key :duration
|
||||
:type :text})
|
||||
|
||||
:else
|
||||
main-descriptor)]
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[rn/view {:flex 1}
|
||||
[rn/view {:style {:padding-bottom 150}}
|
||||
[rn/view {:style {:flex 1}}
|
||||
[preview/customizer state descriptor]]
|
||||
[rn/view
|
||||
{:padding-vertical 60
|
||||
:flex-direction :row
|
||||
:justify-content :center}
|
||||
(case (:type @state)
|
||||
:context-tag
|
||||
[quo2/context-tag group-avatar-default-params {:uri example-photo2} (:label @state)
|
||||
(:channel-name @state)]
|
||||
:group-avatar
|
||||
[quo2/group-avatar-tag (:label @state) group-avatar-default-params]
|
||||
:public-key
|
||||
[quo2/public-key-tag {} example-pk]
|
||||
:avatar
|
||||
[quo2/user-avatar-tag {} current-username (:photo @state)]
|
||||
:audio
|
||||
[quo2/audio-tag "00:32"]
|
||||
:community
|
||||
[quo2/community-tag coinbase-community "Coinbase"])]]]))))
|
||||
[rn/view {:style {:padding-vertical 60}}
|
||||
[preview/blur-view
|
||||
{:style {:flex 1
|
||||
:margin-vertical 20
|
||||
:margin-horizontal 40}
|
||||
:show-blur-background? (:blur? @state)}
|
||||
(case (:type @state)
|
||||
:context-tag [quo2/context-tag
|
||||
{:blur? (:blur? @state)
|
||||
:size :small
|
||||
:color :purple}
|
||||
{:uri example-photo2}
|
||||
(:label @state)
|
||||
(:channel-name @state)]
|
||||
|
||||
:group-avatar [quo2/group-avatar-tag (:label @state)
|
||||
{:blur? (:blur? @state)
|
||||
:size :small
|
||||
:color :purple}]
|
||||
|
||||
:public-key [quo2/public-key-tag
|
||||
{:blur? (:blur? @state)
|
||||
:size :small
|
||||
:color :purple}
|
||||
example-pk]
|
||||
|
||||
:avatar [quo2/user-avatar-tag
|
||||
{:blur? (:blur? @state)
|
||||
:size :small
|
||||
:color :purple}
|
||||
current-username (:photo @state)]
|
||||
|
||||
:audio [quo2/audio-tag (:duration @state) {:blur? (:blur? @state)}]
|
||||
|
||||
:community [quo2/community-tag
|
||||
coinbase-community-image
|
||||
(:label @state)
|
||||
{:blur? (:blur? @state)}])]]]]))))
|
||||
|
||||
(defn preview-context-tags
|
||||
[]
|
||||
[rn/view
|
||||
{:background-color (colors/theme-colors colors/white
|
||||
colors/neutral-95)
|
||||
:flex 1}
|
||||
{:style {:background-color (colors/theme-colors colors/white
|
||||
colors/neutral-95)
|
||||
:flex 1}}
|
||||
[rn/flat-list
|
||||
{:flex 1
|
||||
{:style {:flex 1}
|
||||
:keyboardShouldPersistTaps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
(re-frame/reg-fx
|
||||
:shell/navigate-from-shell-fx
|
||||
(fn [stack-id]
|
||||
(js/setTimeout #(animation/bottom-tab-on-press stack-id) 500)))
|
||||
(animation/bottom-tab-on-press stack-id)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:shell/reset-bottom-tabs
|
||||
|
||||
@@ -156,17 +156,19 @@
|
||||
(fn [chats]
|
||||
(let [{:keys [chats-stack community-stack]}
|
||||
(reduce
|
||||
(fn [acc [_ {:keys [unviewed-messages-count unviewed-mentions-count chat-type]}]]
|
||||
(fn [acc [_ {:keys [unviewed-messages-count unviewed-mentions-count chat-type muted]}]]
|
||||
(case chat-type
|
||||
constants/community-chat-type
|
||||
(-> acc
|
||||
(update-in [:community-stack :unviewed-messages-count] + unviewed-messages-count)
|
||||
(update-in [:community-stack :unviewed-mentions-count] + unviewed-mentions-count))
|
||||
(when-not muted
|
||||
(-> acc
|
||||
(update-in [:community-stack :unviewed-messages-count] + unviewed-messages-count)
|
||||
(update-in [:community-stack :unviewed-mentions-count] + unviewed-mentions-count)))
|
||||
|
||||
(constants/private-group-chat-type constants/one-to-one-chat-type)
|
||||
(-> acc
|
||||
(update-in [:chats-stack :unviewed-messages-count] + unviewed-messages-count)
|
||||
(update-in [:chats-stack :unviewed-mentions-count] + unviewed-mentions-count))
|
||||
(when-not muted
|
||||
(-> acc
|
||||
(update-in [:chats-stack :unviewed-messages-count] + unviewed-messages-count)
|
||||
(update-in [:chats-stack :unviewed-mentions-count] + unviewed-mentions-count)))
|
||||
|
||||
acc))
|
||||
{:chats-stack {:unviewed-messages-count 0 :unviewed-mentions-count 0}
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
"Pretty-print to STDOUT the current component tree."
|
||||
(with-node-or-screen :debug))
|
||||
|
||||
(def within rtl/within)
|
||||
|
||||
(defn fire-event
|
||||
([event-name node]
|
||||
(fire-event event-name node nil))
|
||||
@@ -188,3 +190,7 @@
|
||||
(defn was-called
|
||||
[mock]
|
||||
(.toHaveBeenCalled (js/expect mock)))
|
||||
|
||||
(defn was-called-times
|
||||
[^js mock number-of-times]
|
||||
(.toHaveBeenCalledTimes (js/expect mock) number-of-times))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.144.0",
|
||||
"commit-sha1": "ba1ba1ac0203948339be5877ea6e970efb7b8ee0",
|
||||
"src-sha256": "0r2gxivnx201zfnm69g2mk1izcjid4lf9s4ln721kwrnc1v288sf"
|
||||
"version": "fix/15616",
|
||||
"commit-sha1": "2eccfda1c9495a3a6ecfd96f24efefff1b53461f",
|
||||
"src-sha256": "0dhibgmfix23nmmg5nhyhg6qr069575mi1hp4gqd4rp54wygigvv"
|
||||
}
|
||||
|
||||
@@ -332,7 +332,6 @@ class TestActivityMultipleDevicePR(MultipleSharedDeviceTestCase):
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(702957)
|
||||
@marks.xfail(reason="Blocked by 15500")
|
||||
def test_activity_center_mentions(self):
|
||||
self.home_1.jump_to_communities_home()
|
||||
self.home_2.jump_to_card_by_text('# %s' % self.channel_name)
|
||||
|
||||
@@ -811,7 +811,7 @@ class ChatView(BaseView):
|
||||
self.set_community_image_button = Button(self.driver, translation_id='community-thumbnail-image',
|
||||
suffix='/following-sibling::android.view.ViewGroup')
|
||||
self.confirm_create_in_community_button = Button(self.driver, translation_id="create")
|
||||
self.mentions_list = BaseElement(self.driver, accessibility_id="mentions-list")
|
||||
self.mentions_list = BaseElement(self.driver, accessibility_id="user-list")
|
||||
|
||||
# New UI
|
||||
self.pinned_messages_count = Button(self.driver,
|
||||
|
||||
@@ -2064,5 +2064,11 @@
|
||||
"enable-biometrics": "Enable biometrics",
|
||||
"use-biometrics": "Use biometrics to fill in your password",
|
||||
"name-must-have-at-least-characters": "Name must have at least {{min-chars}} characters",
|
||||
"name-is-not-valid": "Name is not valid"
|
||||
"name-is-not-valid": "Name is not valid",
|
||||
"mute-for-15-mins": "For 15 min",
|
||||
"mute-for-1-hour": "For 1 hour",
|
||||
"mute-for-8-hours": "For 8 hours",
|
||||
"mute-for-1-week": "For 7 days",
|
||||
"mute-till-unmute": "Until you turn it back on",
|
||||
"mute-channel": "Mute channel"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user