Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d708f44de9 | ||
|
|
eea737625d | ||
|
|
39ea12cf29 | ||
|
|
c5166ac48e | ||
|
|
c5e6bd790d | ||
|
|
b29d248a9b | ||
|
|
396ee208bf | ||
|
|
adb50fa0ee | ||
|
|
5017e13013 | ||
|
|
a6fe626d78 | ||
|
|
8f92fe344a | ||
|
|
19a76c22d1 |
@@ -67,8 +67,8 @@ public class PushNotificationHelper {
|
||||
private static final long DEFAULT_VIBRATION = 300L;
|
||||
private static final String CHANNEL_ID = "status-im-notifications";
|
||||
public static final String ACTION_DELETE_NOTIFICATION = "im.status.ethereum.module.DELETE_NOTIFICATION";
|
||||
public static final String ACTION_TAP_NOTIFICATION = "im.status.ethereum.module.TAP_NOTIFICATION";
|
||||
public static final String ACTION_TAP_STOP = "im.status.ethereum.module.TAP_STOP";
|
||||
final int flag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE : PendingIntent.FLAG_CANCEL_CURRENT;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
|
||||
@@ -119,13 +119,8 @@ public class PushNotificationHelper {
|
||||
private final BroadcastReceiver notificationActionReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction() == ACTION_TAP_NOTIFICATION ||
|
||||
intent.getAction() == ACTION_DELETE_NOTIFICATION) {
|
||||
String deepLink = intent.getExtras().getString("im.status.ethereum.deepLink");
|
||||
if (intent.getAction() == ACTION_DELETE_NOTIFICATION) {
|
||||
String groupId = intent.getExtras().getString("im.status.ethereum.groupId");
|
||||
if (intent.getAction() == ACTION_TAP_NOTIFICATION) {
|
||||
context.startActivity(getOpenAppIntent(deepLink));
|
||||
}
|
||||
if (groupId != null) {
|
||||
cleanGroup(groupId);
|
||||
}
|
||||
@@ -141,7 +136,6 @@ public class PushNotificationHelper {
|
||||
private void registerBroadcastReceiver() {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(ACTION_DELETE_NOTIFICATION);
|
||||
filter.addAction(ACTION_TAP_NOTIFICATION);
|
||||
filter.addAction(ACTION_TAP_STOP);
|
||||
context.registerReceiver(notificationActionReceiver, filter);
|
||||
Log.e(LOG_TAG, "Broadcast Receiver registered");
|
||||
@@ -701,37 +695,31 @@ public class PushNotificationHelper {
|
||||
|
||||
private StatusMessage createMessage(Bundle data) {
|
||||
Person author = getPerson(data.getBundle("notificationAuthor"));
|
||||
return new StatusMessage(data.getString("id"), author, data.getLong("timestamp"), data.getString("message"));
|
||||
long timeStampLongValue = (long) data.getDouble("timestamp");
|
||||
return new StatusMessage(data.getString("id"), author, timeStampLongValue, data.getString("message"));
|
||||
}
|
||||
|
||||
private PendingIntent createGroupOnDismissedIntent(Context context, int notificationId, String groupId, String deepLink) {
|
||||
Intent intent = new Intent(ACTION_DELETE_NOTIFICATION);
|
||||
intent.putExtra("im.status.ethereum.deepLink", deepLink);
|
||||
intent.putExtra("im.status.ethereum.groupId", groupId);
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, flag);
|
||||
}
|
||||
|
||||
private PendingIntent createGroupOnTapIntent(Context context, int notificationId, String groupId, String deepLink) {
|
||||
Intent intent = new Intent(ACTION_TAP_NOTIFICATION);
|
||||
intent.putExtra("im.status.ethereum.deepLink", deepLink);
|
||||
intent.putExtra("im.status.ethereum.groupId", groupId);
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
Intent intent = getOpenAppIntent(deepLink);
|
||||
return PendingIntent.getActivity(context.getApplicationContext(), notificationId, intent, flag);
|
||||
}
|
||||
|
||||
private PendingIntent createOnTapIntent(Context context, int notificationId, String deepLink) {
|
||||
Intent intent = new Intent(ACTION_TAP_NOTIFICATION);
|
||||
intent.putExtra("im.status.ethereum.deepLink", deepLink);
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
Intent intent = getOpenAppIntent(deepLink);
|
||||
return PendingIntent.getActivity(context.getApplicationContext(), notificationId, intent, flag);
|
||||
}
|
||||
|
||||
private PendingIntent createOnDismissedIntent(Context context, int notificationId, String deepLink) {
|
||||
Intent intent = new Intent(ACTION_DELETE_NOTIFICATION);
|
||||
intent.putExtra("im.status.ethereum.deepLink", deepLink);
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE);
|
||||
return PendingIntent.getBroadcast(context.getApplicationContext(), notificationId, intent, flag);
|
||||
}
|
||||
|
||||
public void removeStatusMessage(Bundle bundle) {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 28 KiB |
@@ -1,10 +1,11 @@
|
||||
(ns quo2.components.drawers.drawer-buttons.style
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[react-native.platform :as platform]))
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def outer-container
|
||||
{:height 216
|
||||
:border-radius 20})
|
||||
{:height 216
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20
|
||||
:overflow :hidden})
|
||||
|
||||
(def top-card
|
||||
{:flex 1
|
||||
@@ -12,9 +13,7 @@
|
||||
:padding-horizontal 20
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20
|
||||
:background-color (if platform/ios?
|
||||
colors/neutral-80-opa-80-blur
|
||||
colors/neutral-80)})
|
||||
:background-color colors/neutral-80-opa-80-blur})
|
||||
|
||||
(def bottom-card
|
||||
{:position :absolute
|
||||
@@ -33,11 +32,11 @@
|
||||
:justify-content :space-between})
|
||||
|
||||
(def bottom-icon
|
||||
{:border-radius 40
|
||||
{:border-radius 32
|
||||
:border-width 1
|
||||
:margin-left 24
|
||||
:height 28
|
||||
:width 28
|
||||
:height 32
|
||||
:width 32
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:border-color colors/white-opa-5})
|
||||
@@ -52,4 +51,4 @@
|
||||
(defn heading-text
|
||||
[gap]
|
||||
{:color colors/white
|
||||
:margin-bottom gap})
|
||||
:margin-bottom gap})
|
||||
@@ -71,14 +71,28 @@
|
||||
child-2 string, keyword or hiccup
|
||||
"
|
||||
[{:keys [container-style top-card bottom-card]} child-1 child-2]
|
||||
[blur/ios-view
|
||||
[rn/view
|
||||
{:style (merge container-style style/outer-container)}
|
||||
[card
|
||||
(merge {:gap 4
|
||||
:top? true
|
||||
:style style/top-card}
|
||||
top-card) child-1]
|
||||
[card
|
||||
(merge {:style style/bottom-card
|
||||
:gap 20}
|
||||
bottom-card) child-2]])
|
||||
[blur/view
|
||||
{:blur-type :dark
|
||||
:blur-amount 10
|
||||
:style {:flex 1
|
||||
:border-top-left-radius 20
|
||||
:border-top-right-radius 20}}]
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:background-color :transparent
|
||||
:position :absolute
|
||||
:top 0
|
||||
:left 0
|
||||
:right 0
|
||||
:bottom 0}}
|
||||
[card
|
||||
(merge {:gap 4
|
||||
:top? true
|
||||
:style style/top-card}
|
||||
top-card) child-1]
|
||||
[card
|
||||
(merge {:style style/bottom-card
|
||||
:gap 20}
|
||||
bottom-card) child-2]]])
|
||||
@@ -0,0 +1,30 @@
|
||||
(ns quo2.components.empty-state.empty-state.styles
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(def container
|
||||
{:padding 12
|
||||
:align-items :center})
|
||||
|
||||
(def image
|
||||
{:width 80
|
||||
:height 80})
|
||||
|
||||
(def text-container
|
||||
{:margin-top 12
|
||||
:align-items :center})
|
||||
|
||||
(defn title
|
||||
[blur?]
|
||||
(cond-> {:margin-bottom 2}
|
||||
blur? (assoc :color colors/white)))
|
||||
|
||||
(defn description
|
||||
[blur?]
|
||||
(when blur?
|
||||
{:color colors/white}))
|
||||
|
||||
(def button-container {:margin-top 20})
|
||||
|
||||
(defn upper-button-color
|
||||
[customization-color]
|
||||
(colors/custom-color-by-theme customization-color 50 60))
|
||||
@@ -0,0 +1,50 @@
|
||||
(ns quo2.components.empty-state.empty-state.view
|
||||
(:require [quo2.components.buttons.button :as button]
|
||||
[quo2.components.empty-state.empty-state.styles :as styles]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[react-native.core :as rn]
|
||||
[react-native.fast-image :as fast-image]))
|
||||
|
||||
(defn empty-state
|
||||
[{:keys [customization-color image title description blur?]
|
||||
upper-button :upper-button
|
||||
lower-button :lower-button
|
||||
:or {customization-color :blue}}]
|
||||
[rn/view {:style styles/container}
|
||||
[fast-image/fast-image
|
||||
{:style styles/image
|
||||
:source image}]
|
||||
[rn/view {:style styles/text-container}
|
||||
[text/text
|
||||
{:style (styles/title blur?)
|
||||
:number-of-lines 1
|
||||
:weight :semi-bold
|
||||
:size :paragraph-1}
|
||||
title]
|
||||
[text/text
|
||||
{:style (styles/description blur?)
|
||||
:number-of-lines 1
|
||||
:text-align :center
|
||||
:weight :regular
|
||||
:size :paragraph-2}
|
||||
description]]
|
||||
(when-let [{upper-button-text :text
|
||||
upper-button-on-press :on-press} upper-button]
|
||||
[rn/view {:style styles/button-container}
|
||||
[button/button
|
||||
(cond-> {:type :primary
|
||||
:size 32
|
||||
:override-background-color (styles/upper-button-color customization-color)
|
||||
:on-press upper-button-on-press}
|
||||
blur? (assoc :override-theme :dark))
|
||||
upper-button-text]
|
||||
|
||||
(when-let [{lower-button-text :text
|
||||
lower-button-on-press :on-press} lower-button]
|
||||
[button/button
|
||||
(cond-> {:style {:margin-top 12}
|
||||
:size 32
|
||||
:type :blur-bg
|
||||
:on-press lower-button-on-press}
|
||||
blur? (assoc :override-theme :dark))
|
||||
lower-button-text])])])
|
||||
@@ -1,6 +1,7 @@
|
||||
(ns quo2.components.reactions.reaction
|
||||
(:require [quo2.components.icon :as icons]
|
||||
[quo2.components.markdown.text :as text]
|
||||
[quo2.components.reactions.resource :as resource]
|
||||
[quo2.components.reactions.style :as style]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[quo2.theme :as theme]
|
||||
@@ -28,9 +29,10 @@
|
||||
:on-long-press on-long-press
|
||||
:accessibility-label accessibility-label
|
||||
:style (style/reaction neutral?)}
|
||||
[icons/icon emoji
|
||||
{:no-color true
|
||||
:size 16}]
|
||||
[rn/image
|
||||
{:style {:width 16 :height 16}
|
||||
:accessibility-label :emoji
|
||||
:source (resource/get-reaction emoji)}]
|
||||
[text/text
|
||||
{:size :paragraph-2
|
||||
:weight :semi-bold
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
(ns quo2.components.reactions.resource
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(def ^:private reactions-dir "./resources/images/reactions/")
|
||||
|
||||
(defn- resolve-reaction
|
||||
[reaction]
|
||||
(let [path (str reactions-dir (name reaction) ".png")
|
||||
file (io/file path)]
|
||||
(when (.exists file)
|
||||
`(js/require ~(str "." path)))))
|
||||
|
||||
(defn- find-all-image-base-names
|
||||
[]
|
||||
(let [dir (io/file reactions-dir)]
|
||||
(->> dir
|
||||
file-seq
|
||||
(filter #(string/ends-with? % "png"))
|
||||
(map #(.getName %))
|
||||
(map #(string/replace % #"\.png$" ""))
|
||||
(map #(first (string/split % #"@")))
|
||||
distinct)))
|
||||
|
||||
(defmacro resolve-all-reactions
|
||||
[]
|
||||
(reduce (fn [acc reaction]
|
||||
(assoc acc reaction (resolve-reaction reaction)))
|
||||
{}
|
||||
(find-all-image-base-names)))
|
||||
@@ -0,0 +1,12 @@
|
||||
(ns quo2.components.reactions.resource
|
||||
(:require-macros [quo2.components.reactions.resource :refer [resolve-all-reactions]]))
|
||||
|
||||
(def ^:private reactions
|
||||
(resolve-all-reactions))
|
||||
|
||||
(defn get-reaction
|
||||
[reaction]
|
||||
(assert (keyword? reaction) "Reaction should be a keyword")
|
||||
(assert (= "reaction" (namespace reaction))
|
||||
"Reaction keyword should be namespaced with :reaction")
|
||||
(get reactions (name reaction)))
|
||||
@@ -0,0 +1,23 @@
|
||||
(ns quo2.components.selectors.reactions.component-spec
|
||||
(:require [quo2.components.selectors.reactions.view :as view]
|
||||
[test-helpers.component :as h]))
|
||||
|
||||
(h/describe "Selectors > Reactions"
|
||||
(h/test "renders component"
|
||||
(h/render [view/view :reaction/sad])
|
||||
(h/is-truthy (h/get-by-label-text :reaction)))
|
||||
|
||||
(h/describe "on-press event"
|
||||
(h/test "starts with released state"
|
||||
(let [on-press (h/mock-fn)]
|
||||
(h/render [view/view :reaction/love {:on-press on-press}])
|
||||
(h/fire-event :press (h/get-by-label-text :reaction))
|
||||
(h/was-called on-press)))
|
||||
|
||||
(h/test "starts with pressed state"
|
||||
(let [on-press (h/mock-fn)]
|
||||
(h/render [view/view :reaction/love
|
||||
{:on-press on-press
|
||||
:start-pressed? true}])
|
||||
(h/fire-event :press (h/get-by-label-text :reaction))
|
||||
(h/was-called on-press)))))
|
||||
@@ -0,0 +1,11 @@
|
||||
(ns quo2.components.selectors.reactions.style
|
||||
(:require [quo2.foundations.colors :as colors]))
|
||||
|
||||
(defn container
|
||||
[pressed?]
|
||||
{:padding 10
|
||||
:border-radius 12
|
||||
:border-width 1
|
||||
:border-color (colors/theme-colors colors/neutral-20 colors/neutral-80)
|
||||
:background-color (when pressed?
|
||||
(colors/theme-colors colors/neutral-10 colors/neutral-80-opa-40))})
|
||||
@@ -0,0 +1,25 @@
|
||||
(ns quo2.components.selectors.reactions.view
|
||||
(:require [quo2.components.reactions.resource :as reactions.resource]
|
||||
[quo2.components.selectors.reactions.style :as style]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]))
|
||||
|
||||
(defn view
|
||||
[_ {:keys [start-pressed?]}]
|
||||
(let [pressed? (reagent/atom start-pressed?)]
|
||||
(fn [emoji
|
||||
{:keys [container-style on-press
|
||||
accessibility-label]
|
||||
:or {accessibility-label :reaction}}]
|
||||
[rn/touchable-without-feedback
|
||||
{:accessibility-label accessibility-label
|
||||
:on-press (fn [e]
|
||||
(swap! pressed? not)
|
||||
(when on-press
|
||||
(on-press e)))}
|
||||
[rn/view
|
||||
{:style (merge (style/container @pressed?)
|
||||
container-style)}
|
||||
[rn/image
|
||||
{:source (reactions.resource/get-reaction emoji)
|
||||
:style {:width 20 :height 20}}]]])))
|
||||
@@ -30,6 +30,7 @@
|
||||
quo2.components.drawers.permission-context.view
|
||||
quo2.components.dropdowns.dropdown
|
||||
quo2.components.header
|
||||
quo2.components.empty-state.empty-state.view
|
||||
quo2.components.icon
|
||||
quo2.components.info.info-message
|
||||
quo2.components.info.information-box
|
||||
@@ -64,6 +65,7 @@
|
||||
quo2.components.profile.profile-card.view
|
||||
quo2.components.profile.select-profile.view
|
||||
quo2.components.reactions.reaction
|
||||
quo2.components.selectors.reactions.view
|
||||
quo2.components.record-audio.record-audio.view
|
||||
quo2.components.record-audio.soundtrack.view
|
||||
quo2.components.selectors.disclaimer.view
|
||||
@@ -110,6 +112,9 @@
|
||||
(def skeleton quo2.components.loaders.skeleton/skeleton)
|
||||
(def author quo2.components.messages.author.view/author)
|
||||
|
||||
;;;; SELECTORS
|
||||
(def reactions quo2.components.selectors.reactions.view/view)
|
||||
|
||||
;;;; AVATAR
|
||||
(def account-avatar quo2.components.avatars.account-avatar/account-avatar)
|
||||
(def channel-avatar quo2.components.avatars.channel-avatar/channel-avatar)
|
||||
@@ -164,6 +169,9 @@
|
||||
(def drawer-buttons quo2.components.drawers.drawer-buttons.view/view)
|
||||
(def permission-context quo2.components.drawers.permission-context.view/view)
|
||||
|
||||
;;;; EMPTY STATE
|
||||
(def empty-state quo2.components.empty-state.empty-state.view/empty-state)
|
||||
|
||||
;;;; INPUTS
|
||||
(def input quo2.components.inputs.input.view/input)
|
||||
(def profile-input quo2.components.inputs.profile-input.view/profile-input)
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
[quo2.components.drawers.drawer-buttons.component-spec]
|
||||
[quo2.components.drawers.permission-context.component-spec]
|
||||
[quo2.components.inputs.input.component-spec]
|
||||
[quo2.components.keycard.component-spec]
|
||||
[quo2.components.inputs.profile-input.component-spec]
|
||||
[quo2.components.inputs.recovery-phrase.component-spec]
|
||||
[quo2.components.inputs.title-input.component-spec]
|
||||
[quo2.components.keycard.component-spec]
|
||||
[quo2.components.links.link-preview.component-spec]
|
||||
[quo2.components.links.url-preview-list.component-spec]
|
||||
[quo2.components.links.url-preview.component-spec]
|
||||
[quo2.components.links.link-preview.component-spec]
|
||||
[quo2.components.markdown.--tests--.text-component-spec]
|
||||
[quo2.components.notifications.notification.component-spec]
|
||||
[quo2.components.onboarding.small-option-card.component-spec]
|
||||
@@ -30,6 +30,7 @@
|
||||
[quo2.components.record-audio.soundtrack.--tests--.soundtrack-component-spec]
|
||||
[quo2.components.selectors.disclaimer.component-spec]
|
||||
[quo2.components.selectors.filter.component-spec]
|
||||
[quo2.components.selectors.reactions.component-spec]
|
||||
[quo2.components.selectors.selectors.component-spec]
|
||||
[quo2.components.share.share-qr-code.component-spec]
|
||||
[quo2.components.tags.--tests--.status-tags-component-spec]))
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
(rf/defn messages-loaded
|
||||
"Loads more messages for current chat"
|
||||
{:events [::messages-loaded]}
|
||||
[{db :db} chat-id session-id {:keys [cursor messages]}]
|
||||
[{db :db} chat-id session-id {:keys [cursor messages]} on-loaded]
|
||||
(when-not (and (get-in db [:pagination-info chat-id :messages-initialized?])
|
||||
(not= session-id
|
||||
(get-in db [:pagination-info chat-id :messages-initialized?])))
|
||||
@@ -141,6 +141,8 @@
|
||||
[:pagination-info chat-id
|
||||
:cursor-clock-value])
|
||||
clock-value (when cursor (cursor->clock-value cursor))]
|
||||
(when on-loaded
|
||||
(on-loaded (count new-messages)))
|
||||
{:db (-> db
|
||||
(update-in [:pagination-info chat-id :cursor-clock-value]
|
||||
#(if (and (seq cursor) (or (not %) (< clock-value %)))
|
||||
@@ -161,7 +163,7 @@
|
||||
|
||||
(rf/defn load-more-messages
|
||||
{:events [:chat.ui/load-more-messages]}
|
||||
[{:keys [db]} chat-id first-request]
|
||||
[{:keys [db]} chat-id first-request on-loaded]
|
||||
(when-let [session-id (get-in db [:pagination-info chat-id :messages-initialized?])]
|
||||
(when (and
|
||||
(not (get-in db [:pagination-info chat-id :all-loaded?]))
|
||||
@@ -175,13 +177,13 @@
|
||||
chat-id
|
||||
cursor
|
||||
constants/default-number-of-messages
|
||||
#(re-frame/dispatch [::messages-loaded chat-id session-id %])
|
||||
#(re-frame/dispatch [::messages-loaded chat-id session-id % on-loaded])
|
||||
#(re-frame/dispatch [::failed-loading-messages chat-id session-id %]))))))))
|
||||
|
||||
(rf/defn load-more-messages-for-current-chat
|
||||
{:events [:chat.ui/load-more-messages-for-current-chat]}
|
||||
[{:keys [db] :as cofx}]
|
||||
(load-more-messages cofx (:current-chat-id db) false))
|
||||
[{:keys [db] :as cofx} on-loaded]
|
||||
(load-more-messages cofx (:current-chat-id db) false on-loaded))
|
||||
|
||||
(rf/defn load-messages
|
||||
[{:keys [db now] :as cofx} chat-id]
|
||||
@@ -191,4 +193,4 @@
|
||||
:utils/dispatch-later [{:ms 50 :dispatch [:chat.ui/mark-all-read-pressed chat-id]}
|
||||
(when-not (get-in cofx [:db :chats chat-id :public?])
|
||||
{:ms 100 :dispatch [:pin-message/load-pin-messages chat-id]})]}
|
||||
(load-more-messages chat-id true))))
|
||||
(load-more-messages chat-id true nil))))
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.contexts.chat.messages.link-preview.events :as link-preview]
|
||||
[taoensso.timbre :as log]
|
||||
[status-im2.constants :as constants]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(rf/defn status-node-started
|
||||
[{db :db :as cofx} {:keys [error]}]
|
||||
@@ -56,7 +55,7 @@
|
||||
:peers-count (count (:peers peer-stats)))}))
|
||||
|
||||
(rf/defn handle-local-pairing-signals
|
||||
[{:keys [db] :as cofx} {:keys [type action data error] :as event}]
|
||||
[{:keys [db] :as cofx} {:keys [type action data] :as event}]
|
||||
(log/info "local pairing signal received"
|
||||
{:event event})
|
||||
(let [{:keys [account password]} data
|
||||
@@ -79,7 +78,7 @@
|
||||
(and (some? account) (some? password)))
|
||||
multiaccount-data (when received-account?
|
||||
(merge account {:password password}))
|
||||
navigate-to-syncing-devices? (and (or connection-success? error-on-pairing?) receiver?)
|
||||
navigate-to-syncing-devices? (and connection-success? receiver?)
|
||||
user-in-syncing-devices-screen? (= (:view-id db) :syncing-progress)]
|
||||
(merge {:db (cond-> db
|
||||
connection-success?
|
||||
@@ -93,21 +92,12 @@
|
||||
|
||||
completed-pairing?
|
||||
(assoc-in [:syncing :pairing-status] :completed))}
|
||||
(cond
|
||||
(and navigate-to-syncing-devices? (not user-in-syncing-devices-screen?))
|
||||
{:dispatch [:navigate-to :syncing-progress]}
|
||||
|
||||
(and completed-pairing? sender?)
|
||||
{:dispatch [:syncing/clear-states]}
|
||||
|
||||
(and completed-pairing? receiver?)
|
||||
{:dispatch [:multiaccounts.login/local-paired-user]}
|
||||
|
||||
error-on-pairing?
|
||||
{:dispatch [:toasts/upsert
|
||||
{:icon :i/alert
|
||||
:icon-color colors/danger-50
|
||||
:text error}]}))))
|
||||
(when (and navigate-to-syncing-devices? (not user-in-syncing-devices-screen?))
|
||||
{:dispatch [:navigate-to :syncing-progress]})
|
||||
(when (and completed-pairing? sender?)
|
||||
{:dispatch [:syncing/clear-states]})
|
||||
(when (and completed-pairing? receiver?)
|
||||
{:dispatch [:multiaccounts.login/local-paired-user]}))))
|
||||
|
||||
(rf/defn process
|
||||
{:events [:signals/signal-received]}
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
:discover (js/require "../resources/images/ui2/discover.png")
|
||||
:invite-friends (js/require "../resources/images/ui2/invite-friends.png")
|
||||
:no-contacts-light (js/require "../resources/images/ui2/no-contacts-light.png")
|
||||
:no-contacts-dark (js/require "../resources/images/ui2/no-contacts-dark.png")})
|
||||
:no-contacts-dark (js/require "../resources/images/ui2/no-contacts-dark.png")
|
||||
:no-messages-light (js/require "../resources/images/ui2/no-messages-light.png")
|
||||
:no-messages-dark (js/require "../resources/images/ui2/no-messages-dark.png")})
|
||||
|
||||
(def mock-images
|
||||
{:coinbase (js/require "../resources/images/mock2/coinbase.png")
|
||||
|
||||
@@ -71,12 +71,12 @@
|
||||
(def request-to-join-pending-state 1)
|
||||
|
||||
(def reactions
|
||||
{emoji-reaction-love :i/love
|
||||
emoji-reaction-thumbs-up :i/thumbs-up
|
||||
emoji-reaction-thumbs-down :i/thumbs-down
|
||||
emoji-reaction-laugh :i/laugh
|
||||
emoji-reaction-sad :i/sad
|
||||
emoji-reaction-angry :i/angry})
|
||||
{emoji-reaction-love :reaction/love
|
||||
emoji-reaction-thumbs-up :reaction/thumbs-up
|
||||
emoji-reaction-thumbs-down :reaction/thumbs-down
|
||||
emoji-reaction-laugh :reaction/laugh
|
||||
emoji-reaction-sad :reaction/sad
|
||||
emoji-reaction-angry :reaction/angry})
|
||||
|
||||
(def ^:const invitation-state-unknown 0)
|
||||
(def ^:const invitation-state-requested 1)
|
||||
|
||||
@@ -15,24 +15,37 @@
|
||||
[status-im2.contexts.chat.lightbox.constants :as constants]
|
||||
[utils.worklets.lightbox :as worklet]))
|
||||
|
||||
(defn clear-timers
|
||||
[timers]
|
||||
(js/clearTimeout (:mount-animation @timers))
|
||||
(js/clearTimeout (:mount-index-lock @timers))
|
||||
(js/clearTimeout (:hide-0 @timers))
|
||||
(js/clearTimeout (:hide-1 @timers))
|
||||
(js/clearTimeout (:show-0 @timers))
|
||||
(js/clearTimeout (:show-1 @timers))
|
||||
(js/clearTimeout (:show-2 @timers)))
|
||||
|
||||
(defn effect
|
||||
[{:keys [flat-list-ref scroll-index-lock?]} {:keys [opacity layout border]} index]
|
||||
(rn/use-effect (fn []
|
||||
(reagent/next-tick (fn []
|
||||
(when @flat-list-ref
|
||||
(.scrollToIndex ^js @flat-list-ref
|
||||
#js {:animated false :index index}))))
|
||||
(js/setTimeout (fn []
|
||||
(anim/animate opacity 1)
|
||||
(anim/animate layout 0)
|
||||
(anim/animate border 12))
|
||||
(if platform/ios? 250 100))
|
||||
(js/setTimeout #(reset! scroll-index-lock? false) 300)
|
||||
(fn []
|
||||
(rf/dispatch [:chat.ui/zoom-out-signal nil])
|
||||
(when platform/android?
|
||||
(rf/dispatch [:chat.ui/lightbox-scale 1]))))))
|
||||
[{:keys [flat-list-ref scroll-index-lock? timers]} {:keys [opacity layout border]} index]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reagent/next-tick (fn []
|
||||
(when @flat-list-ref
|
||||
(.scrollToIndex ^js @flat-list-ref
|
||||
#js {:animated false :index index}))))
|
||||
(swap! timers assoc
|
||||
:mount-animation
|
||||
(js/setTimeout (fn []
|
||||
(anim/animate opacity 1)
|
||||
(anim/animate layout 0)
|
||||
(anim/animate border 12))
|
||||
(if platform/ios? 250 100)))
|
||||
(swap! timers assoc :mount-index-lock (js/setTimeout #(reset! scroll-index-lock? false) 300))
|
||||
(fn []
|
||||
(rf/dispatch [:chat.ui/zoom-out-signal nil])
|
||||
(when platform/android?
|
||||
(rf/dispatch [:chat.ui/lightbox-scale 1]))
|
||||
(clear-timers timers)))))
|
||||
|
||||
(defn handle-orientation
|
||||
[result {:keys [flat-list-ref]} {:keys [scroll-index]} animations]
|
||||
@@ -102,7 +115,7 @@
|
||||
(anim/animate opacity 0)
|
||||
(rf/dispatch [:navigate-back]))
|
||||
(do
|
||||
#(reset! set-full-height? true)
|
||||
(reset! set-full-height? true)
|
||||
(anim/animate (if x? pan-x pan-y) 0)
|
||||
(anim/animate opacity 1)
|
||||
(anim/animate layout 0)))))))
|
||||
@@ -111,7 +124,8 @@
|
||||
[]
|
||||
{:flat-list-ref (atom nil)
|
||||
:small-list-ref (atom nil)
|
||||
:scroll-index-lock? (atom true)})
|
||||
:scroll-index-lock? (atom true)
|
||||
:timers (atom {})})
|
||||
|
||||
(defn init-state
|
||||
[messages index]
|
||||
|
||||
@@ -88,7 +88,8 @@
|
||||
:screen-width screen-width
|
||||
:window-height window-height
|
||||
:window-width window-width
|
||||
:props props}
|
||||
:props props
|
||||
:curr-orientation curr-orientation}
|
||||
:horizontal horizontal?
|
||||
:inverted inverted?
|
||||
:paging-enabled true
|
||||
@@ -113,7 +114,8 @@
|
||||
(on-viewable-items-changed e props state))]
|
||||
(anim/animate (:background-color animations) "rgba(0,0,0,1)")
|
||||
(reset! (:data state) messages)
|
||||
(utils/orientation-change props state animations)
|
||||
(when platform/ios? ; issue: https://github.com/wix/react-native-navigation/issues/7726
|
||||
(utils/orientation-change props state animations))
|
||||
(utils/effect props animations index)
|
||||
[:f> lightbox-content props state animations derived messages index callback]))))
|
||||
|
||||
|
||||
@@ -107,23 +107,34 @@
|
||||
|
||||
(defn toggle-opacity
|
||||
[index {:keys [opacity-value border-value transparent? props]} portrait?]
|
||||
(let [{:keys [small-list-ref]} props
|
||||
opacity (reanimated/get-shared-value opacity-value)]
|
||||
(let [{:keys [small-list-ref timers]} props
|
||||
opacity (reanimated/get-shared-value opacity-value)]
|
||||
(if (= opacity 1)
|
||||
(do
|
||||
(when platform/ios?
|
||||
;; status-bar issue: https://github.com/status-im/status-mobile/issues/15343
|
||||
(js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible false}}) 75))
|
||||
(js/clearTimeout (:show-0 @timers))
|
||||
(js/clearTimeout (:show-1 @timers))
|
||||
(js/clearTimeout (:show-2 @timers))
|
||||
(swap! timers assoc
|
||||
:hide-0
|
||||
(js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible false}})
|
||||
(if platform/ios? 75 0)))
|
||||
(anim/animate opacity-value 0)
|
||||
(js/setTimeout #(reset! transparent? (not @transparent?)) 400))
|
||||
(swap! timers assoc :hide-1 (js/setTimeout #(reset! transparent? (not @transparent?)) 400)))
|
||||
(do
|
||||
(js/clearTimeout (:hide-0 @timers))
|
||||
(js/clearTimeout (:hide-1 @timers))
|
||||
(reset! transparent? (not @transparent?))
|
||||
(js/setTimeout #(anim/animate opacity-value 1) 50)
|
||||
(js/setTimeout #(when @small-list-ref
|
||||
(.scrollToIndex ^js @small-list-ref #js {:animated false :index index}))
|
||||
100)
|
||||
(when (and platform/ios? portrait?)
|
||||
(js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible true}}) 150))))
|
||||
(swap! timers assoc :show-0 (js/setTimeout #(anim/animate opacity-value 1) 50))
|
||||
(swap! timers assoc
|
||||
:show-1
|
||||
(js/setTimeout #(when @small-list-ref
|
||||
(.scrollToIndex ^js @small-list-ref #js {:animated false :index index}))
|
||||
100))
|
||||
(when portrait?
|
||||
(swap! timers assoc
|
||||
:show-2
|
||||
(js/setTimeout #(navigation/merge-options "lightbox" {:statusBar {:visible true}})
|
||||
(if platform/ios? 150 50))))))
|
||||
(anim/animate border-value (if (= opacity 1) 0 12))))
|
||||
|
||||
;;; Dimensions
|
||||
|
||||
@@ -228,36 +228,35 @@
|
||||
:style (style/image dimensions animations (:border-value render-data))}]]]))
|
||||
|
||||
(defn zoomable-image
|
||||
[{:keys [image-width image-height content message-id]} index render-data]
|
||||
(let [state (utils/init-state)
|
||||
shared-element-id (rf/sub [:shared-element-id])
|
||||
exit-lightbox-signal (rf/sub [:lightbox/exit-signal])
|
||||
zoom-out-signal (rf/sub [:lightbox/zoom-out-signal])
|
||||
curr-orientation (or (rf/sub [:lightbox/orientation])
|
||||
orientation/portrait)
|
||||
{:keys [set-full-height?]} render-data
|
||||
focused? (= shared-element-id message-id)
|
||||
dimensions (utils/get-dimensions
|
||||
(or image-width c/default-dimension)
|
||||
(or image-height c/default-duration)
|
||||
curr-orientation
|
||||
render-data)
|
||||
animations (utils/init-animations)
|
||||
rescale (fn [value exit?]
|
||||
(utils/rescale-image value
|
||||
exit?
|
||||
dimensions
|
||||
animations
|
||||
state))]
|
||||
(rn/use-effect (fn []
|
||||
(js/setTimeout #(reset! set-full-height? true) 500)))
|
||||
(when platform/ios?
|
||||
(utils/handle-orientation-change curr-orientation focused? dimensions animations state)
|
||||
(utils/handle-exit-lightbox-signal exit-lightbox-signal
|
||||
index
|
||||
(anim/get-val (:scale animations))
|
||||
rescale
|
||||
set-full-height?))
|
||||
(utils/handle-zoom-out-signal zoom-out-signal index (anim/get-val (:scale animations)) rescale)
|
||||
[:f> f-zoomable-image dimensions animations state rescale curr-orientation content focused?
|
||||
index render-data]))
|
||||
[]
|
||||
(let [state (utils/init-state)]
|
||||
(fn [{:keys [image-width image-height content message-id]} index render-data]
|
||||
(let [shared-element-id (rf/sub [:shared-element-id])
|
||||
exit-lightbox-signal (rf/sub [:lightbox/exit-signal])
|
||||
zoom-out-signal (rf/sub [:lightbox/zoom-out-signal])
|
||||
{:keys [set-full-height? curr-orientation]} render-data
|
||||
focused? (= shared-element-id message-id)
|
||||
dimensions (utils/get-dimensions
|
||||
(or image-width c/default-dimension)
|
||||
(or image-height c/default-duration)
|
||||
curr-orientation
|
||||
render-data)
|
||||
animations (utils/init-animations)
|
||||
rescale (fn [value exit?]
|
||||
(utils/rescale-image value
|
||||
exit?
|
||||
dimensions
|
||||
animations
|
||||
state))]
|
||||
(rn/use-effect (fn []
|
||||
(js/setTimeout (fn [] (reset! set-full-height? true)) 500)))
|
||||
(when platform/ios?
|
||||
(utils/handle-orientation-change curr-orientation focused? dimensions animations state)
|
||||
(utils/handle-exit-lightbox-signal exit-lightbox-signal
|
||||
index
|
||||
(anim/get-val (:scale animations))
|
||||
rescale
|
||||
set-full-height?))
|
||||
(utils/handle-zoom-out-signal zoom-out-signal index (anim/get-val (:scale animations)) rescale)
|
||||
[:f> f-zoomable-image dimensions animations state rescale curr-orientation content focused?
|
||||
index render-data]))))
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
(let [show-delivery-state? (reagent/atom false)]
|
||||
(fn [{:keys [content-type quoted-message content outgoing outgoing-status] :as message-data}
|
||||
context
|
||||
keyboard-shown
|
||||
keyboard-shown?
|
||||
message-reaction-view?]
|
||||
(let [first-image (first (:album message-data))
|
||||
outgoing-status (if (= content-type constants/content-type-album)
|
||||
@@ -105,7 +105,7 @@
|
||||
:style {:border-radius 16
|
||||
:opacity (if (and outgoing (= outgoing-status :sending)) 0.5 1)}
|
||||
:on-press (fn []
|
||||
(if (and platform/ios? @keyboard-shown)
|
||||
(if (and platform/ios? keyboard-shown?)
|
||||
(rn/dismiss-keyboard!)
|
||||
(when (and outgoing
|
||||
(not= outgoing-status :sending)
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
[react-native.core :as rn]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im2.contexts.chat.composer.reply.view :as reply]
|
||||
[status-im2.common.not-implemented :as not-implemented]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.i18n :as i18n]
|
||||
[utils.re-frame :as rf]
|
||||
@@ -175,29 +174,23 @@
|
||||
:padding-horizontal 30
|
||||
:padding-top 5
|
||||
:padding-bottom 15}}
|
||||
(doall
|
||||
(for [[id icon] constants/reactions]
|
||||
(let [emoji-reaction-id (get own-reactions id)]
|
||||
^{:key id}
|
||||
[not-implemented/not-implemented
|
||||
[quo/button
|
||||
(merge
|
||||
{:size 40
|
||||
:type (if emoji-reaction-id :grey :ghost)
|
||||
:icon true
|
||||
:icon-no-color true
|
||||
:accessibility-label (str "emoji-picker-" id)
|
||||
:on-press (fn []
|
||||
(if emoji-reaction-id
|
||||
(rf/dispatch [:models.reactions/send-emoji-reaction-retraction
|
||||
{:message-id message-id
|
||||
:emoji-id id
|
||||
:emoji-reaction-id emoji-reaction-id}])
|
||||
(rf/dispatch [:models.reactions/send-emoji-reaction
|
||||
{:message-id message-id
|
||||
:emoji-id id}]))
|
||||
(rf/dispatch [:hide-bottom-sheet]))})
|
||||
icon]])))]))
|
||||
(for [[id reaction-name] constants/reactions
|
||||
:let [emoji-reaction-id (get own-reactions id)]]
|
||||
^{:key id}
|
||||
[quo/reactions reaction-name
|
||||
{:start-pressed? (boolean emoji-reaction-id)
|
||||
:accessibility-label (str "reaction-" (name reaction-name))
|
||||
:on-press
|
||||
(fn []
|
||||
(if emoji-reaction-id
|
||||
(rf/dispatch [:models.reactions/send-emoji-reaction-retraction
|
||||
{:message-id message-id
|
||||
:emoji-id id
|
||||
:emoji-reaction-id emoji-reaction-id}])
|
||||
(rf/dispatch [:models.reactions/send-emoji-reaction
|
||||
{:message-id message-id
|
||||
:emoji-id id}]))
|
||||
(rf/dispatch [:hide-bottom-sheet]))}])]))
|
||||
|
||||
(defn reactions-and-actions
|
||||
[message-data
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
(ns status-im2.contexts.chat.messages.list.style
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defonce ^:const cover-height 168)
|
||||
(defonce ^:const overscroll-cover-height 2000)
|
||||
(defonce ^:const header-avatar-top-offset -36)
|
||||
(defonce ^:const messages-list-bottom-offset 16)
|
||||
|
||||
(defn keyboard-avoiding-container
|
||||
[{:keys [top]}]
|
||||
{:position :relative
|
||||
:flex 1
|
||||
:top (- top)
|
||||
:margin-bottom (- top)})
|
||||
|
||||
(def list-container
|
||||
{:padding-vertical 16})
|
||||
|
||||
(defn header-container
|
||||
[show?]
|
||||
{:display (if show? :flex :none)
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:top (- overscroll-cover-height)
|
||||
:margin-bottom (- overscroll-cover-height)})
|
||||
|
||||
(defn header-cover
|
||||
[cover-bg-color]
|
||||
{:flex 1
|
||||
:height (+ overscroll-cover-height cover-height)
|
||||
:background-color cover-bg-color})
|
||||
|
||||
(defn header-bottom-part
|
||||
[animation]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:border-top-right-radius animation
|
||||
:border-top-left-radius animation}
|
||||
{:top -16
|
||||
:margin-bottom -16
|
||||
:padding-bottom 24
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-100)}))
|
||||
|
||||
(def header-avatar
|
||||
{:top header-avatar-top-offset
|
||||
:margin-horizontal 20
|
||||
:margin-bottom header-avatar-top-offset})
|
||||
|
||||
(defn header-image
|
||||
[scale-animation top-margin-animation side-margin-animation]
|
||||
(reanimated/apply-animations-to-style
|
||||
{:transform [{:scale scale-animation}]
|
||||
:margin-top top-margin-animation
|
||||
:margin-left side-margin-animation
|
||||
:margin-bottom side-margin-animation}
|
||||
{:align-items :flex-start}))
|
||||
|
||||
(def name-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def bio
|
||||
{:margin-top 8})
|
||||
@@ -3,8 +3,12 @@
|
||||
[quo2.core :as quo]
|
||||
[react-native.background-timer :as background-timer]
|
||||
[react-native.core :as rn]
|
||||
[react-native.hooks :as hooks]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[status-im.ui.screens.chat.group :as chat.group]
|
||||
[status-im.ui.screens.chat.message.gap :as message.gap]
|
||||
[status-im2.common.not-implemented :as not-implemented]
|
||||
@@ -12,21 +16,35 @@
|
||||
[status-im2.contexts.chat.messages.content.deleted.view :as content.deleted]
|
||||
[status-im2.contexts.chat.messages.content.view :as message]
|
||||
[status-im2.contexts.chat.messages.list.state :as state]
|
||||
[utils.re-frame :as rf]
|
||||
[status-im2.contexts.chat.composer.constants :as composer.constants]))
|
||||
[status-im2.contexts.chat.messages.list.style :as style]
|
||||
[status-im2.contexts.chat.messages.navigation.style :as navigation.style]
|
||||
[status-im2.contexts.chat.composer.constants :as composer.constants]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defonce ^:const threshold-percentage-to-show-floating-scroll-down-button 75)
|
||||
(defonce ^:const loading-indicator-extra-spacing 250)
|
||||
(defonce ^:const loading-indicator-page-loading-height 100)
|
||||
(defonce ^:const scroll-animation-input-range [50 125])
|
||||
(defonce ^:const spacing-between-composer-and-content 64)
|
||||
|
||||
(defonce messages-list-ref (atom nil))
|
||||
(defonce messages-view-height (reagent/atom 0))
|
||||
(defonce messages-view-header-height (reagent/atom 0))
|
||||
(defonce show-floating-scroll-down-button (reagent/atom false))
|
||||
|
||||
(defn list-key-fn [{:keys [message-id value]}] (or message-id value))
|
||||
(defn list-ref [ref] (reset! messages-list-ref ref))
|
||||
|
||||
(defn scroll-to-offset
|
||||
[position]
|
||||
(some-> ^js @messages-list-ref
|
||||
(.scrollToOffset #js
|
||||
{:offset position
|
||||
:animated true})))
|
||||
|
||||
(defn scroll-to-bottom
|
||||
[]
|
||||
(some-> ^js @messages-list-ref
|
||||
(.scrollToOffset #js {:y 0 :animated true})))
|
||||
|
||||
(defonce ^:const threshold-percentage-to-show-floating-scroll-down-button 75)
|
||||
(defonce show-floating-scroll-down-button (reagent/atom false))
|
||||
(scroll-to-offset (- 0 style/messages-list-bottom-offset)))
|
||||
|
||||
(defn on-scroll
|
||||
[evt]
|
||||
@@ -40,11 +58,11 @@
|
||||
(reset! show-floating-scroll-down-button reached-threshold?))))
|
||||
|
||||
(defn on-viewable-items-changed
|
||||
[evt]
|
||||
[e]
|
||||
(when @messages-list-ref
|
||||
(reset! state/first-not-visible-item
|
||||
(when-let [last-visible-element (aget (oops/oget evt "viewableItems")
|
||||
(dec (oops/oget evt "viewableItems.length")))]
|
||||
(when-let [last-visible-element (aget (oops/oget e "viewableItems")
|
||||
(dec (oops/oget e "viewableItems.length")))]
|
||||
(let [index (oops/oget last-visible-element "index")
|
||||
;; Get first not visible element, if it's a datemark/gap
|
||||
;; we might unnecessarely add messages on receiving as
|
||||
@@ -55,43 +73,164 @@
|
||||
(= :message (:type first-not-visible)))
|
||||
first-not-visible))))))
|
||||
|
||||
;;TODO this is not really working in pair with inserting new messages because we stop inserting new
|
||||
;;messages
|
||||
;;if they outside the viewarea, but we load more here because end is reached,so its slowdown UI because
|
||||
;;we
|
||||
;;load and render 20 messages more, but we can't prevent this , because otherwise :on-end-reached will
|
||||
;;work wrong
|
||||
|
||||
(defn list-on-end-reached
|
||||
[]
|
||||
(if @state/scrolling
|
||||
(rf/dispatch [:chat.ui/load-more-messages-for-current-chat])
|
||||
(background-timer/set-timeout #(rf/dispatch [:chat.ui/load-more-messages-for-current-chat])
|
||||
(if platform/low-device? 700 200))))
|
||||
[scroll-y]
|
||||
;; FIXME: that's a bit of a hack but we need to update `scroll-y` once the new messages
|
||||
;; are fetched in order for the header to work properly
|
||||
(let [on-loaded (fn [n]
|
||||
(reanimated/set-shared-value scroll-y
|
||||
(+ (reanimated/get-shared-value scroll-y)
|
||||
(* n 200))))]
|
||||
(if @state/scrolling
|
||||
(rf/dispatch [:chat.ui/load-more-messages-for-current-chat on-loaded])
|
||||
(background-timer/set-timeout #(rf/dispatch [:chat.ui/load-more-messages-for-current-chat
|
||||
on-loaded])
|
||||
(if platform/low-device? 700 100)))))
|
||||
|
||||
(defonce messages-view-height (reagent/atom 0))
|
||||
(defn contact-icon
|
||||
[{:keys [ens-verified added?]}]
|
||||
(when (or ens-verified added?)
|
||||
[rn/view
|
||||
{:style {:padding-left 10
|
||||
:margin-top 2}}
|
||||
(if ens-verified
|
||||
[quo/icon :i/verified
|
||||
{:no-color true
|
||||
:size 20
|
||||
:color (colors/theme-colors colors/success-50 colors/success-60)}]
|
||||
(when added?
|
||||
[quo/icon :i/contact
|
||||
{:no-color true
|
||||
:size 20
|
||||
:color (colors/theme-colors colors/primary-50 colors/primary-60)}]))]))
|
||||
|
||||
(defn on-messages-view-layout
|
||||
[evt]
|
||||
(reset! messages-view-height (oops/oget evt "nativeEvent.layout.height")))
|
||||
(def header-extrapolation-option
|
||||
{:extrapolateLeft "clamp"
|
||||
:extrapolateRight "clamp"})
|
||||
|
||||
(defn list-footer
|
||||
[{:keys [chat-id]}]
|
||||
(let [loading-messages? (rf/sub [:chats/loading-messages? chat-id])
|
||||
all-loaded? (rf/sub [:chats/all-loaded? chat-id])]
|
||||
(when (or loading-messages? (not chat-id) (not all-loaded?))
|
||||
[rn/view {:style (when platform/android? {:scaleY -1})}
|
||||
[quo/skeleton @messages-view-height]])))
|
||||
(defn loading-view
|
||||
[chat-id]
|
||||
(let [loading-messages? (rf/sub [:chats/loading-messages? chat-id])
|
||||
all-loaded? (rf/sub [:chats/all-loaded? chat-id])
|
||||
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
|
||||
loading-first-page? (= (count messages) 0)
|
||||
top-spacing (if loading-first-page? 0 navigation.style/navigation-bar-height)]
|
||||
(when (or loading-messages? (not all-loaded?))
|
||||
[rn/view {:padding-top top-spacing}
|
||||
[quo/skeleton
|
||||
(if loading-first-page?
|
||||
(- @messages-view-height
|
||||
@messages-view-header-height
|
||||
composer.constants/composer-default-height
|
||||
loading-indicator-extra-spacing)
|
||||
loading-indicator-page-loading-height)]])))
|
||||
|
||||
(defn list-header
|
||||
[{:keys [chat-id chat-type invitation-admin]}]
|
||||
(when (= chat-type constants/private-group-chat-type)
|
||||
[rn/view {:style (when platform/android? {:scaleY -1})}
|
||||
[chat.group/group-chat-footer chat-id invitation-admin]]))
|
||||
[insets]
|
||||
[rn/view
|
||||
{:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:margin-bottom (- 0
|
||||
(:top insets)
|
||||
(when platform/ios? style/overscroll-cover-height))
|
||||
:height (+ composer.constants/composer-default-height
|
||||
(:bottom insets)
|
||||
spacing-between-composer-and-content
|
||||
(when platform/ios? style/overscroll-cover-height))}])
|
||||
|
||||
(defn f-list-footer-avatar
|
||||
[{:keys [scroll-y display-name online? photo-path]}]
|
||||
(let [image-scale-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[1 0.5]
|
||||
header-extrapolation-option)
|
||||
image-top-margin-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[0 40]
|
||||
header-extrapolation-option)
|
||||
image-side-margin-animation (reanimated/interpolate scroll-y
|
||||
scroll-animation-input-range
|
||||
[0 -20]
|
||||
header-extrapolation-option)]
|
||||
[reanimated/view
|
||||
{:style (style/header-image image-scale-animation
|
||||
image-top-margin-animation
|
||||
image-side-margin-animation)}
|
||||
[quo/user-avatar
|
||||
{:full-name display-name
|
||||
:online? online?
|
||||
:profile-picture photo-path
|
||||
:size :big}]]))
|
||||
|
||||
(defn list-footer-avatar
|
||||
[props]
|
||||
[:f> f-list-footer-avatar props])
|
||||
|
||||
(defn f-list-footer
|
||||
[{:keys [chat scroll-y cover-bg-color on-layout]}]
|
||||
(let [{:keys [chat-id chat-name emoji chat-type
|
||||
group-chat]} chat
|
||||
all-loaded? (rf/sub [:chats/all-loaded? chat-id])
|
||||
display-name (if (= chat-type constants/one-to-one-chat-type)
|
||||
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
|
||||
(str emoji " " chat-name))
|
||||
{:keys [bio]} (rf/sub [:contacts/contact-by-identity chat-id])
|
||||
online? (rf/sub [:visibility-status-updates/online? chat-id])
|
||||
contact (when-not group-chat
|
||||
(rf/sub [:contacts/contact-by-address chat-id]))
|
||||
photo-path (when-not (empty? (:images contact))
|
||||
(rf/sub [:chats/photo-path chat-id]))
|
||||
border-animation (reanimated/interpolate scroll-y
|
||||
[30 125]
|
||||
[14 0]
|
||||
header-extrapolation-option)]
|
||||
[rn/view {:flex 1}
|
||||
[rn/view
|
||||
{:style (style/header-container all-loaded?)
|
||||
:on-layout on-layout}
|
||||
(when cover-bg-color
|
||||
[rn/view {:style (style/header-cover cover-bg-color)}])
|
||||
[reanimated/view {:style (style/header-bottom-part border-animation)}
|
||||
[rn/view {:style style/header-avatar}
|
||||
[rn/view {:style {:align-items :flex-start}}
|
||||
(when-not group-chat
|
||||
[list-footer-avatar
|
||||
{:scroll-y scroll-y
|
||||
:display-name display-name
|
||||
:online? online?
|
||||
:profile-picture photo-path}])]
|
||||
[rn/view {:style style/name-container}
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
:size :heading-1
|
||||
:style {:margin-top (if group-chat 54 12)}
|
||||
:number-of-lines 1}
|
||||
display-name
|
||||
[contact-icon contact]]]
|
||||
(when bio
|
||||
[quo/text {:style style/bio}
|
||||
bio])]]]
|
||||
[loading-view chat-id]]))
|
||||
|
||||
(defn list-footer
|
||||
[props]
|
||||
[:f> f-list-footer props])
|
||||
|
||||
(defn list-group-chat-header
|
||||
[{:keys [chat-id invitation-admin]}]
|
||||
[rn/view
|
||||
[chat.group/group-chat-footer chat-id invitation-admin]])
|
||||
|
||||
(defn footer-on-layout
|
||||
[e]
|
||||
(let [height (oops/oget e "nativeEvent.layout.height")
|
||||
y (oops/oget e "nativeEvent.layout.y")]
|
||||
(reset! messages-view-header-height (+ height y))))
|
||||
|
||||
(defn render-fn
|
||||
[{:keys [type value deleted? deleted-for-me? content-type] :as message-data} _ _
|
||||
{:keys [context keyboard-shown]}]
|
||||
[rn/view {:style (when platform/android? {:scaleY -1})}
|
||||
{:keys [context keyboard-shown?]}]
|
||||
[rn/view {:background-color (colors/theme-colors colors/white colors/neutral-95)}
|
||||
(if (= type :datemark)
|
||||
[quo/divider-date value]
|
||||
(if (= content-type constants/content-type-gap)
|
||||
@@ -100,71 +239,95 @@
|
||||
[rn/view {:padding-horizontal 8}
|
||||
(if (or deleted? deleted-for-me?)
|
||||
[content.deleted/deleted-message message-data context]
|
||||
[message/message-with-reactions message-data context keyboard-shown])]))])
|
||||
[message/message-with-reactions message-data context keyboard-shown?])]))])
|
||||
|
||||
(defn scroll-handler
|
||||
[event scroll-y]
|
||||
(let [content-size-y (- (oops/oget event "nativeEvent.contentSize.height")
|
||||
(oops/oget event "nativeEvent.layoutMeasurement.height"))
|
||||
current-y (oops/oget event "nativeEvent.contentOffset.y")]
|
||||
(reanimated/set-shared-value scroll-y (- content-size-y current-y))))
|
||||
|
||||
(defn messages-list-content
|
||||
[{:keys [chat-id] :as chat} insets keyboard-shown]
|
||||
(fn []
|
||||
(let [context (rf/sub [:chats/current-chat-message-list-view-context])
|
||||
messages (rf/sub [:chats/raw-chat-messages-stream chat-id])
|
||||
recording? (rf/sub [:chats/recording?])]
|
||||
[rn/view
|
||||
{:style {:flex 1}}
|
||||
;; NOTE: DO NOT use anonymous functions for handlers
|
||||
[rn/flat-list
|
||||
{:key-fn list-key-fn
|
||||
:ref list-ref
|
||||
:header [list-header chat]
|
||||
:footer [list-footer chat]
|
||||
:data messages
|
||||
:render-data {:context context
|
||||
:keyboard-shown keyboard-shown}
|
||||
:render-fn render-fn
|
||||
:on-viewable-items-changed on-viewable-items-changed
|
||||
:on-end-reached list-on-end-reached
|
||||
:on-scroll-to-index-failed identity ; don't remove this
|
||||
:content-container-style {:padding-top (+ composer.constants/composer-default-height
|
||||
(:bottom insets)
|
||||
32)
|
||||
:padding-bottom 16}
|
||||
:scroll-indicator-insets {:top (+ composer.constants/composer-default-height
|
||||
(:bottom insets))}
|
||||
:keyboard-dismiss-mode :interactive
|
||||
:keyboard-should-persist-taps :handled
|
||||
:on-momentum-scroll-begin state/start-scrolling
|
||||
:on-momentum-scroll-end state/stop-scrolling
|
||||
:scroll-event-throttle 16
|
||||
:on-scroll on-scroll
|
||||
;; TODO https://github.com/facebook/react-native/issues/30034
|
||||
:inverted (when platform/ios? true)
|
||||
:style (when platform/android? {:scaleY -1})
|
||||
:on-layout on-messages-view-layout
|
||||
:scroll-enabled (not recording?)}]])))
|
||||
[{:keys [chat insets scroll-y cover-bg-color keyboard-shown?]}]
|
||||
(let [context (rf/sub [:chats/current-chat-message-list-view-context])
|
||||
messages (rf/sub [:chats/raw-chat-messages-stream (:chat-id chat)])
|
||||
recording? (rf/sub [:chats/recording?])
|
||||
all-loaded? (rf/sub [:chats/all-loaded? (:chat-id chat)])]
|
||||
[rn/view {:style {:flex 1}}
|
||||
[rn/flat-list
|
||||
{:key-fn list-key-fn
|
||||
:ref list-ref
|
||||
:header [:<>
|
||||
(when (= (:chat-type chat) constants/private-group-chat-type)
|
||||
[list-group-chat-header chat])
|
||||
[list-header insets]]
|
||||
:footer [list-footer
|
||||
{:chat chat
|
||||
:scroll-y scroll-y
|
||||
:cover-bg-color cover-bg-color
|
||||
:on-layout footer-on-layout}]
|
||||
:data messages
|
||||
:render-data {:context context
|
||||
:keyboard-shown? keyboard-shown?}
|
||||
:render-fn render-fn
|
||||
:on-viewable-items-changed on-viewable-items-changed
|
||||
:on-end-reached #(list-on-end-reached scroll-y)
|
||||
:on-scroll-to-index-failed identity
|
||||
:content-container-style {:padding-bottom style/messages-list-bottom-offset}
|
||||
:scroll-indicator-insets {:top (- composer.constants/composer-default-height 16)}
|
||||
:keyboard-dismiss-mode :interactive
|
||||
:keyboard-should-persist-taps :handled
|
||||
:on-momentum-scroll-begin state/start-scrolling
|
||||
:on-momentum-scroll-end state/stop-scrolling
|
||||
:scroll-event-throttle 16
|
||||
:on-scroll (fn [event]
|
||||
(scroll-handler event scroll-y)
|
||||
(when on-scroll
|
||||
(on-scroll event)))
|
||||
:style {:background-color (if all-loaded?
|
||||
cover-bg-color
|
||||
(colors/theme-colors colors/white
|
||||
colors/neutral-95))}
|
||||
:inverted true
|
||||
:on-layout (fn [e]
|
||||
(when platform/android?
|
||||
;; FIXME: this is due to Android not triggering the initial
|
||||
;; scrollTo event
|
||||
(scroll-to-offset 1))
|
||||
(let [layout-height (oops/oget e "nativeEvent.layout.height")]
|
||||
(reset! messages-view-height layout-height)))
|
||||
:scroll-enabled (not recording?)}]]))
|
||||
|
||||
;; This should be replaced with keyboard hook. It has to do with flat-list probably. The keyboard-shown
|
||||
;; value updates in the parent component, but does not get passed to the children.
|
||||
;; When using listeners and resetting the value on an atom it works.
|
||||
(defn use-keyboard-visibility
|
||||
[]
|
||||
(let [show-listener (atom nil)
|
||||
hide-listener (atom nil)
|
||||
shown? (atom nil)]
|
||||
(defn f-messages-list
|
||||
[{:keys [chat cover-bg-color header-comp footer-comp]}]
|
||||
(let [insets (safe-area/get-insets)
|
||||
scroll-y (reanimated/use-shared-value 0)
|
||||
{:keys [keyboard-height keyboard-shown]} (hooks/use-keyboard)]
|
||||
(rn/use-effect
|
||||
(fn []
|
||||
(reset! show-listener
|
||||
(.addListener rn/keyboard "keyboardWillShow" #(reset! shown? true)))
|
||||
(reset! hide-listener
|
||||
(.addListener rn/keyboard "keyboardWillHide" #(reset! shown? false)))
|
||||
(fn []
|
||||
(.remove ^js @show-listener)
|
||||
(.remove ^js @hide-listener))))
|
||||
{:shown? shown?}))
|
||||
(when keyboard-shown
|
||||
(reanimated/set-shared-value scroll-y
|
||||
(+ (reanimated/get-shared-value scroll-y)
|
||||
keyboard-height))))
|
||||
[keyboard-shown keyboard-height])
|
||||
[rn/keyboard-avoiding-view
|
||||
{:style (style/keyboard-avoiding-container insets)
|
||||
:keyboard-vertical-offset (- (:bottom insets))}
|
||||
|
||||
(defn- f-messages-list
|
||||
[chat insets]
|
||||
(let [{keyboard-shown? :shown?} (use-keyboard-visibility)]
|
||||
[messages-list-content chat insets keyboard-shown?]))
|
||||
(when header-comp
|
||||
[header-comp {:scroll-y scroll-y}])
|
||||
|
||||
[messages-list-content
|
||||
{:chat chat
|
||||
:insets insets
|
||||
:scroll-y scroll-y
|
||||
:cover-bg-color cover-bg-color
|
||||
:keyboard-shown? keyboard-shown}]
|
||||
|
||||
(when footer-comp
|
||||
(footer-comp {:insets insets}))]))
|
||||
|
||||
(defn messages-list
|
||||
[chat insets]
|
||||
[:f> f-messages-list chat insets])
|
||||
[props]
|
||||
[:f> f-messages-list props])
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
(ns status-im2.contexts.chat.messages.navigation.style
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[react-native.platform :as platform]
|
||||
[react-native.reanimated :as reanimated]))
|
||||
|
||||
(defonce ^:const navigation-bar-height 100)
|
||||
(defonce ^:const header-offset 56)
|
||||
|
||||
(defn button-container
|
||||
[position]
|
||||
(merge
|
||||
{:width 32
|
||||
:height 32
|
||||
:border-radius 10
|
||||
:justify-content :center
|
||||
:align-items :center
|
||||
:background-color (colors/theme-colors colors/white-opa-40 colors/neutral-80-opa-40)}
|
||||
position))
|
||||
|
||||
(defn blur-view
|
||||
[status-bar-height]
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:left 0
|
||||
:right 0
|
||||
:height (- navigation-bar-height
|
||||
(if platform/ios? 0 status-bar-height))
|
||||
:display :flex
|
||||
:flex-direction :row
|
||||
:overflow :hidden})
|
||||
|
||||
(defn animated-blur-view
|
||||
[enabled? animation status-bar-height]
|
||||
(reanimated/apply-animations-to-style
|
||||
(when enabled?
|
||||
{:opacity animation})
|
||||
(blur-view status-bar-height)))
|
||||
|
||||
(def navigation-view
|
||||
{:z-index 4})
|
||||
|
||||
(defn header-container
|
||||
[status-bar-height]
|
||||
{:position :absolute
|
||||
:top (- header-offset
|
||||
(if platform/ios? 0 status-bar-height))
|
||||
:left 0
|
||||
:right 0
|
||||
:padding-bottom 8
|
||||
:display :flex
|
||||
:flex-direction :row
|
||||
:overflow :hidden})
|
||||
|
||||
(def header
|
||||
{:flex 1})
|
||||
|
||||
(defn animated-header
|
||||
[enabled? y-animation opacity-animation]
|
||||
(reanimated/apply-animations-to-style
|
||||
;; here using `top` won't work on Android, so we are using `translateY`
|
||||
(when enabled?
|
||||
{:transform [{:translateY y-animation}]
|
||||
:opacity opacity-animation})
|
||||
header))
|
||||
|
||||
(defn pinned-banner
|
||||
[status-bar-height]
|
||||
{:position :absolute
|
||||
:left 0
|
||||
:right 0
|
||||
:top (- navigation-bar-height
|
||||
(if platform/ios? 0 status-bar-height))})
|
||||
|
||||
(defn animated-pinned-banner
|
||||
[enabled? animation status-bar-height]
|
||||
(reanimated/apply-animations-to-style
|
||||
(when enabled?
|
||||
{:opacity animation})
|
||||
(pinned-banner status-bar-height)))
|
||||
|
||||
(def header-content-container
|
||||
{:flex-direction :row
|
||||
:align-items :center
|
||||
:margin-left 8
|
||||
:margin-right 8
|
||||
:margin-top -4
|
||||
:height 40})
|
||||
|
||||
(def header-avatar-container
|
||||
{:margin-right 8})
|
||||
|
||||
(def header-text-container
|
||||
{:flex 1})
|
||||
|
||||
(defn header-display-name
|
||||
[]
|
||||
{:color (colors/theme-colors colors/black colors/white)})
|
||||
|
||||
(defn header-online
|
||||
[]
|
||||
{:color (colors/theme-colors colors/neutral-80-opa-50 colors/white-opa-50)})
|
||||
@@ -0,0 +1,105 @@
|
||||
(ns status-im2.contexts.chat.messages.navigation.view
|
||||
(:require [quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[re-frame.db]
|
||||
[react-native.core :as rn]
|
||||
[react-native.reanimated :as reanimated]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[status-im2.contexts.chat.messages.navigation.style :as style]
|
||||
[status-im2.contexts.chat.messages.pin.banner.view :as pin.banner]
|
||||
[status-im2.constants :as constants]
|
||||
[utils.re-frame :as rf]
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(defn f-navigation-view
|
||||
[{:keys [scroll-y]}]
|
||||
(let [insets (safe-area/get-insets)
|
||||
status-bar-height (:top insets)
|
||||
{:keys [group-chat chat-id chat-name emoji
|
||||
chat-type]} (rf/sub [:chats/current-chat-chat-view])
|
||||
all-loaded? (rf/sub [:chats/all-loaded? chat-id])
|
||||
display-name (if (= chat-type constants/one-to-one-chat-type)
|
||||
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
|
||||
(str emoji " " chat-name))
|
||||
online? (rf/sub [:visibility-status-updates/online? chat-id])
|
||||
contact (when-not group-chat
|
||||
(rf/sub [:contacts/contact-by-address chat-id]))
|
||||
photo-path (when-not (empty? (:images contact))
|
||||
(rf/sub [:chats/photo-path chat-id]))
|
||||
opacity-animation (reanimated/interpolate scroll-y
|
||||
[style/navigation-bar-height
|
||||
(+ style/navigation-bar-height 30)]
|
||||
[0 1]
|
||||
{:extrapolateLeft "clamp"
|
||||
:extrapolateRight "extend"})
|
||||
banner-opacity-animation (reanimated/interpolate scroll-y
|
||||
[(+ style/navigation-bar-height 50)
|
||||
(+ style/navigation-bar-height 100)]
|
||||
[0 1]
|
||||
{:extrapolateLeft "clamp"
|
||||
:extrapolateRight "clamp"})
|
||||
translate-animation (reanimated/interpolate scroll-y
|
||||
[(+ style/navigation-bar-height 25)
|
||||
(+ style/navigation-bar-height 100)]
|
||||
[50 0]
|
||||
{:extrapolateLeft "clamp"
|
||||
:extrapolateRight "clamp"})
|
||||
title-opacity-animation (reanimated/interpolate scroll-y
|
||||
[0 50]
|
||||
[0 1]
|
||||
{:extrapolateLeft "clamp"
|
||||
:extrapolateRight "clamp"})]
|
||||
[rn/view {:style style/navigation-view}
|
||||
[reanimated/blur-view
|
||||
{:blurAmount 32
|
||||
:blurType (colors/theme-colors :xlight :dark)
|
||||
:overlayColor :transparent
|
||||
:style (style/animated-blur-view all-loaded? opacity-animation status-bar-height)}]
|
||||
|
||||
[rn/view
|
||||
[rn/view {:style (style/header-container status-bar-height)}
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:on-press #(rf/dispatch [:navigate-back])
|
||||
:accessibility-label :back-button
|
||||
:style (style/button-container {:margin-left 20})}
|
||||
[quo/icon :i/arrow-left
|
||||
{:size 20 :color (colors/theme-colors colors/black colors/white)}]]
|
||||
[reanimated/view
|
||||
{:style (style/animated-header all-loaded? translate-animation title-opacity-animation)}
|
||||
[rn/view {:style style/header-content-container}
|
||||
(when-not group-chat
|
||||
[rn/view {:style style/header-avatar-container}
|
||||
[quo/user-avatar
|
||||
{:full-name display-name
|
||||
:online? online?
|
||||
:profile-picture photo-path
|
||||
:size :small}]])
|
||||
[rn/view {:style style/header-text-container}
|
||||
[rn/view {:style {:flex-direction :row}}
|
||||
[quo/text
|
||||
{:weight :semi-bold
|
||||
:size :paragraph-1
|
||||
:number-of-lines 1
|
||||
:style (style/header-display-name)}
|
||||
display-name]]
|
||||
(when online?
|
||||
[quo/text
|
||||
{:number-of-lines 1
|
||||
:weight :regular
|
||||
:size :paragraph-2
|
||||
:style (style/header-online)}
|
||||
(i18n/label :t/online)])]]]
|
||||
[rn/touchable-opacity
|
||||
{:active-opacity 1
|
||||
:style (style/button-container {:margin-right 20})
|
||||
:accessibility-label :options-button}
|
||||
[quo/icon :i/options {:size 20 :color (colors/theme-colors colors/black colors/white)}]]]
|
||||
|
||||
[reanimated/view
|
||||
{:style (style/animated-pinned-banner all-loaded? banner-opacity-animation status-bar-height)}
|
||||
[pin.banner/banner chat-id]]]]))
|
||||
|
||||
(defn navigation-view
|
||||
[props]
|
||||
[:f> f-navigation-view props])
|
||||
@@ -1,17 +1,14 @@
|
||||
(ns status-im2.contexts.chat.messages.view
|
||||
(:require [quo2.core :as quo]
|
||||
(:require [quo2.foundations.colors :as colors]
|
||||
[re-frame.db]
|
||||
[react-native.core :as rn]
|
||||
[react-native.safe-area :as safe-area]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.chat.composer.view :as composer]
|
||||
[status-im2.contexts.chat.messages.contact-requests.bottom-drawer :as
|
||||
contact-requests.bottom-drawer]
|
||||
[status-im2.contexts.chat.messages.list.view :as messages.list]
|
||||
[status-im2.contexts.chat.messages.pin.banner.view :as pin.banner]
|
||||
[status-im2.contexts.chat.messages.navigation.view :as messages.navigation]
|
||||
[status-im2.navigation.state :as navigation.state]
|
||||
[utils.debounce :as debounce]
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(defn navigate-back-handler
|
||||
@@ -24,62 +21,23 @@
|
||||
;; and will call system back button action
|
||||
true))
|
||||
|
||||
(defn page-nav
|
||||
[]
|
||||
(let [{:keys [group-chat chat-id chat-name emoji
|
||||
chat-type]} (rf/sub [:chats/current-chat])
|
||||
display-name (if (= chat-type constants/one-to-one-chat-type)
|
||||
(first (rf/sub [:contacts/contact-two-names-by-identity chat-id]))
|
||||
(str emoji " " chat-name))
|
||||
online? (rf/sub [:visibility-status-updates/online? chat-id])
|
||||
contact (when-not group-chat
|
||||
(rf/sub [:contacts/contact-by-address chat-id]))
|
||||
photo-path (rf/sub [:chats/photo-path chat-id])
|
||||
avatar-image-key (if (seq (:images contact))
|
||||
:profile-picture
|
||||
:ring-background)]
|
||||
[quo/page-nav
|
||||
{:align-mid? true
|
||||
:mid-section (if group-chat
|
||||
{:type :text-only
|
||||
:main-text display-name}
|
||||
{:type :user-avatar
|
||||
:avatar {:full-name display-name
|
||||
:online? online?
|
||||
:size :medium
|
||||
avatar-image-key photo-path}
|
||||
:main-text display-name
|
||||
:on-press #(debounce/dispatch-and-chill [:chat.ui/show-profile chat-id]
|
||||
1000)})
|
||||
|
||||
:left-section {:on-press #(do
|
||||
(rf/dispatch [:chat/close])
|
||||
(rf/dispatch [:navigate-back]))
|
||||
:icon :i/arrow-left
|
||||
:accessibility-label :back-button}
|
||||
|
||||
:right-section-buttons [{:on-press #()
|
||||
:style {:border-width 1
|
||||
:border-color :red}
|
||||
:icon :i/options
|
||||
:accessibility-label :options-button}]}]))
|
||||
|
||||
(defn chat-render
|
||||
[]
|
||||
(let [;;NOTE: we want to react only on these fields, do not use full chat map here
|
||||
{:keys [chat-id contact-request-state group-chat able-to-send-message?] :as chat}
|
||||
(rf/sub [:chats/current-chat-chat-view])
|
||||
insets (safe-area/get-insets)]
|
||||
[rn/keyboard-avoiding-view
|
||||
{:style {:position :relative :flex 1}
|
||||
:keyboardVerticalOffset (- (:bottom insets))}
|
||||
[page-nav]
|
||||
[pin.banner/banner chat-id]
|
||||
[messages.list/messages-list chat insets]
|
||||
(if-not able-to-send-message?
|
||||
[contact-requests.bottom-drawer/view chat-id contact-request-state group-chat]
|
||||
[:f> composer/composer insets])]))
|
||||
|
||||
(let [{:keys [chat-id
|
||||
contact-request-state
|
||||
group-chat
|
||||
able-to-send-message?]
|
||||
:as chat} (rf/sub [:chats/current-chat-chat-view])]
|
||||
[messages.list/messages-list
|
||||
{:cover-bg-color (colors/custom-color :turquoise 50 20)
|
||||
:chat chat
|
||||
:header-comp (fn [{:keys [scroll-y]}]
|
||||
[messages.navigation/navigation-view {:scroll-y scroll-y}])
|
||||
:footer-comp (fn [{:keys [insets]}]
|
||||
[rn/view
|
||||
(if-not able-to-send-message?
|
||||
[contact-requests.bottom-drawer/view chat-id contact-request-state group-chat]
|
||||
[:f> composer/composer insets])])}]))
|
||||
|
||||
(defn chat
|
||||
[]
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
[content-width]
|
||||
[rn/image
|
||||
{:style {:resize-mode :stretch
|
||||
:margin-top 32
|
||||
:width content-width}
|
||||
:source (resources/get-image :onboarding-illustration)}])
|
||||
|
||||
|
||||
@@ -1,60 +1,23 @@
|
||||
(ns status-im2.contexts.onboarding.intro.style
|
||||
(:require
|
||||
[react-native.platform :as platform]
|
||||
[quo2.foundations.colors :as colors]))
|
||||
|
||||
(def progress-bar-container
|
||||
{:background-color :transparent
|
||||
:flex-direction :row
|
||||
:margin-vertical 16})
|
||||
|
||||
(defn progress-bar-item
|
||||
[index position end?]
|
||||
{:height 2
|
||||
:flex 1
|
||||
:border-width 1
|
||||
:border-color (if (= index position) colors/white colors/white-opa-10)
|
||||
:margin-right (if end? 0 8)})
|
||||
|
||||
(def carousel
|
||||
{:height 92
|
||||
;; (padding-top) This insets issue needs a consistent implementation across all screens.
|
||||
:padding-top (if platform/android? 0 44)
|
||||
:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0
|
||||
:z-index 2
|
||||
:background-color :transparent
|
||||
:padding-vertical 12
|
||||
:padding-horizontal 20})
|
||||
|
||||
(def carousel-text
|
||||
{:background-color :transparent
|
||||
:color colors/white})
|
||||
|
||||
(def page-container
|
||||
{:flex 1
|
||||
:justify-content :flex-end})
|
||||
|
||||
(def page-image
|
||||
{:position :absolute
|
||||
:top 0
|
||||
:bottom 0
|
||||
:left 0
|
||||
:right 0
|
||||
:width "100%"
|
||||
:aspect-ratio 1})
|
||||
|
||||
(def text-container
|
||||
{:flex 1
|
||||
:max-width 180
|
||||
:flex-wrap :wrap})
|
||||
|
||||
(def plain-text
|
||||
{:flex 1
|
||||
:color (colors/alpha colors/white 0.7)})
|
||||
{:size :paragraph-2
|
||||
:weight :regular
|
||||
:color colors/white-opa-70})
|
||||
|
||||
(def highlighted-text
|
||||
{:flex 1
|
||||
:color colors/white})
|
||||
{:flex 1
|
||||
:size :paragraph-2
|
||||
:weight :regular
|
||||
:color colors/white})
|
||||
|
||||
@@ -21,17 +21,15 @@
|
||||
(rf/dispatch [:hide-terms-of-services-opt-in-screen]))
|
||||
:heading (i18n/label :t/new-to-status)
|
||||
:accessibility-label :new-to-status-button}}
|
||||
(i18n/label :t/you-already-use-status)
|
||||
[quo/text
|
||||
{:style style/plain-text}
|
||||
(i18n/label :t/you-already-use-status)]
|
||||
[quo/text
|
||||
{:style style/text-container}
|
||||
[quo/text
|
||||
{:size :paragraph-2
|
||||
:style style/plain-text
|
||||
:weight :semi-bold}
|
||||
{:style style/plain-text}
|
||||
(i18n/label :t/by-continuing-you-accept)]
|
||||
[quo/text
|
||||
{:on-press #(rf/dispatch [:open-modal :privacy-policy])
|
||||
:size :paragraph-2
|
||||
:style style/highlighted-text
|
||||
:weight :semi-bold}
|
||||
:style style/highlighted-text}
|
||||
(i18n/label :t/terms-of-service)]]]])
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
(ns status-im2.contexts.quo-preview.empty-state.empty-state
|
||||
(:require [quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]
|
||||
[status-im2.common.resources :as resources]))
|
||||
|
||||
(def descriptor
|
||||
[{:label "Title:"
|
||||
:key :title
|
||||
:type :text}
|
||||
{:label "Description:"
|
||||
:key :description
|
||||
:type :text}
|
||||
{:label "Image:"
|
||||
:key :image
|
||||
:type :select
|
||||
:options [{:key :no-contacts-light
|
||||
:value "No contacts light"}
|
||||
{:key :no-contacts-dark
|
||||
:value "No contacts dark"}
|
||||
{:key :no-messages-light
|
||||
:value "No messages light"}
|
||||
{:key :no-messages-dark
|
||||
:value "No messages dark"}]}
|
||||
{:label "Upper button text"
|
||||
:key :upper-button-text
|
||||
:type :text}
|
||||
{:label "Lower button text"
|
||||
:key :lower-button-text
|
||||
:type :text}
|
||||
{:label "Blur:"
|
||||
:key :blur?
|
||||
:type :boolean}])
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
(let [state (reagent/atom {:image :no-messages-light
|
||||
:title "A big friendly title"
|
||||
:description "Some cool description will be here"
|
||||
:blur? false
|
||||
:upper-button-text "Send community link"
|
||||
:lower-button-text "Invite friends to Status"})]
|
||||
(fn []
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[preview/customizer state descriptor]
|
||||
[rn/view
|
||||
{:style {:margin-vertical 24
|
||||
:background-color (when (:blur? @state) colors/neutral-95)}}
|
||||
[preview/blur-view
|
||||
{:style {:width "100%"
|
||||
:align-items :center
|
||||
:top (if (:blur? @state) 32 16)
|
||||
:position (if (:blur? @state)
|
||||
:absolute
|
||||
:relative)}
|
||||
:height 300
|
||||
:show-blur-background? (:blur? @state)
|
||||
:blur-view-props (when (:blur? @state)
|
||||
{:overlay-color colors/neutral-80-opa-80})}
|
||||
|
||||
[rn/view {:style {:flex 1 :width "100%"}}
|
||||
[quo/empty-state
|
||||
(-> @state
|
||||
(assoc :upper-button
|
||||
{:text (:upper-button-text @state)
|
||||
:on-press #(js/alert "Upper button")})
|
||||
(assoc :lower-button
|
||||
{:text (:lower-button-text @state)
|
||||
:on-press #(js/alert "Lower button")})
|
||||
(update :image resources/get-image))]]]]]])))
|
||||
|
||||
(defn preview-empty-state
|
||||
[]
|
||||
[rn/view
|
||||
{:style {:flex 1
|
||||
:background-color (colors/theme-colors colors/white colors/neutral-95)}}
|
||||
[rn/flat-list
|
||||
{:style {:flex 1}
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
||||
@@ -13,6 +13,7 @@
|
||||
[status-im2.contexts.quo-preview.avatars.group-avatar :as group-avatar]
|
||||
[status-im2.contexts.quo-preview.avatars.icon-avatar :as icon-avatar]
|
||||
[status-im2.contexts.quo-preview.avatars.user-avatar :as user-avatar]
|
||||
[status-im2.contexts.quo-preview.selectors.reactions :as selector-reactions]
|
||||
[status-im2.contexts.quo-preview.avatars.wallet-user-avatar :as wallet-user-avatar]
|
||||
[status-im2.contexts.quo-preview.banners.banner :as banner]
|
||||
[status-im2.contexts.quo-preview.buttons.button :as button]
|
||||
@@ -82,6 +83,7 @@
|
||||
[status-im2.contexts.quo-preview.tabs.account-selector :as account-selector]
|
||||
[status-im2.contexts.quo-preview.tabs.segmented-tab :as segmented]
|
||||
[status-im2.contexts.quo-preview.tabs.tabs :as tabs]
|
||||
[status-im2.contexts.quo-preview.empty-state.empty-state :as empty-state]
|
||||
[status-im2.contexts.quo-preview.tags.context-tags :as context-tags]
|
||||
[status-im2.contexts.quo-preview.tags.permission-tag :as permission-tag]
|
||||
[status-im2.contexts.quo-preview.tags.status-tags :as status-tags]
|
||||
@@ -186,6 +188,9 @@
|
||||
:dropdowns [{:name :dropdown
|
||||
:options {:topBar {:visible true}}
|
||||
:component dropdown/preview-dropdown}]
|
||||
:empty-state [{:name :empty-state
|
||||
:options {:topBar {:visible true}}
|
||||
:component empty-state/preview-empty-state}]
|
||||
:info [{:name :info-message
|
||||
:options {:topBar {:visible true}}
|
||||
:component info-message/preview-info-message}
|
||||
@@ -296,7 +301,10 @@
|
||||
:component filter/preview}
|
||||
{:name :selectors
|
||||
:options {:topBar {:visible true}}
|
||||
:component selectors/preview-selectors}]
|
||||
:component selectors/preview-selectors}
|
||||
{:name :select-reactions
|
||||
:options {:topBar {:visible true}}
|
||||
:component selector-reactions/preview}]
|
||||
:settings [{:name :privacy-option
|
||||
:options {:topBar {:visible true}}
|
||||
:component privacy-option/preview-options}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
(ns status-im2.contexts.quo-preview.reactions.react
|
||||
(:require [quo2.components.reactions.reaction :as quo2.reaction]
|
||||
(:require [clojure.string :as string]
|
||||
[quo2.components.reactions.reaction :as quo2.reaction]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[reagent.core :as reagent]
|
||||
[status-im2.constants :as constants]
|
||||
[status-im2.contexts.quo-preview.preview :as preview]))
|
||||
|
||||
(def descriptor
|
||||
@@ -12,29 +14,24 @@
|
||||
{:label "Emoji"
|
||||
:key :emoji
|
||||
:type :select
|
||||
:options [{:key :main-icons/love16
|
||||
:value "Love"}
|
||||
{:key :main-icons/thumbs-up16
|
||||
:value "Thumbs Up"}
|
||||
{:key :main-icons/thumbs-down16
|
||||
:value "Thumbs Down"}
|
||||
{:key :main-icons/laugh16
|
||||
:value "Laugh"}
|
||||
{:key :main-icons/sad16
|
||||
:value "Sad"}]}
|
||||
:options (for [reaction (vals constants/reactions)]
|
||||
{:key reaction
|
||||
:value (string/capitalize (name reaction))})}
|
||||
{:label "Neutral"
|
||||
:key :neutral?
|
||||
:type :boolean}])
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
(let [state (reagent/atom {:emoji :main-icons/love16})]
|
||||
(let [state (reagent/atom {:emoji :reaction/love})]
|
||||
(fn []
|
||||
[rn/touchable-without-feedback {:on-press rn/dismiss-keyboard!}
|
||||
[rn/view {:padding-bottom 150}
|
||||
[preview/customizer state descriptor]
|
||||
[rn/view
|
||||
{:padding-vertical 60
|
||||
:justify-content :center
|
||||
:flex-direction :row
|
||||
:align-items :center}
|
||||
[quo2.reaction/reaction @state]
|
||||
[quo2.reaction/add-reaction @state]]]])))
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
(ns status-im2.contexts.quo-preview.selectors.reactions
|
||||
(:require [quo2.core :as quo]
|
||||
[quo2.foundations.colors :as colors]
|
||||
[react-native.core :as rn]
|
||||
[status-im2.constants :as constants]))
|
||||
|
||||
(defn cool-preview
|
||||
[]
|
||||
[rn/view
|
||||
[rn/view {:style {:margin-vertical 24}}
|
||||
(into [rn/view
|
||||
{:style {:flex 1
|
||||
:margin-top 200
|
||||
:flex-direction :row
|
||||
:justify-content :center
|
||||
:align-items :center}}]
|
||||
(for [emoji (vals constants/reactions)]
|
||||
^{:key emoji}
|
||||
[quo/reactions emoji {:container-style {:margin-right 5}}]))]])
|
||||
|
||||
(defn preview
|
||||
[]
|
||||
[rn/view
|
||||
{:style {:background-color (colors/theme-colors colors/white colors/neutral-95)
|
||||
:flex 1}}
|
||||
[rn/flat-list
|
||||
{:style {:flex 1}
|
||||
:keyboard-should-persist-taps :always
|
||||
:header [cool-preview]
|
||||
:key-fn str}]])
|
||||
@@ -105,7 +105,9 @@
|
||||
:translucent true}
|
||||
:navigationBar {:backgroundColor colors/black}
|
||||
:layout {:componentBackgroundColor :transparent
|
||||
:backgroundColor :transparent}
|
||||
:backgroundColor :transparent
|
||||
;; issue: https://github.com/wix/react-native-navigation/issues/7726
|
||||
:orientation (if platform/ios? ["portrait" "landscape"] ["portrait"])}
|
||||
:animations {:push {:sharedElementTransitions [{:fromId :shared-element
|
||||
:toId :shared-element
|
||||
:interpolation {:type :decelerate
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
(ns status-im2.setup.i18n-resources
|
||||
(:require [clojure.string :as string]
|
||||
[utils.i18n :as i18n]
|
||||
[react-native.languages :as react-native-languages]))
|
||||
[utils.i18n :as i18n]))
|
||||
|
||||
(def default-device-language (react-native-languages/get-lang-keyword))
|
||||
;; FIXME: that should be replaced with `(react-native-languages/get-lang-keyword)`
|
||||
;; in order for languages/translations to work
|
||||
;; see https://github.com/status-im/status-mobile/issues/16058 for details
|
||||
(def default-device-language :en)
|
||||
|
||||
(def languages
|
||||
#{:ar :bn :de :el :en :es :es_419 :es_AR :fil :fr :hi :id :in :it :ja :ko :ms :nl :pl :pt :pt_BR :ru
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"_comment": "Instead use: scripts/update-status-go.sh <rev>",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.154.1",
|
||||
"commit-sha1": "1c51df20e002e9b93203a9128882d60deed6f47a",
|
||||
"src-sha256": "0mhznnkdx7946mkz8bzlsv70iv2aynirh9dsm5cyanjjap123qgv"
|
||||
"version": "v0.154.2",
|
||||
"commit-sha1": "5d62a9eef4a6676d62a9a51cdadea596d3d9a658",
|
||||
"src-sha256": "148z0r8iyqanx91hgvbm7wx1nf82v2am1hyy6f892vmbc2y8axnx"
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ class BaseTestReport:
|
||||
def get_all_tests(self):
|
||||
tests = list()
|
||||
file_list = [f for f in os.listdir(self.TEST_REPORT_DIR) if f.endswith('json')]
|
||||
print("REPORT FILES LIST:\n%s" % "\n".join(os.listdir(self.TEST_REPORT_DIR)))
|
||||
for file_name in file_list:
|
||||
file_path = os.path.join(self.TEST_REPORT_DIR, file_name)
|
||||
test_data = json.load(open(file_path))
|
||||
|
||||
@@ -210,6 +210,7 @@ class TestrailReport(BaseTestReport):
|
||||
'status_id': self.outcomes['undefined_fail'] if last_testrun.error else self.outcomes['passed'],
|
||||
'comment': comment})
|
||||
|
||||
print("RESULTS:\n%s" % "\n".join(str(i) for i in data))
|
||||
results = self.post('add_results_for_cases/%s' % self.run_id, data={"results": data})
|
||||
try:
|
||||
results[0]
|
||||
|
||||
@@ -15,7 +15,7 @@ from sauceclient import SauceException
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.common.exceptions import WebDriverException
|
||||
from selenium.webdriver.support.wait import WebDriverWait
|
||||
from urllib3.exceptions import MaxRetryError
|
||||
from urllib3.exceptions import MaxRetryError, ProtocolError
|
||||
|
||||
from support.api.network_api import NetworkApi
|
||||
from support.github_report import GithubHtmlReport
|
||||
@@ -312,6 +312,7 @@ def create_shared_drivers(quantity):
|
||||
return drivers, loop
|
||||
except MaxRetryError as e:
|
||||
test_suite_data.current_test.testruns[-1].error = e.reason
|
||||
raise e
|
||||
|
||||
|
||||
class LocalSharedMultipleDeviceTestCase(AbstractTestCase):
|
||||
@@ -360,22 +361,26 @@ class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
|
||||
jobs[driver.session_id] = index + 1
|
||||
self.errors = Errors()
|
||||
test_suite_data.current_test.group_name = self.__class__.__name__
|
||||
print("STARTING %s; test suite data: %s" % (method.__name__, [i.name for i in test_suite_data.tests]))
|
||||
|
||||
def teardown_method(self, method):
|
||||
geth_names, geth_contents = [], []
|
||||
for driver in self.drivers:
|
||||
try:
|
||||
self.print_sauce_lab_info(self.drivers[driver])
|
||||
# self.print_sauce_lab_info(self.drivers[driver])
|
||||
self.add_alert_text_to_report(self.drivers[driver])
|
||||
geth_names.append(
|
||||
'%s_geth%s.log' % (test_suite_data.current_test.name, str(self.drivers[driver].number)))
|
||||
geth_contents.append(self.pull_geth(self.drivers[driver]))
|
||||
|
||||
except (WebDriverException, AttributeError):
|
||||
pass
|
||||
except (WebDriverException, AttributeError, RemoteDisconnected, ProtocolError):
|
||||
print("Error in teardown method of %s" % method.__name__) #pass
|
||||
finally:
|
||||
geth = {geth_names[i]: geth_contents[i] for i in range(len(geth_names))}
|
||||
test_suite_data.current_test.geth_paths = self.github_report.save_geth(geth)
|
||||
try:
|
||||
geth = {geth_names[i]: geth_contents[i] for i in range(len(geth_names))}
|
||||
test_suite_data.current_test.geth_paths = self.github_report.save_geth(geth)
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
@pytest.fixture(scope='class', autouse=True)
|
||||
def prepare(self, request):
|
||||
@@ -390,28 +395,31 @@ class SauceSharedMultipleDeviceTestCase(AbstractTestCase):
|
||||
from tests.conftest import sauce
|
||||
requests_session = requests.Session()
|
||||
requests_session.auth = (sauce_username, sauce_access_key)
|
||||
for _, driver in cls.drivers.items():
|
||||
session_id = driver.session_id
|
||||
try:
|
||||
sauce.jobs.update_job(username=sauce_username, job_id=session_id, name=cls.__name__)
|
||||
except (RemoteDisconnected, SauceException):
|
||||
pass
|
||||
try:
|
||||
driver.quit()
|
||||
except WebDriverException:
|
||||
pass
|
||||
url = 'https://api.%s/rest/v1/%s/jobs/%s/assets/%s' % (apibase, sauce_username, session_id, "log.json")
|
||||
WebDriverWait(driver, 60, 2).until(lambda _: requests_session.get(url).status_code == 200)
|
||||
commands = requests_session.get(url).json()
|
||||
for command in commands:
|
||||
if cls.drivers:
|
||||
for _, driver in cls.drivers.items():
|
||||
session_id = driver.session_id
|
||||
try:
|
||||
if command['message'].startswith("Started "):
|
||||
for test in test_suite_data.tests:
|
||||
if command['message'] == "Started %s" % test.name:
|
||||
test.testruns[-1].first_commands[session_id] = commands.index(command) + 1
|
||||
except KeyError:
|
||||
continue
|
||||
cls.loop.close()
|
||||
sauce.jobs.update_job(username=sauce_username, job_id=session_id, name=cls.__name__)
|
||||
except (RemoteDisconnected, SauceException):
|
||||
pass
|
||||
try:
|
||||
driver.quit()
|
||||
except WebDriverException:
|
||||
pass
|
||||
url = 'https://api.%s/rest/v1/%s/jobs/%s/assets/%s' % (apibase, sauce_username, session_id, "log.json")
|
||||
WebDriverWait(driver, 60, 2).until(lambda _: requests_session.get(url).status_code == 200)
|
||||
commands = requests_session.get(url).json()
|
||||
for command in commands:
|
||||
try:
|
||||
if command['message'].startswith("Started "):
|
||||
for test in test_suite_data.tests:
|
||||
if command['message'] == "Started %s" % test.name:
|
||||
test.testruns[-1].first_commands[session_id] = commands.index(command) + 1
|
||||
except KeyError:
|
||||
continue
|
||||
if cls.loop:
|
||||
cls.loop.close()
|
||||
print("Teardown of %s, TEST SUITE DATA:\n%s" % (cls.__name__, [i.name for i in test_suite_data.tests]))
|
||||
for test in test_suite_data.tests:
|
||||
cls.github_report.save_test(test)
|
||||
|
||||
|
||||
@@ -288,6 +288,7 @@ def pytest_runtest_makereport(item, call):
|
||||
if error:
|
||||
test_suite_data.current_test.testruns[-1].error = final_error
|
||||
from support.github_report import GithubHtmlReport
|
||||
print("Conftest, TEST SUITE DATA - %s:\n%s" % (item.instance.__class__.__name__, test_suite_data.tests))
|
||||
GithubHtmlReport().save_test(test_suite_data.current_test)
|
||||
|
||||
if report.when == 'call':
|
||||
|
||||
@@ -3,7 +3,7 @@ import time
|
||||
|
||||
import emoji
|
||||
import pytest
|
||||
from selenium.common.exceptions import TimeoutException
|
||||
from selenium.common.exceptions import TimeoutException, NoSuchElementException
|
||||
|
||||
from tests import marks, common_password, run_in_parallel
|
||||
from tests.base_test_case import MultipleSharedDeviceTestCase, create_shared_drivers
|
||||
@@ -453,7 +453,6 @@ class TestContactBlockMigrateKeycardMultipleSharedDevices(MultipleSharedDeviceTe
|
||||
self.errors.append('Contact is shown in Profile after removing user from contacts')
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
|
||||
@marks.testrail_id(702188)
|
||||
@marks.xfail(
|
||||
reason="flaky; issue when sometimes history is not fetched from offline for public chat, needs investigation")
|
||||
@@ -840,7 +839,7 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
|
||||
'username': self.username_2}))))
|
||||
self.home_1, self.home_2 = self.device_1.get_home_view(), self.device_2.get_home_view()
|
||||
self.homes = (self.home_1, self.home_2)
|
||||
self.profile_1, self.profile_2 = (home.get_profile_view() for home in self.homes)
|
||||
self.profile_1, self.profile_2 = (home.get_profile_view() for home in self.homes)
|
||||
self.public_key_2 = self.home_2.get_public_key()
|
||||
|
||||
self.profile_1.just_fyi("Sending contact request via Profile > Contacts")
|
||||
@@ -927,6 +926,7 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
|
||||
self.home_2.just_fyi("Check 'Open in Status' option")
|
||||
url_message = 'http://status.im'
|
||||
self.chat_1.send_message(url_message)
|
||||
self.chat_2.element_starts_with_text(url_message, 'button').wait_for_visibility_of_element(120)
|
||||
self.chat_2.element_starts_with_text(url_message, 'button').click_inside_element_by_coordinate(0.2, 0.5)
|
||||
web_view = self.chat_2.open_in_status_button.click()
|
||||
if not web_view.element_by_text('Private, Secure Communication').is_element_displayed(60):
|
||||
@@ -934,6 +934,7 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.xfail(reason="Pin feature is in development", run=False)
|
||||
@marks.testrail_id(702731)
|
||||
def test_1_1_chat_pin_messages(self):
|
||||
self.home_1.just_fyi("Check that Device1 can pin own message in 1-1 chat")
|
||||
@@ -1034,19 +1035,23 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
|
||||
|
||||
self.chat_2.just_fyi("Send messages with non-latin symbols")
|
||||
self.home_1.jump_to_card_by_text(self.username_2)
|
||||
self.chat_1.send_message("just a text") # Sending a message here so the next ones will be in a separate line
|
||||
messages = ['hello', '¿Cómo estás tu año?', 'ё, доброго вечерочка', '® æ ç ♥']
|
||||
[self.chat_2.send_message(message) for message in messages]
|
||||
for message in messages:
|
||||
if not self.chat_1.chat_element_by_text(message).is_element_displayed():
|
||||
self.errors.append("Message with test '%s' was not received" % message)
|
||||
self.errors.append("Message with text '%s' was not received" % message)
|
||||
|
||||
self.chat_2.just_fyi("Checking updated member photo, timestamp and username on message")
|
||||
self.chat_2.hide_keyboard_if_shown()
|
||||
timestamp = self.chat_2.chat_element_by_text(messages[0]).timestamp
|
||||
sent_time_variants = self.chat_2.convert_device_time_to_chat_timestamp()
|
||||
if timestamp not in sent_time_variants:
|
||||
self.errors.append(
|
||||
'Timestamp on message %s does not correspond expected [%s]' % (timestamp, *sent_time_variants))
|
||||
try:
|
||||
timestamp = self.chat_2.chat_element_by_text(messages[0]).timestamp
|
||||
sent_time_variants = self.chat_2.convert_device_time_to_chat_timestamp()
|
||||
if timestamp not in sent_time_variants:
|
||||
self.errors.append(
|
||||
'Timestamp on message %s does not correspond expected [%s]' % (timestamp, *sent_time_variants))
|
||||
except NoSuchElementException:
|
||||
self.errors.append("No timestamp on message %s" % messages[0])
|
||||
for message in [messages[1], messages[2]]:
|
||||
if self.chat_2.chat_element_by_text(message).member_photo.is_element_displayed():
|
||||
self.errors.append('%s is not stack to 1st(they are sent in less than 5 minutes)!' % message)
|
||||
@@ -1187,6 +1192,7 @@ class TestOneToOneChatMultipleSharedDevicesNewUi(MultipleSharedDeviceTestCase):
|
||||
[device.click_system_back_button_until_element_is_shown() for device in (self.device_1, self.device_2)]
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.xfail(reason="Issue with messages not being sent for a long time")
|
||||
@marks.testrail_id(702783)
|
||||
def test_1_1_chat_is_shown_message_sent_delivered_from_offline(self):
|
||||
self.chat_2.jump_to_card_by_text(self.username_1)
|
||||
|
||||
@@ -257,6 +257,7 @@ class TestGroupChatMultipleDeviceMergedNewUI(MultipleSharedDeviceTestCase):
|
||||
self.errors.append('%s if not shown for device %s' % (message, str(i)))
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.xfail(reason="Pin feature is in development", run=False)
|
||||
@marks.testrail_id(702732)
|
||||
def test_group_chat_pin_messages(self):
|
||||
[self.homes[i].click_system_back_button_until_element_is_shown() for i in range(3)]
|
||||
|
||||
@@ -4,13 +4,13 @@ from datetime import timedelta
|
||||
import emoji
|
||||
import pytest
|
||||
from dateutil import parser
|
||||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.common.exceptions import NoSuchElementException, TimeoutException
|
||||
|
||||
from tests import marks, test_dapp_name, test_dapp_url, run_in_parallel, common_password
|
||||
from tests import marks, test_dapp_name, test_dapp_url, run_in_parallel
|
||||
from tests.base_test_case import create_shared_drivers, MultipleSharedDeviceTestCase
|
||||
from views.chat_view import CommunityView
|
||||
from views.sign_in_view import SignInView
|
||||
from views.dbs.waku_backup import user as waku_user
|
||||
from views.sign_in_view import SignInView
|
||||
|
||||
|
||||
@pytest.mark.xdist_group(name="three_1")
|
||||
@@ -342,7 +342,8 @@ class TestCommunityOneDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
self.channel.copy_message_text(message)
|
||||
actual_copied_text = self.channel.driver.get_clipboard_text()
|
||||
if actual_copied_text != message:
|
||||
self.errors.append('Message %s text was not copied in community channel, text in clipboard %s' % actual_copied_text)
|
||||
self.errors.append(
|
||||
'Message %s text was not copied in community channel, text in clipboard %s' % actual_copied_text)
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@@ -443,7 +444,6 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
self.channel_2 = self.community_2.get_channel(self.channel_name).click()
|
||||
|
||||
@marks.testrail_id(702838)
|
||||
@marks.xfail(reason="blocked by 14797")
|
||||
def test_community_message_send_check_timestamps_sender_username(self):
|
||||
message = self.text_message
|
||||
sent_time_variants = self.channel_1.convert_device_time_to_chat_timestamp()
|
||||
@@ -499,13 +499,12 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
self.home_1.just_fyi('Send image in 1-1 chat from Gallery')
|
||||
image_description = 'description'
|
||||
self.channel_1.send_images_with_description(image_description)
|
||||
# TODO: possible after adding proper accessibility-id to 1 image in chat
|
||||
# self.channel_1.chat_message_input.click()
|
||||
# self.channel_1.chat_element_by_text(image_description).image_in_message.click()
|
||||
# self.channel_1.click_system_back_button()
|
||||
self.channel_1.chat_message_input.click()
|
||||
self.channel_1.chat_element_by_text(image_description).image_in_message.click()
|
||||
self.channel_1.click_system_back_button()
|
||||
|
||||
# TODO: options for image are still WIP; add case with edit description of image and after 15901 fix
|
||||
# self.home_2.just_fyi('check image, description and options for receiver')
|
||||
self.home_2.just_fyi('check image, description and options for receiver')
|
||||
# self.channel_2.chat_element_by_text(image_description).image_in_message.click()
|
||||
# self.home_1.just_fyi('save image')
|
||||
# self.chat_1.save_image_button.click_until_presence_of_element(self.chat_1.show_images_button)
|
||||
@@ -531,6 +530,10 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
#
|
||||
# self.channel_2.chat_element_by_text(image_description).image_in_message.save_new_screenshot_of_element('images_test.png')
|
||||
|
||||
if not self.channel_2.chat_element_by_text(
|
||||
image_description).image_in_message.is_element_image_similar_to_template('image_sent_in_community.png'):
|
||||
self.errors.append("Not expected image is shown to the receiver")
|
||||
|
||||
self.channel_2.just_fyi("Can reply to images")
|
||||
self.channel_2.quote_message(image_description)
|
||||
message_text = 'reply to image'
|
||||
@@ -551,7 +554,8 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
self.channel_2.just_fyi("Check gallery on second device")
|
||||
self.channel_2.jump_to_communities_home()
|
||||
self.home_2.get_to_community_channel_from_home(self.community_name)
|
||||
if self.channel_2.chat_element_by_text(image_description).image_container_in_message.is_element_differs_from_template(file_name, 5):
|
||||
if self.channel_2.chat_element_by_text(
|
||||
image_description).image_container_in_message.is_element_differs_from_template(file_name, 5):
|
||||
self.errors.append("Gallery message do not match the template!")
|
||||
|
||||
self.channel_2.just_fyi("Can reply to gallery")
|
||||
@@ -593,67 +597,66 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
@marks.testrail_id(702844)
|
||||
def test_community_links_with_previews_github_youtube_twitter_gif_send_enable(self):
|
||||
preview_urls = {
|
||||
# TODO: disabled because of the bug in 15891
|
||||
# 'giphy':{'url': 'https://giphy.com/gifs/this-is-fine-QMHoU66sBXqqLqYvGO',
|
||||
# 'title': 'This Is Fine GIF - Find & Share on GIPHY',
|
||||
# 'description': 'Discover & share this Meme GIF with everyone you know. GIPHY is how you search, share, discover, and create GIFs.',
|
||||
# 'link': 'giphy.com'},
|
||||
'github_pr': {'url': 'https://github.com/status-im/status-mobile/pull/11707',
|
||||
'title': 'Update translations by jinhojang6 · Pull Request #11707 · status-im/status-mobile',
|
||||
'description': 'Update translation json files of 19 languages.',
|
||||
'link': 'github.com'},
|
||||
'yotube_short': {
|
||||
'url': 'https://youtu.be/Je7yErjEVt4',
|
||||
'title': 'Status, your gateway to Ethereum',
|
||||
'description': 'Learn more at https://status.im. This video aims to provide an explanation '
|
||||
'and brief preview of the utility that will be supported by the Status App - provid...',
|
||||
'link': 'youtu.be'},
|
||||
'yotube_full': {
|
||||
'url': 'https://www.youtube.com/watch?v=XN-SVmuJH2g&list=PLbrz7IuP1hrgNtYe9g6YHwHO6F3OqNMao',
|
||||
'title': 'Status & Keycard – Hardware-Enforced Security',
|
||||
'description': 'With Status and Keycard, you can enable hardware enforced authorizations to '
|
||||
'your Status account and transactions. Two-factor authentication to access your ac...',
|
||||
'link': 'www.youtube.com'},
|
||||
'yotube_mobile': {
|
||||
'url': 'https://m.youtube.com/watch?v=Je7yErjEVt4',
|
||||
'title': 'Status, your gateway to Ethereum',
|
||||
'description': 'Learn more at https://status.im. This video aims to provide an explanation '
|
||||
'and brief preview of the utility that will be supported by the Status App - provid...',
|
||||
'link': 'm.youtube.com',
|
||||
},
|
||||
# TODO: disabled because of the bug in 15891
|
||||
# 'giphy':{'url': 'https://giphy.com/gifs/this-is-fine-QMHoU66sBXqqLqYvGO',
|
||||
# 'title': 'This Is Fine GIF - Find & Share on GIPHY',
|
||||
# 'description': 'Discover & share this Meme GIF with everyone you know. GIPHY is how you search, share, discover, and create GIFs.',
|
||||
# 'link': 'giphy.com'},
|
||||
'github_pr': {'url': 'https://github.com/status-im/status-mobile/pull/11707',
|
||||
'title': 'Update translations by jinhojang6 · Pull Request #11707 · status-im/status-mobile',
|
||||
'description': 'Update translation json files of 19 languages.',
|
||||
'link': 'github.com'},
|
||||
'yotube_short': {
|
||||
'url': 'https://youtu.be/Je7yErjEVt4',
|
||||
'title': 'Status, your gateway to Ethereum',
|
||||
'description': 'Learn more at https://status.im. This video aims to provide an explanation '
|
||||
'and brief preview of the utility that will be supported by the Status App - provid...',
|
||||
'link': 'youtu.be'},
|
||||
'yotube_full': {
|
||||
'url': 'https://www.youtube.com/watch?v=XN-SVmuJH2g&list=PLbrz7IuP1hrgNtYe9g6YHwHO6F3OqNMao',
|
||||
'title': 'Status & Keycard – Hardware-Enforced Security',
|
||||
'description': 'With Status and Keycard, you can enable hardware enforced authorizations to '
|
||||
'your Status account and transactions. Two-factor authentication to access your ac...',
|
||||
'link': 'www.youtube.com'},
|
||||
'yotube_mobile': {
|
||||
'url': 'https://m.youtube.com/watch?v=Je7yErjEVt4',
|
||||
'title': 'Status, your gateway to Ethereum',
|
||||
'description': 'Learn more at https://status.im. This video aims to provide an explanation '
|
||||
'and brief preview of the utility that will be supported by the Status App - provid...',
|
||||
'link': 'm.youtube.com',
|
||||
},
|
||||
|
||||
# twitter link is temporary removed from check as current xpath locator in message.preview_title is not applicable for this type of links
|
||||
# 'twitter': {
|
||||
# 'url': 'https://twitter.com/ethdotorg/status/1445161651771162627?s=20',
|
||||
# 'txt': "We've rethought how we translate content, allowing us to translate",
|
||||
# 'subtitle': 'Twitter'
|
||||
# }
|
||||
}
|
||||
# twitter link is temporary removed from check as current xpath locator in message.preview_title is not applicable for this type of links
|
||||
# 'twitter': {
|
||||
# 'url': 'https://twitter.com/ethdotorg/status/1445161651771162627?s=20',
|
||||
# 'txt': "We've rethought how we translate content, allowing us to translate",
|
||||
# 'subtitle': 'Twitter'
|
||||
# }
|
||||
}
|
||||
|
||||
|
||||
for key in preview_urls:
|
||||
for key, data in preview_urls.items():
|
||||
self.home_2.just_fyi("Checking %s preview case" % key)
|
||||
data = preview_urls[key]
|
||||
url = data['url']
|
||||
self.channel_2.chat_message_input.set_value(url)
|
||||
self.channel_2.url_preview_composer.wait_for_element(20)
|
||||
if self.channel_2.url_preview_composer_text.text != data['title']:
|
||||
self.errors.append(
|
||||
"Preview text is not expected, it is '%s'" % self.channel_2.url_preview_composer_text.text)
|
||||
shown_title = self.channel_2.url_preview_composer_text.text
|
||||
if shown_title != data['title']:
|
||||
self.errors.append("Preview text is not expected, it is '%s'" % shown_title)
|
||||
self.channel_2.send_message_button.click()
|
||||
self.channel_1.get_preview_message_by_text(url).wait_for_element(60)
|
||||
message = self.channel_1.get_preview_message_by_text(url)
|
||||
# if not message.preview_image:
|
||||
# self.errors.append("No preview is shown for %s" % link_data['url'])
|
||||
if message.preview_title.text != data['title']:
|
||||
self.errors.append("Title is not equal expected for '%s', actual is '%s'" % (url, message.preview_title.text))
|
||||
|
||||
if message.preview_subtitle.text != data['description']:
|
||||
shown_title = message.preview_title.text
|
||||
if shown_title != data['title']:
|
||||
self.errors.append("Title is not equal expected for '%s', actual is '%s'" % (url, shown_title))
|
||||
shown_description = message.preview_subtitle.text
|
||||
if shown_description != data['description']:
|
||||
self.errors.append(
|
||||
"Description is not equal expected for '%s', actual is '%s'" % (url, message.preview_subtitle.text))
|
||||
|
||||
if message.preview_link.text != data['link']:
|
||||
self.errors.append("Link is not equal expected for '%s', actual is '%s'" % (url, message.preview_link.text))
|
||||
"Description is not equal expected for '%s', actual is '%s'" % (url, shown_description))
|
||||
shown_link = message.preview_link.text
|
||||
if shown_link != data['link']:
|
||||
self.errors.append("Link is not equal expected for '%s', actual is '%s'" % (url, shown_link))
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@@ -743,8 +746,14 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
self.home_2.just_fyi("Check message in 1-1 chat after unblock")
|
||||
self.home_2.get_chat(self.username_1).click()
|
||||
self.chat_2.send_message(message_unblocked)
|
||||
if not self.chat_1.chat_element_by_text(message_unblocked).is_element_displayed(30):
|
||||
self.errors.append("Message was not received in 1-1 chat after user unblock!")
|
||||
try:
|
||||
self.chat_2.chat_element_by_text(message_unblocked).wait_for_status_to_be(expected_status='Delivered',
|
||||
timeout=120)
|
||||
if not self.chat_1.chat_element_by_text(message_unblocked).is_element_displayed(30):
|
||||
self.errors.append("Message was not received in 1-1 chat after user unblock!")
|
||||
except TimeoutException:
|
||||
self.errors.append('Message was not delivered after back up online.')
|
||||
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(703086)
|
||||
@@ -759,13 +768,14 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
community_1_element.long_press_until_element_is_shown(mark_as_read_button)
|
||||
mark_as_read_button.click()
|
||||
if community_1_element.new_messages_public_chat.is_element_displayed():
|
||||
self.errors.append('Unread messages badge is shown in community channel while there are no unread messages')
|
||||
# TODO: there should be one more check for community channel, which is still not ready
|
||||
self.errors.append(
|
||||
'Unread messages badge is shown in community channel while there are no unread messages')
|
||||
# TODO: there should be one more check for community channel, which is still not ready
|
||||
|
||||
# self.community_1.click_system_back_button_until_element_is_shown()
|
||||
# community_1_element = self.home_1.get_chat(self.community_name, community=True)
|
||||
# if community_1_element.new_messages_community.is_element_displayed():
|
||||
# self.errors.append('New messages community badge is shown on community after marking messages as read')
|
||||
# self.community_1.click_system_back_button_until_element_is_shown()
|
||||
# community_1_element = self.home_1.get_chat(self.community_name, community=True)
|
||||
# if community_1_element.new_messages_community.is_element_displayed():
|
||||
# self.errors.append('New messages community badge is shown on community after marking messages as read')
|
||||
self.errors.verify_no_errors()
|
||||
|
||||
@marks.testrail_id(702786)
|
||||
@@ -790,7 +800,8 @@ class TestCommunityMultipleDeviceMerged(MultipleSharedDeviceTestCase):
|
||||
if self.channel_1.chat_message_input.is_element_displayed():
|
||||
self.errors.append("Message with the mention is not shown in the chat for the admin")
|
||||
else:
|
||||
self.errors.append("Channel did not open by clicking on a notification with the mention for admin")
|
||||
self.errors.append(
|
||||
"Channel did not open by clicking on a notification with the mention for admin")
|
||||
else:
|
||||
self.errors.append("Push notification with the mention was not received by admin")
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ class ChatElementByText(Text):
|
||||
try:
|
||||
self.driver.info("Trying to access image inside message with text '%s'" % self.message_text)
|
||||
ChatElementByText(self.driver, self.message_text).wait_for_sent_state(60)
|
||||
return Button(self.driver, xpath='%s//android.view.ViewGroup/android.widget.ImageView' % self.locator)
|
||||
return Button(self.driver, xpath="%s//*[@content-desc='image-message']" % self.locator)
|
||||
except NoSuchElementException:
|
||||
self.driver.fail("No image is found in message!")
|
||||
|
||||
@@ -996,10 +996,9 @@ class ChatView(BaseView):
|
||||
|
||||
def set_reaction(self, message: str, emoji: str = 'thumbs-up', emoji_message=False):
|
||||
self.driver.info("Setting '%s' reaction" % emoji)
|
||||
key = emojis[emoji]
|
||||
# Audio message is obvious should be tapped not on audio-scroll-line
|
||||
# so we tap on its below element as exception here (not the case for link/tag message!)
|
||||
element = Button(self.driver, accessibility_id='emoji-picker-%s' % key)
|
||||
element = Button(self.driver, accessibility_id='reaction-%s' % emoji)
|
||||
if message == 'audio':
|
||||
self.audio_message_in_chat_timer.long_press_element()
|
||||
else:
|
||||
|
||||
|
After Width: | Height: | Size: 172 KiB |