Compare commits

...
Author SHA1 Message Date
Richard Ramos e48ffb632f chore: update status-go version 2023-10-10 10:16:51 -04:00
Volodymyr Kozieiev 919ee4b9ab wallet activity design followup fixes (#17590) 2023-10-10 14:54:26 +01:00
Omar Basem d664653921 Wallet: dApps Tab (#17587)
* feat: dapps tab
2023-10-10 16:28:05 +04:00
Mohsen 36dd828a55 [#17446] fix: Display name is not shown in chats after sync (#17591) 2023-10-10 14:36:40 +03:00
Omar Basem bcc175041a Wallet: About Tab (#17556)
* wallet about tab
2023-10-10 09:30:10 +04:00
Ibrahem Khalil d4cc0189d2 Disable scroll to the bottom button when composer is active. 2023-10-09 20:33:20 +03:00
Volodymyr Kozieiev 1770ff24ce Collectible details page (#17520)
Collectible details page
2023-10-09 14:18:43 +01:00
Lungu Cristian 265d9be6c2 Follow-up quo2 reaction selector components (#17304)
* ref: refactored react/react-selector/reactions-selector

feat: added preview multi-select descriptor

feat: finished component

ref: fixed linting

feat: added pinned styles to all reactions

lint: fixed linting

ref: preview customizer for reactions-selector

lint: fixed linting

fix: preview select bg

ref: destructured reaction

* feat: added pinned prop in chat messages

* fix: removed status_im2 require inside quo2 component

* fix: addressed review comments

* lint: fixed linting

* fix: added missing theme arg to theme-color

* chore: removed unnecessary FIXME

* fix: message reactions not working on press
2023-10-09 12:44:12 +03:00
Lungu CristianandYevheniia Berdnyk b432aab701 Changes to onboarding/new-to-status (quo/fonts/spacings/copy) (#17562)
* fix: changes to onboarding/new-to-status (quo/fonts/spacings/copy)

* e2e: updated button  locator

---------

Co-authored-by: Yevheniia Berdnyk <ie.berdnyk@gmail.com>
2023-10-09 10:45:28 +03:00
Ulises Manuel 1abd1e9420 [#16329] QR code variants (#17221)
* Rename wallet-user-avatar's `:color` prop to `:customization-color`
* Refactor QR code component and implement all variants
  - Improve preview screen
* Update QR code usages
* Remove `status-im2.common.qr-code-viewer.view/qr-code-view` component
to keep only one implementation.
* Remove the node dependency:
  "qrcode": "^1.4.1"
2023-10-08 17:42:58 -06:00
Ibrahem Khalil 4dc834b079 Faded first line of composer (#17489) 2023-10-07 11:12:10 +03:00
Mohamed Javid c6a9148e20 [Fix] SVG Icon color (#17561)
This commit fixes the "X" mark in the clear icon displayed in inputs and URL preview components by updating the props for "svg-icon" to add the "color" and "color-2" keys only if it's present and valid.

Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
2023-10-07 13:20:44 +05:30
Yevheniia Berdnyk 7c1850b901 e2e: some fixes and moved old ui tests to a separate dir 2023-10-07 03:24:51 +03:00
BalogunofAfrica b88dcf1072 fix: back button on 1:1 chat (#17544)
* fix: back button on 1:1 chat

* fix: show close icon only for 1:1 and community chat

* fix: use close icon for only 1:1 chat
2023-10-06 12:47:29 +01:00
yqrashawn afbd890a04 fix: several bugs in 1.25 design feedback (#17548) 2023-10-06 15:32:33 +08:00
John Ngei 933a5a1a9e 1-1 chats bug fixes
* removed horizontal padding for separator line

* fixed: page-nav button backgrounds visibility

* updated top margin

* updated selected item top margin

* fixed: author and  message margins
2023-10-06 00:45:26 +03:00
Icaro Motta 6e897f0ea6 Migrate away from rf/defn and rf/merge (first step) (#17451)
This commit shows how to move away from rf/defn
https://github.com/status-im/status-mobile/blob/f12c7401d167d7adb81f97bdf9c0902b39ee37bc/src/utils/re_frame.clj#L1-L90
& rf/merge
https://github.com/status-im/status-mobile/blob/f12c7401d167d7adb81f97bdf9c0902b39ee37bc/src/utils/re_frame.cljs#L39-L85
and why we should do it.

## Problems

Before jumping to solutions, let's understand the problems first, in no order of
importance.

### Problem 1: Cyclic dependencies

If you ever tried to move event handlers or the functions used inside them to
different files in status-mobile, you probably stumbled in cyclic dependency
errors.

When an event is registered in re-frame, it is globally available for any other
place to dispatch. The dispatch mechanism relies on globally unique keywords,
the so called event IDs, e.g. :chat/mute-successfully. This means that event
namespaces don't need to require other event namespaces, just like you don't
need to require subscription namespaces in views.

rf/merge increases the likelihood of cyclic dependencies because they force
event namespaces to require each other. Although not as common, this happened a
few times with devs in the team and it can be a big hassle to fix if you are
unlucky. It is a problem we should not have in the first place (at least not as
easily).

### Problem 2: We are not linting thousands of lines of code

The linter (clj-kondo) is incapable of understanding the rf/defn macro. In
theory, we could teach clj-kondo what the macro produces. I tried this, but gave
up after a few tries.

This is a big deal, clj-kondo can catch many issues and will continue to catch
more as it continue to evolve. It's hard to precisely count how many lines are
affected, but `find src/ -type f -name 'events.cljs' -exec wc -l {} +` gives us
more than 4k LOC.

### Problem 3: Blocking RN's UI thread for too long

Re-frame has a routing mechanism to manage events. When an event is dispatched,
it is enqueued and scheduled to run some time later (very soon). This process is
asynchronous and is optimized in such a way as to balance responsiveness vs the
time to empty the queue.

>[...] when processing events, one after the other, do ALL the currently queued
>events. Don't stop. Don't yield to the browser. Hog that CPU.
>
>[...] but if any new events are dispatched during this cycle of processing,
>don't do them immediately. Leave them queued.
>
>-- https://github.com/day8/re-frame/blob/master/src/re_frame/router.cljc#L8-L60

Decisions were made (way back in 2017) to reduce the number of registered
re-frame events and, more importantly, to coalesce events into bigger ones with
the rf/merge pattern. I tried to find evidence of real problems that were trying
to be solved, but my understanding is that decisions were largely based on
personal architectural preferences.

Fast-forward to 2023, and we are in a situation where we have many heavy events
that process a LOT of stuff in one go using rf/merge, thus blocking the UI
thread longer than we should. See, for example,
[status-im2.contexts.profile.login.events/login-existing-profile](https://github.com/status-im/status-mobile/blob/3082605d1e9897da1b1784588aa22fdc65c84823/src/status_im2/contexts/profile/login/events.cljs#L69),
[status-im2.contexts.profile.login.events/get-chats-callback](https://github.com/status-im/status-mobile/blob/3082605d1e9897da1b1784588aa22fdc65c84823/src/status_im2/contexts/profile/login/events.cljs#L98),
and many others.

The following excerpt was generally used to justify the idea that coalescing
events would make the app perform better.

> We will reduce the the amount of subscription re-computations, as for each
> distinct action, :db effect will be produced and swapped into app-db only once
>
> -- https://github.com/status-im/swarms/issues/31#issuecomment-346345981

This is in fact incorrect. Re-frame, ever since 2015 (so before the original
discussions in 2017) uses a concept of batching to process events, which means
subscriptions won't re-run after every dispatched event, and thus components
won't re-render either. Re-frame is smarter than that.

> groups of events queued up will be handled in a batch, one after the other,
> without yielding to the browser (previously re-frame yielded to the browser
> before every single event).
>
> -- https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/releases/2015.md#050--2015-11-5

Here's a practical example you can try in a shadow-cljs :mobile REPL to see the
batching behavior in practice.

```clojure
;; A dummy event that toggles between DEBUG and INFO levels.
(re-frame/reg-event-fx :dummy-event
  (fn [{:keys [db]}]
    {:db (update-in db
                    [:profile/profile :log-level]
                    (fn [level]
                      (if (= "DEBUG" level)
                        "INFO"
                        "DEBUG")))}))

(def timer
  (js/setInterval #(re-frame/dispatch [:dummy-event])
                  50))

;; 1. In component status-im.ui.screens.advanced-settings.views/advanced-settings,
;; add a print call to see when it's re-rendered by Reagent because the
;; subscription :log-level/current-log-level will be affected by our dummy event.
;;
;; 2. Also add a print call to the subscription :log-level/current-log-level to
;; see that the subscription does NOT re-run on every dispatch.

;; Remember to eval this expression to cancel the timer.
(js/clearInterval timer)
```

If you run the above timer with 50ms interval, you'll see a fair amount of
batching happening. You can infer that's the case because you'll see way less
than 20 print statements per second (so way less than 20 recomputations of the
subscription, which is the max theoretical limit).

When the interval is reduced even more, to say 10ms (to simulate lots of
dispatches in a row), sometimes you don't see a single recomputation in a 5s
window because re-frame is too busy processing events.

This shows just how critical it is to have event handlers finishing as soon as
possible to relinquish control back to the UI thread, otherwise responsiveness
is affected. It also shows that too many dispatches in a row can be bad, just as
big event handlers would block the batch for too long. You see here that
dispatching events in succession does NOT cause needless re-computations.

Of course there's an overhead of using re-frame.core/dispatch instead of calling
a Clojure function, but the trade-off is clearly documented: the more we
process in a single event, the less responsive the app may be because re-frame
won't be able to relinquish control back to the UI thread. The total time to
process the batch increases, but re-frame can't stop in the middle compared to
when different dispatches are used.

Thus, I believe this rf/merge pattern is harmful as a default practice in an
environment such as ours, where it's desirable end-users feel a snappy RN app. I
actually firmly believe we can improve the app's responsiveness by not
coalescing events by default. We're also preventing status-mobile from taking
the most advantage from future improvements in re-frame's scheduler. I can
totally see us experimenting with other algorithms in the scheduler to best fit
our needs. We should not blindly reduce the number of events as stated here
https://github.com/status-im/status-mobile/pull/2634#discussion_r155243127.

Solution: only coalesce events into one pile when it's strictly desirable to
atomically update the app db to avoid inconsistencies, otherwise, let the
re-frame scheduler do its job by using fx, not rf/merge. When needed, embrace
*eventual app db consistency* as a way to achieve lower UI latency, i.e. write
fast and short events, intentionally use :dispatch-later or other timing effects
to bend the re-frame's scheduler to your will.

There's another argument in favor of using something like rf/merge which I would
like to deconstruct. rf/merge gives us a way to reuse computations from
different events, which is nice. The thing here is that we don't need rf/merge
or re-frame to reuse functions across namespaces. rf/merge complects re-frame
with the need to reuse transformations.

Instead, the solution is as trivial as it gets, reuse app db "transformers"
across events by extracting the logic to data store namespaces
(src/status_im/data_store). This solution has the added benefit of not causing
cyclic dependency errors.

### Problem 4: Clojure's language server doesn't understand code under rf/defn

Nowadays, many (if not most) Clojure devs rely on the Clojure Language Server
https://github.com/clojure-lsp/clojure-lsp to be more effective. It is an
invaluable tool, but it doesn't work well with the macro rf/defn, and it's a
constant source of frustration when working in event namespaces. Renaming
symbols inside the macro don't work, finding references, jumping to local
bindings, etc.

Solution: don't use rf/defn, instead use re-frame's reg-event-fx function and
clojure-lsp will understand all the code inside event handlers.

### Problem 5: Unit tests for events need to "test the world"

Re-frame's author strongly recommends testing events that contain non-trivial
data transformations, and we do have many in status-mobile (note: let's not
confuse with integration tests in status_im/integration_test.cljs). That, and
non-trivial layer-3 subscriptions should be covered too. The reasoning is that
if we have a well developed and tested state layer, many UI bugs can be
prevented as the software evolves, since the UI is partially or greatly derived
from the global state. See re-frame: What to Test?
https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/Testing.md#what-to-test.
See PR Introduce subscription tests
https://github.com/status-im/status-mobile/pull/14472, where I share more
details about re-frame's testing practices.

When we use rf/merge, we make unit testing events a perennial source of
frustration because too many responsibilities are aggregated in a single event.
Unfortunately, we don't have many devs in the team that attempted to write unit
tests for events to confirm my claim, but no worries, let's dive into a real
example.

In a unit test for an event, we want to test that, given a cofx and args, the
event handler returns the expected map of effects with the correct values
(usually db transformations).

Let's assume we need to test the following event. The code is still using the
combo rf/defn & rf/merge.

```clojure
(rf/defn accept-notification-success
  {:events [:activity-center.notifications/accept-success]}
  [{:keys [db] :as cofx} notification-id {:keys [chats]}]
  (when-let [notification (get-notification db notification-id)]
    (rf/merge cofx
              (chat.events/ensure-chats (map data-store.chats/<-rpc chats))
              (notifications-reconcile [(assoc notification :read true :accepted true)]))))
```

As you can see, we're "rf/merging" two other functions, namely ensure-chats and
notifications-reconcile. In fact, ensure-chats is not registered in re-frame,
but it's 99% defined as if it's one because it needs to be "mergeable" according
to the rules of rf/merge. Both of these "events" are quite complicated under the
hood and should be unit tested on their own.

Now here goes the unit test. Don't worry about the details, except for the expected output.

```clojure
(deftest accept-notification-success-test
  (testing "marks notification as accepted and read, then reconciles"
    (let [notif-1          {:id "0x1" :type types/private-group-chat}
          notif-2          {:id "0x2" :type types/private-group-chat}
          notif-2-accepted (assoc notif-2 :accepted true :read true)
          cofx             {:db {:activity-center {:filter        {:type types/no-type :status :all}
                                                   :notifications [notif-2 notif-1]}}}

          expected {:db         {:activity-center {:filter        {:type 0 :status :all}
                                                   :notifications [notif-2-accepted notif-1]}
                                 :chats           {}
                                 :chats-home-list nil}
                    ;; *** HERE ***
                    :dispatch-n [[:activity-center.notifications/fetch-unread-count]
                                 [:activity-center.notifications/fetch-pending-contact-requests]]}
          actual   (events/accept-notification-success cofx (:id notif-2) nil)]
      (is (= expected actual)))))
```

Notice the map has a :dispatch-n effect and other stuff inside of it that are
not the responsibility of the event under test to care about. This happens
because rf/merge forces the event handler to compute/call everything in one go.
And things get MUCH worse when you want to test an event A that uses rf/merge,
but A calls other events B and C that also use rf/merge (e.g. event
:profile.login/get-chats-callback). At that point you flip the table in horror
😱, but testing events and maintaining them should be trivial.

Solution: Use re-frame's `fx` effect.

Here's the improved implementation and its accompanying test.

```clojure
(defn accept-notification-success
  [{:keys [db]} [notification-id {:keys [chats]}]]
  (when-let [notification (get-notification db notification-id)]
    (let [new-notifications [(assoc notification :read true :accepted true)]]
      {:fx [[:dispatch [:chat/ensure-chats (map data-store.chats/<-rpc chats)]]
            [:dispatch [:activity-center.notifications/reconcile new-notifications]]]})))

(re-frame/reg-event-fx :activity-center.notifications/accept-success accept-notification-success)

(deftest accept-notification-success-test
  (testing "marks notification as accepted and read, then reconciles"
    (let [notif-1          {:id "0x1" :type types/private-group-chat}
          notif-2          {:id "0x2" :type types/private-group-chat}
          notif-2-accepted (assoc notif-2 :accepted true :read true)
          cofx             {:db {:activity-center {:filter        {:type types/no-type :status :all}
                                                   :notifications [notif-2 notif-1]}}}

          ;; *** HERE ***
          expected {:fx [[:dispatch [:chat/ensure-chats []]]
                         [:dispatch [:activity-center.notifications/reconcile [notif-2-accepted]]]]}
          actual   (events/accept-notification-success cofx [(:id notif-2) nil])]
      (is (= expected actual)))))
```

Notice how the test expectation is NOT verifying what other events do (it's
actually "impossible" now). Using fx completely decouples events and makes
testing them a joy again.

### Problem 6: Unordered effects

status-mobile still uses the legacy way to describe the effects map, which has
the problem that their order is unpredictable.

> Prior to v1.1.0, the answer is: no guarantees were provided about ordering.
> Actual order is an implementation detail upon which you should not rely.
>
> -- https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/Effects.md#order-of-effects

> In fact, with v1.1.0 best practice changed to event handlers should only
> return two effects :db and :fx, in which case :db was always done first and
> then :fx, and within :fx the ordering is sequential. This new approach is more
> about making it easier to compose event handlers from many smaller functions,
> but more specificity around ordering was a consequence.
>
> -- https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/Effects.md#order-of-effects

### Problem 7: Usage of deprecated effect dispatch-n

We have 35 usages, the majority in new code using dispatch-n, which has been
officially deprecated in favor of multiple dispatch tuples in fx. See
https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/api-builtin-effects.md#L114

### Problem 8: Complexity 🧙‍♂️

Have you ever tried to understand and/or explain how rf/merge and rf/defn work?
They have their fare share of complexity and have tripped up many contributors.

This is not ideal if we want to create a project where contributors can learn
re-frame as quickly as possible. Re-frame is already complicated enough to grasp
for many, the added abstractions should be valuable enough to justify.

Interestingly, rf/merge is a stateful function, and although this is not a
problem in practice, it is partially violating re-frame's spirit of only using
pure functions inside event handlers.

### Problem 9: Using a wrapping macro rf/defn instead of global interceptors

When rf/defn was created inside status-mobile, re-frame didn't have global
interceptors yet (which were introduced 3+ years ago). We no longer have this
limitation after we upgraded our old re-frame version in PR
https://github.com/status-im/status-mobile/pull/15997.

Global interceptors are a simple and functional abstraction to specify functions
that should run on every event, for example, for debugging during development,
logging, etc. This PR already shows this is possible by removing the wrapping
function utils.re-frame/register-handler-fx without causing any breakage.

## Conclusion

By embracing re-frame's best practices for describing effects
https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/FAQs/BestPractice.md#use-the-fx-effect,
we can solve long standing issues that affect every contributor at different
levels and bring the following benefits:

- Simplify the codebase.
- Bring back the DX we all deserve, i.e. Clojure Language Server and clj-kondo
  fully working in event namespaces.
- Greatly facilitate the testability of events.
- Give devs more flexibility to make the app more responsive, because the new
  default would not coalesce events, which in turn, would block the UI thread
  for shorter periods of time. At least that's the theory, but exceptions will
  be found.

The actions to achieve those benefits are:

- Don't use the macro approach, replace rf/defn with
  re-frame.core/reg-event-fx.
- Don't use rf/merge, simply use re-frame's built-in effect :fx.
- Don't call event handlers as normal functions, just as we don't directly call
  subscription handlers. Use re-frame's built-in effect :fx.

## How do we refactor the remainder of the code?

Some numbers first:

- There are 228 events defined with rf/defn in src/status-im2/.
- There are 34 usages of rf/merge in src/status_im2/.

## Resources

- Release notes where fx was introduced in re-frame:
  https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/releases/2020.md#110-2020-08-24
2023-10-05 16:11:45 -03:00
Icaro Motta b73ac6b107 Upgrade clj-kondo and configure new linters (#17543)
- Upgrade clj-kondo to latest version to take advantage of new linters. From
  version 2023.04.14
  https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md#20230414 to
  2023.09.07
  https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md#20230907
- Use new linter ":unused-alias" and set at WARN level for the moment, because
  otherwise the PR would increase a bit too much, but it did catch many unused
  "require" aliases. Added in version 2023.09.07
  https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md#20230907
- Use new linter ":case-symbol-test" and fix the reported errors, added in
  version 2023.07.13
  https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md#20230713
- Use new linters ":equals-true", ":plus-one", and ":minus-one" and fix reported
  errors, added in version 2023.05.18
  https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md#20230518
- Raise level from WARN to ERROR for linter "uninitialized-var".
- Explicitly add ":case-duplicate-test" to clj-kondo config, renamed in version
  2023.07.13
  https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md#20230713
- Explicitly add ":case-quoted-test" to clj-kondo config, renamed in version
  2023.07.13
  https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md#20230713
- Explicitly add ":deprecated-namespace" to clj-kondo config, added in version
  2023.07.13
  https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md#20230713

Fixes https://github.com/status-im/status-mobile/issues/17287
2023-10-05 15:50:57 -03:00
Anton Iakimov d80fb0b3de update status/wakuv2 fleets DNS discovery enrtree
https://github.com/status-im/status-go/compare/e1354016...9034f0a9

https://github.com/status-im/infra-misc/issues/171
2023-10-05 18:22:42 +01:00
Andrea Maria Piana ef80fc567f Update waku nodes
https://github.com/status-im/status-go/compare/e1354016...b8041fc8
2023-10-05 18:22:10 +01:00
Siddarth Kumar 7043e03dd4 Enter scan code tab in Syncing (#16852)
* feat: add enter scan code view

This commit adds the enter sync code UI.

Fixes : #16062

Design link : https://www.figma.com/file/V6nlpAWIf2e1XU8RJ9yQPe/Syncing-for-Mobile?node-id=675%3A179729&mode=dev

Review notes :
We do not make use of quo2/input here because currently that component does not allow stretching to fit in sync code which spans upto 3 lines.
2023-10-05 21:05:54 +05:30
182 changed files with 3415 additions and 2490 deletions
+10 -2
View File
@@ -9,23 +9,29 @@
status-im.utils.styles/defn clojure.core/defn
test-helpers.unit/deftest-sub clojure.core/defn
taoensso.tufte/defnp clojure.core/defn}
:linters {:clj-kondo-config {:level :error}
:linters {:case-duplicate-test {:level :error}
:case-quoted-test {:level :error}
:case-symbol-test {:level :error}
:clj-kondo-config {:level :error}
:cond-else {:level :error}
:consistent-alias {:level :error
:aliases {clojure.string string
clojure.set set
clojure.walk walk
taoensso.timbre log}}
:deprecated-namespace {:level :warning}
:docstring-blank {:level :error}
:equals-true {:level :error}
:inline-def {:level :error}
:invalid-arity {:skip-args [status-im.utils.fx/defn utils.re-frame/defn]}
:loop-without-recur {:level :error}
:minus-one {:level :error}
:misplaced-docstring {:level :error}
:missing-body-in-when {:level :error}
:missing-clause-in-try {:level :error}
:missing-else-branch {:level :error}
:not-empty? {:level :error}
:quoted-case-test-constant {:level :error}
:plus-one {:level :error}
:redundant-do {:level :error}
:redundant-let {:level :error}
:refer-all {:level :error}
@@ -48,6 +54,8 @@
number
status-im.test-helpers/restore-app-db]}
:unresolved-var {:level :error}
:uninitialized-var {:level :error}
:unused-alias {:level :warning}
:unused-binding {:level :error}
:unused-import {:level :error}
:unused-namespace {:level :error}
+1
View File
@@ -31,6 +31,7 @@
"defview" :arg1-body
"letsubs" :binding
"with-let" "let"
"reg-event-fx" :arg1-pair
"testing" :arg1-body
"deftest-sub" :arg1-body
"wait-for" :arg1-body
@@ -0,0 +1,31 @@
### How should the paste button behave when clipboard is empty?
This question first came up as a review comment to this PR
https://github.com/status-im/status-mobile/pull/16852
There were 2 considerations :
- Either keep the paste button disabled when there is nothing in the clipboard OR
- Always keep the paste button enabled
There were positives and negatives for both approaches.
Positives of keeping paste button disabled when there is nothing in clipboard would require us to
check the value of clipboard as soon as the component is mounted (i.e when the user first sees
the screen). In iOS this means a native permissions dialog would appear requesting for permissions
to paste from the clipboard.
Negatives of this approach is that as soon as any user navigates to this screen they are greeted with
this popup which can be annoying sometimes.
Positives of keeping paste button always enabled is that we can trigger a request to the clipboard on
tap of the paste button which would trigger the native permissions dialog requesting for permissions
to paste from the clipboard.
In this case seeing this dialog is okay because the user has initiated a paste action.
Negatives of this approach is that in the event the clipboard is empty the user will still see the
system dialog and on approving nothing will be pasted (because the clipboard was empty).
This behaviour can be confusing.
On consulting the Design Team via discord it was concluded that out of the two approaches
having the paste button always enabled is a better UX overall.
+17 -10
View File
@@ -467,21 +467,28 @@ Prefer the pure version of `:json-rpc/call` (no callbacks).
### Registering event handlers
Events must always be declared with the `utils.fx/defn` macro. Also, don't use
Register events with `re-frame.core/reg-event-fx` and follow [re-frame's best
practice](https://github.com/day8/re-frame/blob/39adca93673f334dc751ee2d99d340b51a9cc6db/docs/FAQs/BestPractice.md#use-the-fx-effect)
so use only `:db` and `:fx` effects. `rf/merge` is deprecated and should not be
used in the new code in `src/status_im2/`. Don't use
`re-frame.core/reg-event-db`.
```clojure
;; bad
(re-frame/reg-event-fx
:wakuv2.ui/save-all-confirmed
(fn [{:keys [db] :as cofx}]
...))
(rf/defn invite-people-pressed
{:events [:communities/invite-people-pressed]}
[cofx id]
(rf/merge cofx
(reset-community-id-input id)
(bottom-sheet/hide-bottom-sheet)
(navigation/open-modal :invite-people-community {:invite? true})))
;; good
(fx/defn save-all
{:events [:wakuv2.ui/save-all-confirmed]}
[{:keys [db] :as cofx}]
...)
(re-frame/reg-event-fx :communities/invite-people-pressed
(fn [{:keys [db]} [id]]
{:db (assoc db :communities/community-id-input id)
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:open-modal :invite-people-community {:invite? true}]]]}))
```
### Registering top-level re-frame subscriptions
@@ -805,7 +812,7 @@ alternative.
Please check the [Clojure Style](https://guide.clojure.style/#deprecated) documentation
To reduce visual clutter from deprecated methods in your text editor, consult this [example](https://rider-support.jetbrains.com/hc/en-us/community/posts/4419728641810-How-to-disable-the-the-strike-thru-for-deprecated-methods-in-Javascript-#:~:text=Try%20disabling%20%22Preferences%20%7C%20Editor%20%7C,It%20works%20for%20me). The approach can be adapted for settings in VSCode, Emacs, VIM, and others.
### Test structure
[Unit tests](#glossary) should be created alongside their respective source
+13
View File
@@ -38,6 +38,19 @@ in {
};
});
# Clojure's linter receives frequent upgrades, and we want to take advantage
# of the latest available rules.
clj-kondo = super.clj-kondo.override rec {
buildGraalvmNativeImage = args: super.buildGraalvmNativeImage (args // rec {
inherit (args) pname;
version = "2023.09.07";
src = super.fetchurl {
url = "https://github.com/clj-kondo/${pname}/releases/download/v${version}/${pname}-${version}-standalone.jar";
sha256 = "sha256-F7ePdITYKkGB6nsR3EFJ7zLDCUoT0g3i+AAjXzBd624=";
};
});
};
# Checks fail on darwin.
git-lfs = super.git-lfs.overrideAttrs (old: {
doCheck = false;
-1
View File
@@ -35,7 +35,6 @@
"functional-red-black-tree": "^1.0.1",
"i18n-js": "^3.3.0",
"node-libs-react-native": "^1.2.1",
"qrcode": "^1.4.1",
"react": "18.0.0",
"react-dom": "18.0.0",
"react-native": "0.69.10",
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 539 KiB

After

Width:  |  Height:  |  Size: 430 KiB

@@ -35,7 +35,7 @@
:indicator-size status indicator outer radius, set to nil or 0 when no indicator
:indicator-border `indicator-size`-`indicator-border` is the inner radius
:indicator-color color for status indicator
:theme :light or :dark
:theme :light or :dark
:background-color intials avatar background color
:color intials avatar text color
:size intials avatar radius
@@ -28,11 +28,11 @@
(defn wallet-user-avatar
"params, first name, last name, color, size
and if it's dark or not!"
[{:keys [f-name l-name color size]
:or {f-name "John"
l-name "Doe"
color :red
size :x-large}}]
[{:keys [f-name l-name customization-color size]
:or {f-name "John"
l-name "Doe"
customization-color :red
size :x-large}}]
(let [circle-size (size circle-sizes)
small? (= size :small)
f-name-initial (-> f-name
@@ -41,8 +41,8 @@
l-name-initial (-> l-name
string/upper-case
(subs 0 1))
circle-color (colors/custom-color color 50 20)
text-color (colors/custom-color-by-theme color 50 60)]
circle-color (colors/custom-color customization-color 50 20)
text-color (colors/custom-color-by-theme customization-color 50 60)]
[rn/view
{:style {:width circle-size
:height circle-size
@@ -5,8 +5,8 @@
[{:keys [pressed? blur? theme]}]
(cond
(and pressed? blur?) (colors/theme-colors colors/neutral-80-opa-20 colors/white-opa-20 theme)
(= pressed? true) (colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
(= blur? true) (colors/theme-colors colors/neutral-80-opa-10 colors/white-opa-10 theme)
pressed? (colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
blur? (colors/theme-colors colors/neutral-80-opa-10 colors/white-opa-10 theme)
:else (colors/theme-colors colors/neutral-30 colors/neutral-70 theme)))
(defn get-label-color
@@ -3,7 +3,7 @@
(defn get-border-color
[{:keys [pressed? theme]}]
(if (= pressed? true)
(if pressed?
(colors/theme-colors colors/neutral-40 colors/neutral-60 theme)
(colors/theme-colors colors/neutral-30 colors/neutral-70 theme)))
@@ -6,7 +6,7 @@
(defn generate-years
[current-year]
(let [current-year current-year]
(reverse (vec (range (- current-year 100) (+ current-year 1))))))
(reverse (vec (range (- current-year 100) (inc current-year))))))
(defn current-year
[]
@@ -55,7 +55,7 @@
[rn/view {:accessibility-label :line-chart}
[charts/line-chart
{:height 96
:width (+ width 1)
:width (inc width)
:max-value max-value
:min-value 0
:adjust-to-width true
+9 -5
View File
@@ -21,11 +21,15 @@
^{:key icon-name}
(if-let [svg-icon (icons.svg/get-icon icon-name size)]
[svg-icon
{:size size
:color (when (valid-color? color) color)
:color-2 (when (valid-color? color-2) color-2)
:accessibility-label accessibility-label
:style container-style}]
(cond-> {:size size
:accessibility-label accessibility-label
:style container-style}
(and color (valid-color? color))
(assoc :color color)
(and color-2 (valid-color? color-2))
(assoc :color-2 color-2))]
[rn/image
{:style
(merge {:width size
@@ -14,7 +14,8 @@
(defn chat-key-text
[]
{:color (colors/theme-colors colors/neutral-40 colors/neutral-50)
:margin-left 8})
:margin-left 8
:padding-top 1})
(defn middle-dot-chat-key
[]
@@ -12,12 +12,13 @@
:align-items :center})
(defn center-content-container
[centered?]
[centered? center-opacity]
{:flex 1
:margin-horizontal 12
:flex-direction :row
:align-items :center
:justify-content (if centered? :center :flex-start)})
:justify-content (if centered? :center :flex-start)
:opacity center-opacity})
(def right-actions-container
{:flex-direction :row})
@@ -79,6 +80,7 @@
:text-align-vertical :center}))
(def community-network-logo
{:width 24
:height 24
:margin-right 6})
{:width 24
:height 24
:border-radius 12
:margin-right 6})
@@ -86,8 +86,8 @@
nil)])
(defn- title-center
[{:keys [centered? title]}]
[rn/view {:style (style/center-content-container centered?)}
[{:keys [centered? title center-opacity]}]
[rn/view {:style (style/center-content-container centered? center-opacity)}
[text/text
{:weight :medium
:size :paragraph-1
@@ -95,13 +95,13 @@
title]])
(defn- dropdown-center
[{:keys [theme background dropdown-on-press dropdown-selected? dropdown-text]}]
[{:keys [theme background dropdown-on-press dropdown-selected? dropdown-text center-opacity]}]
(let [dropdown-type (cond
(= background :photo) :grey
(and (= theme :dark) (= background :blur)) :grey
:else :ghost)
dropdown-state (if dropdown-selected? :active :default)]
[rn/view {:style (style/center-content-container true)}
[rn/view {:style (style/center-content-container true center-opacity)}
[dropdown/view
{:type dropdown-type
:state dropdown-state
@@ -111,8 +111,8 @@
dropdown-text]]))
(defn- token-center
[{:keys [theme background token-logo token-name token-abbreviation]}]
[rn/view {:style (style/center-content-container false)}
[{:keys [theme background token-logo token-name token-abbreviation center-opacity]}]
[rn/view {:style (style/center-content-container false center-opacity)}
[rn/image {:style style/token-logo :source token-logo}]
[text/text
{:style style/token-name
@@ -128,8 +128,8 @@
token-abbreviation]])
(defn- channel-center
[{:keys [theme background channel-emoji channel-name channel-icon]}]
[rn/view {:style (style/center-content-container false)}
[{:keys [theme background channel-emoji channel-name channel-icon center-opacity]}]
[rn/view {:style (style/center-content-container false center-opacity)}
[rn/text {:style style/channel-emoji}
channel-emoji]
[text/text
@@ -141,11 +141,11 @@
[icons/icon channel-icon {:size 16 :color (style/channel-icon-color theme background)}]])
(defn- title-description-center
[{:keys [background theme picture title description]}]
[rn/view {:style (style/center-content-container false)}
[{:keys [background theme picture title description center-opacity]}]
[rn/view {:style (style/center-content-container false center-opacity)}
(when picture
[rn/view {:style style/group-avatar-picture}
[group-avatar/view {:picture picture :size 28}]])
[group-avatar/view {:picture picture :size :size-28}]])
[rn/view {:style style/title-description-container}
[text/text
{:style style/title-description-title
@@ -161,11 +161,11 @@
description]]])
(defn- community-network-center
[{:keys [type community-logo network-logo community-name network-name]}]
[{:keys [type community-logo network-logo community-name network-name center-opacity]}]
(let [community? (= type :community)
shown-logo (if community? community-logo network-logo)
shown-name (if community? community-name network-name)]
[rn/view {:style (style/center-content-container false)}
[rn/view {:style (style/center-content-container false center-opacity)}
[rn/image
{:style style/community-network-logo
:source shown-logo}]
@@ -176,8 +176,8 @@
shown-name]]))
(defn- wallet-networks-center
[{:keys [networks networks-on-press background]}]
[rn/view {:style (style/center-content-container true)}
[{:keys [networks networks-on-press background center-opacity]}]
[rn/view {:style (style/center-content-container true center-opacity)}
[network-dropdown/view
{:state :default
:on-press networks-on-press
@@ -6,10 +6,14 @@
(def text-container {:flex 1})
(def title
(def icon-title
{:color colors/white
:height 22})
(def main-title
{:color colors/white
:height 26})
(def subtitle
{:color colors/white-opa-70
:height 18})
@@ -33,21 +37,23 @@
:height 32})
(def main-variant
{:flex 1
:margin-bottom -8})
{:flex 1})
(def main-variant-text-container
{:height 62
:padding-top 10
:padding-bottom 12
:padding-horizontal 12})
(def touchable-overlay {:border-radius 16})
(defn main-variant-image
[max-height]
{:flex 1
:max-height max-height})
(def main-button
{:padding-horizontal 12 :margin-bottom 12 :margin-top 8})
(defn card
[height]
{:background-color colors/white-opa-5
:border-radius 16
:height height})
(def main-variant-extra-space {:height 8})
@@ -1,55 +1,71 @@
(ns quo2.components.onboarding.small-option-card.view
(:require [quo2.components.markdown.text :as text]
[quo2.components.onboarding.small-option-card.style :as style]
[quo2.components.buttons.button.view :as button]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]))
(defn- texts
[{:keys [title subtitle]}]
[rn/view {:style style/text-container}
[text/text
{:style style/title
:size :paragraph-1
:weight :semi-bold
:number-of-lines 1}
title]
[text/text
{:style style/subtitle
:size :paragraph-2
:weight :regular
:number-of-lines 1}
subtitle]])
(defn- icon-variant
[{:keys [title subtitle image]}]
[rn/view {:style style/icon-variant}
[{:keys [title subtitle image accessibility-label on-press]}]
[rn/pressable
{:accessibility-label accessibility-label
:style style/icon-variant
:underlay-color colors/white-opa-5
:on-press on-press}
[rn/view {:style style/icon-variant-image-container}
[fast-image/fast-image
{:accessibility-label :small-option-card-icon-image
:style style/icon-variant-image
:resize-mode :contain
:source image}]]
[texts
{:title title
:subtitle subtitle}]])
[rn/view {:style style/text-container}
[text/text
{:style style/icon-title
:size :paragraph-1
:weight :semi-bold
:number-of-lines 1}
title]
[text/text
{:style style/subtitle
:size :paragraph-2
:weight :regular
:number-of-lines 1}
subtitle]]])
(defn- main-variant
[{:keys [title subtitle image max-height]}]
[{:keys [title subtitle button-label image max-height accessibility-label on-press]}]
[rn/view {:style style/main-variant}
[rn/view {:style style/main-variant-text-container}
[texts
{:title title
:subtitle subtitle}]]
[text/text
{:style style/main-title
:size :heading-2
:weight :semi-bold
:number-of-lines 1}
title]
[text/text
{:style style/subtitle
:size :paragraph-2
:weight :regular
:number-of-lines 1}
subtitle]]
[fast-image/fast-image
{:accessibility-label :small-option-card-main-image
:style {:flex 1 :max-height max-height}
:style (style/main-variant-image max-height)
:resize-mode :contain
:source image}]])
:source image}]
[button/button
{:on-press on-press
:accessibility-label accessibility-label
:type :grey
:size 40
:container-style style/main-button
:theme :dark
:background :blur}
button-label]])
(defn small-option-card
"Variants: `:main` or `:icon`"
[{:keys [variant title subtitle image max-height on-press accessibility-label]
[{:keys [variant title subtitle button-label image max-height on-press accessibility-label]
:or {variant :main accessibility-label :small-option-card}}]
(let [main-variant? (= variant :main)
card-component (if main-variant? main-variant icon-variant)
@@ -57,18 +73,12 @@
(not main-variant?) style/icon-variant-height
max-height (min max-height style/main-variant-height)
:else style/main-variant-height)]
[rn/view
[rn/touchable-highlight
{:accessibility-label accessibility-label
:style style/touchable-overlay
:active-opacity 1
:underlay-color colors/white-opa-5
:on-press on-press}
[rn/view {:style (style/card card-height)}
[card-component
{:title title
:subtitle subtitle
:image image
:max-height max-height}]]]
(when main-variant?
[rn/view {:style style/main-variant-extra-space}])]))
[rn/view {:style (style/card card-height)}
[card-component
{:title title
:subtitle subtitle
:button-label button-label
:on-press on-press
:accessibility-label accessibility-label
:image image
:max-height max-height}]]))
@@ -1,43 +0,0 @@
(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]
[react-native.core :as rn]))
(defn- add-reaction-internal
[{:keys [on-press theme]}]
[rn/touchable-opacity
{:on-press on-press
:accessibility-label :emoji-reaction-add
:style (style/add-reaction theme)}
[icons/icon :i/add-reaction
{:size 20
:color (colors/theme-colors colors/neutral-50
colors/neutral-40
theme)}]])
(def add-reaction (theme/with-theme add-reaction-internal))
(defn reaction-internal
"Add your emoji as a param here"
[{:keys [emoji clicks neutral? on-press accessibility-label on-long-press theme]}]
(let [numeric-value (int clicks)]
[rn/touchable-opacity
{:on-press on-press
:on-long-press on-long-press
:accessibility-label accessibility-label
:style (style/reaction neutral? theme)}
[rn/image
{:style {:width 16 :height 16}
:accessibility-label :emoji
:source (resource/get-reaction emoji)}]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style style/reaction-count}
(str (if (pos? numeric-value) numeric-value 1))]]))
(def reaction (theme/with-theme reaction-internal))
-33
View File
@@ -1,33 +0,0 @@
(ns quo2.components.reactions.style
(:require [quo2.foundations.colors :as colors]))
(def reaction-styling
{:flex-direction :row
:justify-content :center
:align-items :center
:padding-horizontal 8
:border-radius 8
:height 24})
(defn add-reaction
[theme]
(merge reaction-styling
{:padding-horizontal 8
:border-width 1
:border-color (colors/theme-colors colors/neutral-20
colors/neutral-70
theme)}))
(defn reaction
[neutral? theme]
(merge reaction-styling
{:border-color (colors/theme-colors colors/neutral-20
colors/neutral-80
theme)
:border-width 1
:background-color (if neutral?
(colors/theme-colors colors/neutral-10
colors/neutral-80-opa-40)
:transparent)}))
(def reaction-count {:margin-left 4})
@@ -0,0 +1,12 @@
(ns quo2.components.selectors.react.style)
(def container
{:flex-direction :row
:justify-content :flex-start
:flex 1
:flex-wrap :wrap
:margin-bottom -6})
(def reaction-container
{:margin-bottom 6
:margin-right 6})
@@ -0,0 +1,26 @@
(ns quo2.components.selectors.react.view
(:require [quo2.components.selectors.react-selector.view :as react-selector]
[quo2.components.selectors.react.style :as style]
[react-native.core :as rn]))
(defn view
[{:keys [reactions on-press on-long-press add-reaction? on-press-add use-case container-style]}]
[rn/view {:style (merge style/container container-style)}
(for [emoji-reaction reactions
:let [{:keys [emoji emoji-id emoji-reaction-id quantity own]} emoji-reaction]]
[react-selector/view
{:key emoji-reaction-id
:emoji emoji
:state (if own :pressed :not-pressed)
:use-case use-case
:container-style style/reaction-container
:clicks quantity
:on-press #(on-press emoji-reaction)
:on-long-press #(on-long-press emoji-reaction)
:accessibility-label (str "emoji-reaction-" emoji-id)}])
(when add-reaction?
[react-selector/view
{:on-press on-press-add
:state :add-reaction
:use-case use-case
:accessibility-label (str "emoji-add-reaction")}])])
@@ -0,0 +1,44 @@
(ns quo2.components.selectors.react-selector.style
(:require [quo2.foundations.colors :as colors]))
(defn add-reaction
[pinned? theme]
(let [border-color (if pinned?
(colors/theme-colors colors/neutral-80-opa-5 colors/white-opa-5 theme)
(colors/theme-colors colors/neutral-20 colors/neutral-70 theme))]
(cond-> {:justify-content :center
:align-items :center
:padding-horizontal 7
:border-radius 8
:border-width 1
:height 24
:border-color border-color})))
(defn reaction
[pressed? pinned? theme]
(let [background-color (cond
(not pressed?) :transparent
pinned? (colors/theme-colors colors/neutral-80-opa-5
colors/white-opa-5
theme)
:else (colors/theme-colors colors/neutral-10
colors/neutral-80-opa-40
theme))
border-color (if pinned?
(colors/theme-colors colors/neutral-80-opa-5
colors/white-opa-5
theme)
(colors/theme-colors colors/neutral-20
colors/neutral-80
theme))]
{:flex-direction :row
:justify-content :center
:align-items :center
:padding-horizontal 8
:border-radius 8
:height 24
:border-width 1
:background-color background-color
:border-color border-color}))
(def reaction-count {:margin-left 4})
@@ -0,0 +1,46 @@
(ns quo2.components.selectors.react-selector.view
(:require [quo2.components.markdown.text :as text]
[quo2.components.selectors.reaction-resource :as reaction.resource]
[quo2.components.selectors.react-selector.style :as style]
[quo2.theme :as quo.theme]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]
[react-native.core :as rn]))
(defn- view-internal
[{:keys [emoji clicks state use-case on-press accessibility-label on-long-press container-style
theme]}]
(let [numeric-value (int clicks)
icon-color (if (= :pinned use-case)
(colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70 theme)
(colors/theme-colors colors/neutral-50 colors/neutral-40 theme))]
(if (= :add-reaction state)
[rn/touchable-opacity
{:on-press on-press
:accessibility-label :emoji-reaction-add
:style (style/add-reaction
(= :pinned use-case)
theme)}
[icons/icon :i/add-reaction
{:size 20
:color icon-color}]]
[rn/touchable-opacity
{:on-press on-press
:on-long-press on-long-press
:accessibility-label accessibility-label
:style (merge (style/reaction (= :pressed state)
(= :pinned use-case)
theme)
container-style)}
[rn/image
{:style {:width 15 :height 15}
:accessibility-label :emoji
:source (reaction.resource/get-reaction emoji)}]
[text/text
{:size :paragraph-2
:weight :semi-bold
:style style/reaction-count}
(str (if (pos? numeric-value) numeric-value 1))]])))
(def view (quo.theme/with-theme view-internal))
@@ -1,4 +1,4 @@
(ns quo2.components.reactions.resource
(ns quo2.components.selectors.reaction-resource
(:require [clojure.java.io :as io]
[clojure.string :as string]))
@@ -1,5 +1,5 @@
(ns quo2.components.reactions.resource
(:require-macros [quo2.components.reactions.resource :refer [resolve-all-reactions]]))
(ns quo2.components.selectors.reaction-resource
(:require-macros [quo2.components.selectors.reaction-resource :refer [resolve-all-reactions]]))
(def ^:private reactions
(resolve-all-reactions))
@@ -1,25 +0,0 @@
(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}}]]])))
@@ -1,23 +1,26 @@
(ns quo2.components.selectors.reactions.component-spec
(:require [quo2.components.selectors.reactions.view :as view]
(ns quo2.components.selectors.reactions-selector.component-spec
(:require [quo2.components.selectors.reactions-selector.view :as view]
[test-helpers.component :as h]))
(h/describe "Selectors > Reactions"
(h/test "renders component"
(h/render [view/view :reaction/sad])
(h/render [view/view {:emoji :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/render [view/view
{:emoji :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
(h/render [view/view
{:emoji :reaction/love
:on-press on-press
:start-pressed? true}])
(h/fire-event :press (h/get-by-label-text :reaction))
(h/was-called on-press)))))
@@ -1,4 +1,4 @@
(ns quo2.components.selectors.reactions.style
(ns quo2.components.selectors.reactions-selector.style
(:require [quo2.foundations.colors :as colors]))
(defn container
@@ -0,0 +1,23 @@
(ns quo2.components.selectors.reactions-selector.view
(:require [quo2.components.selectors.reaction-resource :as reactions.resource]
[quo2.components.selectors.reactions-selector.style :as style]
[react-native.core :as rn]
[reagent.core :as reagent]))
(defn view
[{:keys [start-pressed?]}]
(let [pressed? (reagent/atom start-pressed?)]
(fn [{:keys [emoji container-style on-press
accessibility-label]
:or {accessibility-label :reaction}}]
[rn/pressable
{:accessibility-label accessibility-label
:style (merge (style/container @pressed?)
container-style)
:on-press (fn [e]
(swap! pressed? not)
(when on-press
(on-press e)))}
[rn/image
{:source (reactions.resource/get-reaction emoji)
:style {:width 20 :height 20}}]])))
@@ -4,7 +4,6 @@
[quo2.components.settings.reorder-item.types :as types]
[quo2.components.settings.reorder-item.view :as reorder-item]
[quo2.foundations.colors :as colors]
[react-native.blur :as blur]
[react-native.core :as rn]
[quo2.components.settings.category.style :as style]
[quo2.theme :as quo.theme]
@@ -20,8 +19,6 @@
[{:keys [label data blur? theme]}]
(reagent/with-let [atom-data (reagent/atom data)]
[rn/view {:style style/container}
(when blur?
[rn/view (style/blur-container) [blur/view (style/blur-view)]])
[text/text
{:weight :medium
:size :paragraph-2
@@ -3,7 +3,6 @@
[quo2.components.markdown.text :as text]
[quo2.components.settings.settings-item.view :as settings-item]
[quo2.foundations.colors :as colors]
[react-native.blur :as blur]
[react-native.core :as rn]
[quo2.components.settings.category.style :as style]
[quo2.theme :as quo.theme]))
@@ -11,8 +10,6 @@
(defn- category-internal
[{:keys [label data blur? theme]}]
[rn/view {:style style/container}
(when blur?
[rn/view (style/blur-container) [blur/view (style/blur-view)]])
[text/text
{:weight :medium
:size :paragraph-2
@@ -3,8 +3,7 @@
(defn container
[size card? blur? theme]
{:flex 1
:flex-direction :row
{:flex-direction :row
:justify-content :space-between
:padding-vertical (when (= size :default) 8)
:padding-horizontal (when (= size :default) 12)
@@ -57,8 +57,11 @@
(i18n/label :t/days)])])
(defn- left-side
[{:keys [theme title status size blur? description icon subtitle label icon-color customization-color
emoji]}]
"The description can either be given as a string `description` or a component `custom-subtitle`"
[{:keys [theme title status size blur? description custom-subtitle icon subtitle label icon-color
customization-color
emoji]
:as props}]
[rn/view {:style style/left-side}
[left-title
{:title title
@@ -70,19 +73,21 @@
{:size size
:blur? blur?
:theme theme}]
[left-subtitle
{:theme theme
:size size
:description description
:icon icon
:icon-color icon-color
:blur? blur?
:subtitle subtitle
:customization-color customization-color
:emoji emoji}])])
(if custom-subtitle
[custom-subtitle props]
[left-subtitle
{:theme theme
:size size
:description description
:icon icon
:icon-color icon-color
:blur? blur?
:subtitle subtitle
:customization-color customization-color
:emoji emoji}]))])
(defn- right-side
[{:keys [label icon-right? icon-color communities-list]}]
[{:keys [label icon-right? icon icon-color communities-list]}]
[rn/view {:style style/right-container}
(case label
:preview [preview-list/view
@@ -95,16 +100,15 @@
nil)
(when icon-right?
[rn/view {:style (style/right-icon label)}
[icons/icon
(if (= :none label)
:i/copy
:i/chevron-right)
[icons/icon icon
{:accessibility-label :icon-right
:color icon-color
:size 20}]])])
(def view-internal
(fn [{:keys [blur? card? icon-right? label status size theme on-press communities-list] :as props}]
(fn [{:keys [blur? card? icon-right? icon label status size theme on-press communities-list
container-style]
:as props}]
(let [icon-color (if (or blur? (= :dark theme))
colors/neutral-40
colors/neutral-50)]
@@ -114,12 +118,13 @@
{:accessibility-label :data-item
:disabled (not icon-right?)
:on-press on-press
:style (style/container size card? blur? theme)}
:style (merge (style/container size card? blur? theme) container-style)}
[left-side props]
(when (and (= :default status) (not= :small size))
[right-side
{:label label
:icon-right? icon-right?
:icon icon
:icon-color icon-color
:communities-list communities-list}])]))))
@@ -8,7 +8,7 @@
(defn reorder-item
[item type {:keys [blur? drag]}]
(case type
(condp = type
types/item [item/view item blur? drag]
types/placeholder [placeholder/view item]
types/skeleton [skeleton/view]
+37 -7
View File
@@ -1,14 +1,44 @@
(ns quo2.components.share.qr-code.style
(:require [quo2.foundations.colors :as colors]))
(def container
{:flex-direction :row
:justify-content :center})
(defn container
[size]
(if size
{:width size
:height size}
{:flex 1}))
(defn image
[width height]
{:width width
:height height
(def avatar-overlay
{:position :absolute
:top 0
:right 0
:left 0
:bottom 0
:justify-content :center
:align-items :center})
(defn qr-image
[size]
{:width (or size "100%")
:height (or size "100%")
:background-color colors/white-opa-70
:border-radius 12
:aspect-ratio 1})
(def ^:private avatar-container-common
{:width 68
:height 68
:justify-content :center
:align-items :center
:background-color colors/white})
(def avatar-container-circular
(assoc avatar-container-common :border-radius 33))
(def avatar-container-rounded
(assoc avatar-container-common :border-radius 16))
(def community-logo-image
{:width 64
:height 64
:border-radius 32})
+71 -7
View File
@@ -1,12 +1,76 @@
(ns quo2.components.share.qr-code.view
(:require [quo2.components.share.qr-code.style :as style]
(:require [quo2.components.avatars.account-avatar.view :as account-avatar]
[quo2.components.avatars.channel-avatar.view :as channel-avatar]
[quo2.components.avatars.user-avatar.view :as user-avatar]
[quo2.components.avatars.wallet-user-avatar :as wallet-avatar]
[quo2.components.share.qr-code.style :as style]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]))
(defn qr-code
[{:keys [source width height]}]
[rn/view
{:style style/container}
(defn- avatar-image
[{avatar-type :avatar
:as props}]
[rn/view {:style style/avatar-overlay}
[rn/view
{:style (if (= avatar-type :wallet-account)
style/avatar-container-rounded
style/avatar-container-circular)}
(case avatar-type
:profile
[user-avatar/user-avatar
(assoc props
:size :size-64
:status-indicator? false
:online? false
:ring? false)]
:wallet-account
[account-avatar/view (assoc props :size :size-64 :type :default)]
:community
[rn/image
{:style style/community-logo-image
:source (:picture props)}]
:channel
[channel-avatar/view (assoc props :locked? nil :size :size-64)]
:saved-address
[wallet-avatar/wallet-user-avatar (assoc props :size :size-64)]
nil)]])
(defn view
"Receives a URL to show a QR code with an avatar (optional) over it.
Parameters:
- qr-image-uri: A valid uri representing the QR code to display using `fast-image`
- size: if not provided, the QR code image will grow according to its parent
- avatar: Type of the avatar, defaults to `:none` and it can be:
`:profile`, `:wallet-account`, `:community`, `:channel` or `:saved-address`
Depending on the type selected, different properties are accepted:
`:profile`:
- profile-picture
- full-name
- customization-color
`:wallet-account`
- emoji
- customization-color
`:community`
- picture
`:channel`
- emoji
- customization-color
`:saved-address`
- f-name
- l-name
- customization-color"
[{:keys [avatar size qr-image-uri]
:or {avatar :none}
:as props}]
[rn/view {:style (style/container size)}
[fast-image/fast-image
{:source source
:style (style/image width height)}]])
{:style (style/qr-image size)
:source {:uri qr-image-uri}}]
(when-not (= avatar :none)
[avatar-image props])])
@@ -8,15 +8,11 @@
[react-native.core :as rn]))
(defn view
[{:keys [source link-title
url-on-press url-on-long-press qr-url share-on-press]}]
[{:keys [qr-image-uri link-title url-on-press url-on-long-press qr-url share-on-press]}]
[blur/ios-view
{:style style/qr-code-container
:blur-type :light}
[qr-code/qr-code
{:source source
:width "100%"
:height 311}]
[qr-code/view {:qr-image-uri qr-image-uri}]
[rn/view {:style style/profile-address-container}
[rn/view {:style style/profile-address-column}
[text/text
@@ -33,12 +33,15 @@
[theme blur?]
{:border-width 1
:border-radius 6
:margin-right 8
:margin-right 4
:padding-horizontal 2
:border-color (if-not blur?
(colors/theme-colors colors/neutral-20 colors/neutral-80 theme)
colors/white-opa-10)})
(def timestamp-container
{:margin-left 4})
(defn timestamp
[theme blur?]
{:color (if-not blur?
@@ -56,7 +56,7 @@
:size :label
:style (style/transaction-counter theme)}
(i18n/label :t/x-counter {:counter counter})]])
[rn/view
[rn/view {:style style/timestamp-container}
[text/text
{:weight :regular
:size :label
@@ -4,8 +4,8 @@
(def container-info
{:padding-horizontal 20
:padding-top 12
:flex-grow 1
:padding-bottom 32
:flex-grow 1
:max-height 98})
(def container-info-top
+7 -8
View File
@@ -99,12 +99,13 @@
quo2.components.profile.profile-card.view
quo2.components.profile.select-profile.view
quo2.components.profile.showcase-nav.view
quo2.components.reactions.reaction
quo2.components.record-audio.record-audio.view
quo2.components.record-audio.soundtrack.view
quo2.components.selectors.disclaimer.view
quo2.components.selectors.filter.view
quo2.components.selectors.reactions.view
quo2.components.selectors.reactions-selector.view
quo2.components.selectors.react.view
quo2.components.selectors.react-selector.view
quo2.components.selectors.selectors.view
quo2.components.settings.accounts.view
quo2.components.settings.data-item.view
@@ -307,10 +308,6 @@
(def select-profile quo2.components.profile.select-profile.view/view)
(def showcase-nav quo2.components.profile.showcase-nav.view/view)
;;;; Reactions
(def reaction quo2.components.reactions.reaction/reaction)
(def add-reaction quo2.components.reactions.reaction/add-reaction)
;;;; Record Audio
(def record-audio quo2.components.record-audio.record-audio.view/record-audio)
(def soundtrack quo2.components.record-audio.soundtrack.view/f-soundtrack)
@@ -319,7 +316,9 @@
(def author quo2.components.messages.author.view/author)
(def disclaimer quo2.components.selectors.disclaimer.view/view)
(def filter quo2.components.selectors.filter.view/view)
(def reactions quo2.components.selectors.reactions.view/view)
(def reactions-selector quo2.components.selectors.reactions-selector.view/view)
(def react quo2.components.selectors.react.view/view)
(def react-selector quo2.components.selectors.react-selector.view/view)
(def checkbox quo2.components.selectors.selectors.view/checkbox)
(def toggle quo2.components.selectors.selectors.view/toggle)
(def radio quo2.components.selectors.selectors.view/radio)
@@ -335,7 +334,7 @@
(def settings-item quo2.components.settings.settings-item.view/view)
;;;; Share
(def qr-code quo2.components.share.qr-code.view/qr-code)
(def qr-code quo2.components.share.qr-code.view/view)
(def share-qr-code quo2.components.share.share-qr-code.view/view)
;;;; SWITCHER
+1 -1
View File
@@ -59,7 +59,7 @@
[quo2.components.record-audio.soundtrack.component-spec]
[quo2.components.selectors.disclaimer.component-spec]
[quo2.components.selectors.filter.component-spec]
[quo2.components.selectors.reactions.component-spec]
[quo2.components.selectors.reactions-selector.component-spec]
[quo2.components.selectors.selectors.component-spec]
[quo2.components.settings.reorder-item.component-spec]
[quo2.components.settings.settings-item.component-spec]
+10 -44
View File
@@ -6,7 +6,6 @@
[quo2.foundations.colors :as quo2.colors]
[re-frame.core :as re-frame]
[status-im.utils.deprecated-types :as types]
[status-im.ui.components.emoji-thumbnail.styles :as emoji-thumbnail-styles]
[status-im.utils.universal-links.core :as universal-links]
[status-im.bottom-sheet.events :as bottom-sheet]
[status-im2.common.toasts.events :as toasts]
@@ -457,50 +456,17 @@
[{:keys [db]}]
{:db (assoc db :communities/create-channel {})})
(rf/defn invite-people-pressed
{:events [:communities/invite-people-pressed]}
[cofx id]
(rf/merge cofx
(reset-community-id-input id)
(bottom-sheet/hide-bottom-sheet-old)
(navigation/open-modal :invite-people-community {:invite? true})))
(re-frame/reg-event-fx :communities/invite-people-pressed
(fn [{:keys [db]} [id]]
{:db (assoc db :communities/community-id-input id)
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:open-modal :invite-people-community {:invite? true}]]]}))
(rf/defn share-community-pressed
{:events [:communities/share-community-pressed]}
[cofx id]
(rf/merge cofx
(reset-community-id-input id)
(bottom-sheet/hide-bottom-sheet-old)
(navigation/open-modal :invite-people-community {})))
(rf/defn create-channel-pressed
{:events [::create-channel-pressed]}
[{:keys [db] :as cofx} id]
(rf/merge cofx
(reset-community-id-input id)
(reset-channel-info)
(rf/dispatch [::create-channel-fields
(rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)])
(navigation/open-modal :create-community-channel {:community-id id})))
(rf/defn edit-channel-pressed
{:events [::edit-channel-pressed]}
[{:keys [db] :as cofx} community-id chat-name description color emoji chat-id category-id position]
(let [{:keys [color emoji]} (if (string/blank? emoji)
(rand-nth emoji-thumbnail-styles/emoji-picker-default-thumbnails)
{:color color :emoji emoji})]
(rf/merge cofx
{:db (assoc db
:communities/create-channel
{:name chat-name
:description description
:color color
:community-id community-id
:emoji emoji
:edit-channel-id chat-id
:category-id category-id
:position position})}
(navigation/open-modal :edit-community-channel nil))))
(re-frame/reg-event-fx :communities/share-community-pressed
(fn [{:keys [db]} [id]]
{:db (assoc db :communities/community-id-input id)
:fx [[:dispatch [:hide-bottom-sheet]]
[:dispatch [:open-modal :invite-people-community {}]]]}))
(rf/defn community-created
{:events [::community-created]}
+1 -1
View File
@@ -25,7 +25,7 @@
(defn- rpc->type
[{:keys [type name] :as chat}]
(case type
(condp = type
notification-types/reply
(assoc chat
:chat-name name
+4 -8
View File
@@ -54,7 +54,6 @@
status-im2.contexts.shell.jump-to.events
status-im2.contexts.onboarding.events
status-im.chat.models.gaps
[status-im2.navigation.events :as navigation]
[status-im2.common.theme.core :as theme]
[react-native.core :as rn]
[react-native.platform :as platform]
@@ -250,10 +249,7 @@
:on-success (fn [on-ramps]
(re-frame/dispatch [::crypto-loaded on-ramps]))}]})
(rf/defn open-buy-crypto-screen
{:events [:buy-crypto.ui/open-screen]}
[cofx]
(rf/merge
cofx
(navigation/open-modal :buy-crypto nil)
(wallet/keep-watching-history)))
(re-frame/reg-event-fx :buy-crypto.ui/open-screen
(fn []
{:fx [[:dispatch [:wallet/keep-watching]]
[:dispatch [:open-modal :buy-crypto nil]]]}))
+9 -6
View File
@@ -41,13 +41,16 @@
(defn contact-two-names-by-identity
[contact profile contact-identity]
(let [me? (= (:public-key profile) contact-identity)]
(let [{:keys [public-key preferred-name display-name]} profile
{:keys [primary-name secondary-name]} contact
me? (= public-key contact-identity)]
(if me?
[(or (:preferred-name profile)
(:display-name profile)
(:primary-name contact)
(gfycat/generate-gfy contact-identity))]
[(:primary-name contact) (:secondary-name contact)])))
[(cond
(not (string/blank? preferred-name)) preferred-name
(not (string/blank? display-name)) display-name
(not (string/blank? primary-name)) primary-name
:else (gfycat/generate-gfy contact-identity))]
[primary-name secondary-name])))
(defn displayed-photo
[{:keys [images]}]
+4 -13
View File
@@ -1,13 +1,10 @@
(ns status-im.stickers.core
(:require [clojure.string :as string]
[re-frame.core :as re-frame]
[status-im2.constants :as constants]
[status-im2.config :as config]
[utils.re-frame :as rf]
(:require [re-frame.core :as re-frame]
[status-im.utils.utils :as utils]
[status-im2.navigation.events :as navigation]
[status-im.wallet.utils :as wallet.utils]
[status-im2.constants :as constants]
[utils.ethereum.chain :as chain]
[status-im.wallet.utils :as wallet.utils]))
[utils.re-frame :as rf]))
(re-frame/reg-fx
:stickers/set-pending-timeout-fx
@@ -115,12 +112,6 @@
[{:keys [db]} packs]
{:db (assoc db :stickers/recent-stickers packs)})
(rf/defn open-sticker-pack
{:events [:stickers/open-sticker-pack]}
[{{:networks/keys [current-network]} :db :as cofx} id]
(when (and id (or config/stickers-test-enabled? (string/starts-with? current-network "mainnet")))
(navigation/open-modal cofx :stickers-pack {:id id})))
(rf/defn select-pack
{:events [:stickers/select-pack]}
[{:keys [db]} id]
+2
View File
@@ -5,6 +5,7 @@
[shadow.test :as st]
[shadow.test.env :as env]
[utils.re-frame :as rf]
[status-im2.setup.interceptors :as interceptors]
status-im2.setup.i18n-resources))
(defonce repl? (atom false))
@@ -113,6 +114,7 @@
(defn ^:export main
[& args]
(reset-test-data!)
(interceptors/register-global-interceptors)
(rf/set-mergeable-keys #{:filters/load-filters
:pairing/set-installation-metadata
:dispatch-n
+2 -1
View File
@@ -23,6 +23,7 @@
[status-im.wallet.core :as wallet]))
(rf/defn process-next
{:events [:transport/process-next]}
[cofx ^js response-js sync-handler]
(if sync-handler
(sync-handler cofx response-js true)
@@ -60,7 +61,7 @@
(js-delete response-js "chats")
(rf/merge cofx
(process-next response-js sync-handler)
(chat.events/ensure-chats (map data-store.chats/<-rpc (types/js->clj chats)))))
#(chat.events/ensure-chats % [(map data-store.chats/<-rpc (types/js->clj chats))])))
(seq messages)
(models.message/receive-many cofx response-js)
@@ -199,10 +199,11 @@
(defn contact-request-status-label
[state]
[rn/view {:style (style/contact-request-status-label state)}
(case state
(condp = state
constants/contact-request-message-state-pending [contact-request-status-pending]
constants/contact-request-message-state-accepted [contact-request-status-accepted]
constants/contact-request-message-state-declined [contact-request-status-declined])])
constants/contact-request-message-state-declined [contact-request-status-declined]
nil)])
;;;; SYSTEM
@@ -389,10 +389,11 @@
:background-color (when (= :retry state)
colors/blue-light)
:border-width 1
:border-color (case state
:border-color (condp = state
constants/contact-request-message-state-accepted colors/green-transparent-10
constants/contact-request-message-state-declined colors/red-light
constants/contact-request-message-state-pending colors/gray-lighter)
constants/contact-request-message-state-pending colors/gray-lighter
nil)
:padding-vertical 10
:padding-horizontal 16})
@@ -19,7 +19,7 @@
[status-im.utils.gfycat.core :as gfy]
[status-im.utils.universal-links.utils :as universal-links]
[status-im.utils.utils :as utils]
[status-im2.common.qr-code-viewer.view :as qr-code-viewer]
[status-im2.common.qr-codes.view :as qr-codes]
[status-im2.config :as config]
[utils.i18n :as i18n])
(:require-macros [status-im.utils.views :as views]))
@@ -32,7 +32,9 @@
[react/view {:on-layout #(reset! width (-> ^js % .-nativeEvent .-layout .-width))}
[react/view {:style {:padding-top 16 :padding-horizontal 16}}
(when @width
[qr-code-viewer/qr-code-view (- @width 32) address])
[qr-codes/qr-code
{:url address
:size (- @width 32)}])
(when ens-name
[react/view
[copyable-text/copyable-text-view
@@ -4,10 +4,10 @@
[reagent.core :as reagent]
[utils.ethereum.eip.eip55 :as eip55]
[status-im.ethereum.eip681 :as eip681]
[utils.i18n :as i18n]
[status-im.ui.components.copyable-text :as copyable-text]
[status-im2.common.qr-code-viewer.view :as qr-code-viewer]
[status-im.ui.components.react :as react])
[status-im.ui.components.react :as react]
[status-im2.common.qr-codes.view :as qr-codes]
[utils.i18n :as i18n])
(:require-macros [status-im.utils.views :as views]))
(views/defview share-address
@@ -18,9 +18,9 @@
[react/view {:on-layout #(reset! width (-> ^js % .-nativeEvent .-layout .-width))}
[react/view {:style {:padding-top 16 :padding-horizontal 16}}
(when @width
[qr-code-viewer/qr-code-view
(- @width 32)
(eip681/generate-uri address {:chain-id chain-id})])
[qr-codes/qr-code
{:url (eip681/generate-uri address {:chain-id chain-id})
:size (- @width 32)}])
[copyable-text/copyable-text-view
{:label :t/ethereum-address
:container-style {:margin-top 12 :margin-bottom 4}
@@ -34,7 +34,7 @@
:pending (transaction-icon :main-icons/arrow-right
colors/black-transparent
colors/gray)
(throw (str "Unknown transaction type: " k))))
(throw (js/Error. (str "Unknown transaction type: " k)))))
(defn render-transaction
[{:keys [label contact address contact-accessibility-label
+9 -14
View File
@@ -197,12 +197,10 @@
:on-success #(re-frame/dispatch [::collectible-assets-fetch-success address
collectible-slug %])}]}))
(rf/defn show-nft-details
{:events [::show-nft-details]}
[cofx asset]
(rf/merge cofx
{:db (assoc (:db cofx) :wallet/selected-collectible asset)}
(navigation/open-modal :nft-details {})))
(re-frame/reg-event-fx ::show-nft-details
(fn [{:keys [db]} [asset]]
{:db (assoc db :wallet/selected-collectible asset)
:fx [[:dispatch [:open-modal :nft-details {}]]]}))
(defn rpc->token
[tokens]
@@ -610,14 +608,11 @@
{:db (assoc-in db [:wallet/prepare-transaction field] value)}
(bottom-sheet/hide-bottom-sheet-old)))
(rf/defn navigate-to-recipient-code
{:events [:wallet.send/navigate-to-recipient-code]}
[{:keys [db] :as cofx}]
(rf/merge cofx
{:db (-> db
(assoc :wallet/recipient {}))}
(bottom-sheet/hide-bottom-sheet-old)
(navigation/open-modal :recipient nil)))
(re-frame/reg-event-fx :wallet.send/navigate-to-recipient-code
(fn [{:keys [db]}]
{:db (-> db (assoc :wallet/recipient {}))
:fx [[:dispatch [:bottom-sheet/hide-old]]
[:dispatch [:open-modal :recipient nil]]]}))
(rf/defn show-delete-account-confirmation
{:events [:wallet.settings/show-delete-account-confirmation]}
+12 -12
View File
@@ -6,18 +6,18 @@
(deftest test-too-precise-amount?
(testing "try both decimal and scientific or hex format"
(is (= false (#'status-im.wallet.db/too-precise-amount? "100" 2)))
(is (= false (#'status-im.wallet.db/too-precise-amount? "100" 0)))
(is (= true (#'status-im.wallet.db/too-precise-amount? "100.1" 0)))
(is (= false (#'status-im.wallet.db/too-precise-amount? "100.23" 2)))
(is (= true (#'status-im.wallet.db/too-precise-amount? "100.233" 2)))
(is (= true (#'status-im.wallet.db/too-precise-amount? "100.0000000000000000001" 18)))
(is (= false (#'status-im.wallet.db/too-precise-amount? "100.000000000000000001" 18)))
(is (= false (#'status-im.wallet.db/too-precise-amount? "1e-18" 18)))
(is (= true (#'status-im.wallet.db/too-precise-amount? "1e-19" 18)))
(is (= true (#'status-im.wallet.db/too-precise-amount? "0xa.a" 2))) ;; 0xa.a is 10.625
(is (= false (#'status-im.wallet.db/too-precise-amount? "0xa.a" 3)))
(is (= false (#'status-im.wallet.db/too-precise-amount? "1000" 3)))))
(is (false? (#'status-im.wallet.db/too-precise-amount? "100" 2)))
(is (false? (#'status-im.wallet.db/too-precise-amount? "100" 0)))
(is (true? (#'status-im.wallet.db/too-precise-amount? "100.1" 0)))
(is (false? (#'status-im.wallet.db/too-precise-amount? "100.23" 2)))
(is (true? (#'status-im.wallet.db/too-precise-amount? "100.233" 2)))
(is (true? (#'status-im.wallet.db/too-precise-amount? "100.0000000000000000001" 18)))
(is (false? (#'status-im.wallet.db/too-precise-amount? "100.000000000000000001" 18)))
(is (false? (#'status-im.wallet.db/too-precise-amount? "1e-18" 18)))
(is (true? (#'status-im.wallet.db/too-precise-amount? "1e-19" 18)))
(is (true? (#'status-im.wallet.db/too-precise-amount? "0xa.a" 2))) ;; 0xa.a is 10.625
(is (false? (#'status-im.wallet.db/too-precise-amount? "0xa.a" 3)))
(is (false? (#'status-im.wallet.db/too-precise-amount? "1000" 3)))))
(defn- equal-results?
[a b]
+7 -7
View File
@@ -1,14 +1,15 @@
(ns status-im.wallet.swap.core
(:require [re-frame.db :as re-frame.db]
[re-frame.core :as re-frame]
[utils.re-frame :as rf]
[status-im2.navigation.events :as navigation]))
(rf/defn open-asset-selector-modal
"source? true signinfies we are selecting the source asset. false implies selection of sink asset"
{:events [::open-asset-selector-modal]}
[{:keys [db]} source?]
(rf/merge {:db (assoc db :wallet/modal-selecting-source-token? source?)}
(navigation/open-modal :swap-asset-selector {})))
;; "source? true" means we are selecting the source asset. false implies
;; selection of sink asset."
(re-frame/reg-event-fx ::open-asset-selector-modal
(fn [{:keys [db]} [source?]]
{:db (assoc db :wallet/modal-selecting-source-token? source?)
:fx [[:dispatch [:open-modal :swap-asset-selector {}]]]}))
(rf/defn set-from-token
{:events [::set-from-token]}
@@ -48,4 +49,3 @@
:wallet/all-tokens
vals
(map #(str (:name %) "-" (:symbol %)))))
@@ -1,15 +0,0 @@
(ns status-im2.common.qr-code-viewer.style
(:require [quo2.foundations.colors :as colors]))
(def qr-code-padding 16)
(defn qr-code-container
[width]
{:align-self :center
:width width
:height width
:padding-horizontal 16
:background-color colors/white
:align-items :center
:justify-content :center
:border-radius 8})
@@ -1,31 +0,0 @@
(ns status-im2.common.qr-code-viewer.view
(:require ["qrcode" :as qr-code-js]
[cljs-bean.core :as bean]
[reagent.core :as reagent]
[status-im2.common.qr-code-viewer.style :as style]
[react-native.core :as rn]
[react-native.svg :as svg]))
(defn qr-code
[{:keys [size value]}]
(let [uri (reagent/atom nil)]
(.toString
qr-code-js
value
(bean/->js {:margin 0 :width size})
#(reset! uri %2))
(fn []
(when @uri
[svg/svgxml {:xml @uri :width size :height size}]))))
(defn qr-code-view
"Qr Code view including the frame.
Note: `size` includes frame with `style/qr-code-padding.`"
[size value]
(when (and size value)
[rn/view
{:style (style/qr-code-container size)
:accessibility-label :qr-code-image}
[qr-code
{:value value
:size (- size (* style/qr-code-padding 2))}]]))
+37
View File
@@ -0,0 +1,37 @@
(ns status-im2.common.qr-codes.view
(:require [quo2.core :as quo]
[utils.image-server :as image-server]
[utils.re-frame :as rf]))
(defn qr-code
"Receives a URL to show a QR code with an avatar (optional) over it.
Parameters:
- url: String to transform to QR
- size: if not provided, the QR code image will grow according to its parent
- avatar: Type of the avatar, defaults to `:none` and it can be:
`:profile`, `:wallet-account`, `:community`, `:channel` or `:saved-address`
Depending on the type selected, different properties are accepted:
`:profile`:
- profile-picture
- full-name
- customization-color
`:wallet-account`
- emoji
- customization-color
`:community`
- picture
`:channel`
- emoji
- customization-color
`:saved-address`
- f-name
- l-name
- customization-color"
[{:keys [url size] :as props}]
(let [qr-media-server-uri (image-server/get-qr-image-uri-for-any-url
{:url url
:port (rf/sub [:mediaserver/port])
:qr-size (or 400 (int size))
:error-level :highest})]
[quo/qr-code (assoc props :qr-image-uri qr-media-server-uri)]))
+9 -2
View File
@@ -81,10 +81,16 @@
:dark (js/require "../resources/images/ui2/no-notifications-dark.png")}})
(def mock-images
{:diamond (js/require "../resources/images/mock2/diamond.png")
:bored-ape (js/require "../resources/images/mock2/bored-ape.png")
{:bored-ape (js/require "../resources/images/mock2/bored-ape.png")
:coinbase (js/require "../resources/images/mock2/coinbase.png")
:collectible (js/require "../resources/images/mock2/collectible.png")
:collectible-monkey (js/require "../resources/images/mock2/collectible-monkey.png")
:collectible1 (js/require "../resources/images/mock2/collectible1.png")
:collectible2 (js/require "../resources/images/mock2/collectible2.png")
:collectible3 (js/require "../resources/images/mock2/collectible3.png")
:collectible4 (js/require "../resources/images/mock2/collectible4.png")
:collectible5 (js/require "../resources/images/mock2/collectible5.png")
:collectible6 (js/require "../resources/images/mock2/collectible6.png")
:contact (js/require "../resources/images/mock2/contact.png")
:community-banner (js/require "../resources/images/mock2/community-banner.png")
:community-logo (js/require "../resources/images/mock2/community-logo.png")
@@ -92,6 +98,7 @@
:dark-blur-bg (js/require "../resources/images/mock2/dark_blur_bg.png")
:dark-blur-background (js/require "../resources/images/mock2/dark-blur-background.png")
:decentraland (js/require "../resources/images/mock2/decentraland.png")
:diamond (js/require "../resources/images/mock2/diamond.png")
:gif (js/require "../resources/images/mock2/gif.png")
:monkey (js/require "../resources/images/mock2/monkey.png")
:light-blur-background (js/require "../resources/images/mock2/light-blur-background.png")
+19 -21
View File
@@ -23,8 +23,8 @@
(if collapsed? 50 170))
(defn f-scroll-page-header
[scroll-height height name page-nav-right-side logo sticky-header top-nav title-colum navigate-back?
collapsed? overlay-shown?]
[{:keys [scroll-height height page-nav-right-section-buttons sticky-header
top-nav title-colum navigate-back? collapsed? page-nav-props overlay-shown?]}]
(let [input-range [0 10]
output-range [-208 -45]
y (reanimated/use-shared-value scroll-height)
@@ -54,17 +54,6 @@
:top 0
:left 0
:right 0}}
(when logo
[reanimated/view
{:style (style/sticky-header-title opacity-animation)}
[rn/image
{:source logo
:style style/sticky-header-image}]
[quo/text
{:size :paragraph-1
:weight :semi-bold
:style {:line-height 21}}
name]])
(if top-nav
[rn/view {:style {:margin-top 0}}
top-nav]
@@ -74,10 +63,12 @@
:background (if (= 1 (reanimated/get-shared-value opacity-animation))
:blur
:photo)
:right-side page-nav-right-side
:right-side page-nav-right-section-buttons
:center-opacity (reanimated/get-shared-value opacity-animation)
:overlay-shown? overlay-shown?}
navigate-back? (assoc :icon-name :i/close
:on-press #(rf/dispatch [:navigate-back])))])
:on-press #(rf/dispatch [:navigate-back]))
page-nav-props (merge page-nav-props))])
(when title-colum
title-colum)
sticky-header]]))
@@ -105,14 +96,21 @@
(defn scroll-page
[_ _ _]
(let [scroll-height (reagent/atom negative-scroll-position-0)]
(fn [{:keys [name theme cover-image logo page-nav-right-section-buttons on-scroll
collapsed?
height top-nav title-colum background-color navigate-back? overlay-shown?]}
sticky-header
(fn [{:keys [theme cover-image logo on-scroll
collapsed? height top-nav title-colum background-color navigate-back? page-nav-props
overlay-shown? sticky-header]}
children]
[:<>
[:f> f-scroll-page-header @scroll-height height name page-nav-right-section-buttons
logo sticky-header top-nav title-colum navigate-back? collapsed? overlay-shown?]
[:f> f-scroll-page-header
{:scroll-height @scroll-height
:height height
:sticky-header sticky-header
:top-nav top-nav
:title-colum title-colum
:navigate-back? navigate-back?
:collapsed? collapsed?
:page-nav-props page-nav-props
:overlay-shown? overlay-shown?}]
[rn/scroll-view
{:content-container-style {:flex-grow 1}
:content-inset-adjustment-behavior :never
+6 -4
View File
@@ -157,12 +157,14 @@
(def delete-message-for-me-undo-time-limit-ms 4000)
(def waku-nodes-config
{:status.prod ["enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@prod.nodes.status.im"]
:status.test ["enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@test.nodes.status.im"]
{:status.prod
["enrtree://AL65EKLJAUXKKPG43HVTML5EFFWEZ7L4LOKTLZCLJASG4DSESQZEC@prod.status.nodes.status.im"]
:status.test
["enrtree://AIO6LUM3IVWCU2KCPBBI6FEH2W42IGK3ASCZHZGG5TIXUR56OGQUO@test.status.nodes.status.im"]
:wakuv2.prod
["enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@prod.waku.nodes.status.im"]
["enrtree://ANEDLO25QVUGJOUTQFRYKWX6P4Z4GKVESBMHML7DZ6YK4LGS5FC5O@prod.wakuv2.nodes.status.im"]
:wakuv2.test
["enrtree://AOGECG2SPND25EEFMAJ5WF3KSGJNSGV356DSTL2YVLLZWIV6SAYBM@test.waku.nodes.status.im"]})
["enrtree://AO47IDOLBKH72HIZZOXQP6NMRESAN7CHYWIBNXDXWRJRZWLODKII6@test.wakuv2.nodes.status.im"]})
(def default-kdf-iterations 3200)
+5 -6
View File
@@ -23,7 +23,6 @@
(def ^:const content-type-link 101)
(def ^:const content-type-album 102)
(def ^:const contact-request-state-none 0)
(def ^:const contact-request-state-mutual 1)
(def ^:const contact-request-state-sent 2)
@@ -236,17 +235,17 @@
{2 {0 image-size
1 image-size}
3 {0 [(* image-size 2) (* image-size 1.25)]
1 [(- image-size 0.5) (- (* image-size 0.75) 1)]
2 [(- image-size 0.5) (- (* image-size 0.75) 1)]}
1 [(- image-size 0.5) (dec (* image-size 0.75))]
2 [(- image-size 0.5) (dec (* image-size 0.75))]}
4 {0 image-size
1 image-size
2 image-size
3 image-size}
5 {0 image-size
1 image-size
2 (- (* image-size 0.67) 1)
3 (- (* image-size 0.67) 1)
4 (- (* image-size 0.67) 1)}
2 (dec (* image-size 0.67))
3 (dec (* image-size 0.67))
4 (dec (* image-size 0.67))}
:default {0 image-size
1 image-size
2 (- (* image-size 0.5) 0.5)
@@ -15,10 +15,11 @@
(defn focus
[{:keys [input-ref] :as props}
{:keys [text-value focused? lock-selection? saved-cursor-position gradient-z-index]}
{:keys [height saved-height last-height opacity background-y gradient-opacity container-opacity]
{:keys [text-value focused? lock-selection? saved-cursor-position]}
{:keys [height saved-height last-height opacity background-y container-opacity]
:as animations}
{:keys [max-height] :as dimensions}]
{:keys [max-height] :as dimensions}
show-floating-scroll-down-button?]
(reset! focused? true)
(rf/dispatch [:chat.ui/set-input-focused true])
(reanimated/animate height (reanimated/get-shared-value last-height))
@@ -27,14 +28,12 @@
(when (> (reanimated/get-shared-value last-height) (* constants/background-threshold max-height))
(reanimated/animate opacity 1)
(reanimated/set-shared-value background-y 0))
(when (= @gradient-z-index -1)
(reanimated/animate gradient-opacity 1)
(reset! gradient-z-index 1))
(js/setTimeout #(reset! lock-selection? false) 300)
(when (and (not-empty @text-value) @input-ref)
(.setNativeProps ^js @input-ref
(clj->js {:selection {:start @saved-cursor-position :end @saved-cursor-position}})))
(kb/handle-refocus-emoji-kb-ios props animations dimensions))
(kb/handle-refocus-emoji-kb-ios props animations dimensions)
(reset! show-floating-scroll-down-button? false))
(defn blur
[{:keys [text-value focused? lock-selection? cursor-position saved-cursor-position gradient-z-index
@@ -122,7 +122,7 @@
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:margin-top 2}}
(str " "
(case (or content-type contentType)
(condp = (or content-type contentType)
constant/content-type-image (if (pos? album-images-count)
(i18n/label :t/album-images-count
{:album-images-count album-images-count})
@@ -47,7 +47,8 @@
:label (i18n/label :t/jump-to)
:style {:align-self :center}}}
{}]]
(when @show-floating-scroll-down-button?
(when (and (not @focused?)
@show-floating-scroll-down-button?)
[quo/floating-shell-button
{:scroll-to-bottom {:on-press scroll-to-bottom-fn}}
style/scroll-to-bottom-button])]))
+28 -27
View File
@@ -105,34 +105,35 @@
:menu-items @(:menu-items state)
:style (style/input-view state)}
[rn/text-input
{:ref #(reset! (:input-ref props) %)
:default-value @(:text-value state)
:on-focus #(handler/focus props state animations dimensions)
:on-blur #(handler/blur state animations dimensions subscriptions)
:on-content-size-change #(handler/content-size-change %
state
animations
subscriptions
dimensions
(or keyboard-shown
(:edit subscriptions)))
:on-scroll #(handler/scroll % props state animations dimensions)
:on-change-text #(handler/change-text % props state)
:on-selection-change #(handler/selection-change % props state)
:on-selection #(selection/on-selection % props state)
:keyboard-appearance (quo.theme/theme-value :light :dark)
:max-height max-height
{:ref #(reset! (:input-ref props) %)
:default-value @(:text-value state)
:on-focus
#(handler/focus props state animations dimensions show-floating-scroll-down-button?)
:on-blur #(handler/blur state animations dimensions subscriptions)
:on-content-size-change #(handler/content-size-change %
state
animations
subscriptions
dimensions
(or keyboard-shown
(:edit subscriptions)))
:on-scroll #(handler/scroll % props state animations dimensions)
:on-change-text #(handler/change-text % props state)
:on-selection-change #(handler/selection-change % props state)
:on-selection #(selection/on-selection % props state)
:keyboard-appearance (quo.theme/theme-value :light :dark)
:max-height max-height
:max-font-size-multiplier 1
:multiline true
:placeholder (i18n/label :t/type-something)
:placeholder-text-color (colors/theme-colors colors/neutral-30 colors/neutral-50)
:style (style/input-text props
state
subscriptions
{:max-height max-height
:theme theme})
:max-length constants/max-text-size
:accessibility-label :chat-message-input}]]
:multiline true
:placeholder (i18n/label :t/type-something)
:placeholder-text-color (colors/theme-colors colors/neutral-30 colors/neutral-50)
:style (style/input-text props
state
subscriptions
{:max-height max-height
:theme theme})
:max-length constants/max-text-size
:accessibility-label :chat-message-input}]]
(when chat-screen-loaded?
[:<>
[gradients/view props state animations show-bottom-gradient?]
+15 -15
View File
@@ -80,15 +80,15 @@
(:invitation-admin chat)))))
(rf/defn leave-removed-chat
{:events [:chat/leave-removed-chat]}
[{{:keys [view-id current-chat-id chats]} :db
:as cofx}]
(when (and (= view-id :chat)
(not (contains? chats current-chat-id)))
(navigation/navigate-back cofx)))
(rf/defn ensure-chats
"Add chats to db and update"
[{:keys [db] :as cofx} chats]
(defn ensure-chats
[{:keys [db] :as cofx} [chats]]
(let [{:keys [all-chats chats-home-list removed-chats]}
(reduce
(fn [acc {:keys [chat-id profile-public-key timeline? community-id active muted] :as chat}]
@@ -103,18 +103,18 @@
:chats-home-list #{}
:removed-chats #{}}
(map (map-chats cofx) chats))]
(rf/merge
cofx
(merge {:db (-> db
(update :chats merge all-chats)
(update :chats-home-list set/union chats-home-list)
(update :chats #(apply dissoc % removed-chats))
(update :chats-home-list set/difference removed-chats))}
(when (not-empty removed-chats)
{:clear-message-notifications
[removed-chats
(get-in db [:profile/profile :remote-push-notifications-enabled?])]}))
leave-removed-chat)))
{:db (-> db
(update :chats merge all-chats)
(update :chats-home-list set/union chats-home-list)
(update :chats #(apply dissoc % removed-chats))
(update :chats-home-list set/difference removed-chats))
:fx [(when (not-empty removed-chats)
[:clear-message-notifications
[removed-chats
(get-in db [:profile/profile :remote-push-notifications-enabled?])]])
[:dispatch [:chat/leave-removed-chat]]]}))
(re-frame/reg-event-fx :chat/ensure-chats ensure-chats)
(rf/defn clear-history
"Clears history of the particular chat"
@@ -60,8 +60,8 @@
:other-person
:dont-show))
preview-text
(case content-type
constants/content-type-text
(cond
(= content-type constants/content-type-text)
(if reply?
(case author
:you (str (i18n/label :t/you-replied) ": " content-text)
@@ -74,33 +74,34 @@
:dont-show content-text
content-text))
constants/content-type-emoji
(= content-type constants/content-type-emoji)
(case author
:you (str (i18n/label :t/You) ": " content-text)
:other-person (str primary-name ": " content-text)
:dont-show content-text
content-text)
constants/content-type-system-text
(= content-type constants/content-type-system-text)
(case author
:you (i18n/label :t/you-pinned-a-message)
:other-person (i18n/label :t/user-pinned-a-message {:user primary-name})
:dont-show (i18n/label :t/Pinned-a-message)
(i18n/label :t/Pinned-a-message))
(constants/content-type-contact-request
constants/content-type-system-message-mutual-event-removed
constants/content-type-system-message-mutual-event-accepted)
(#{constants/content-type-contact-request
constants/content-type-system-message-mutual-event-removed
constants/content-type-system-message-mutual-event-accepted}
content-type)
(i18n/label :t/contact-request)
constants/content-type-sticker
(= content-type constants/content-type-sticker)
(case author
:you (i18n/label :t/you-sent-a-sticker)
:other-person (i18n/label :t/user-sent-a-sticker {:user primary-name})
:dont-show (i18n/label :t/sent-a-sticker)
(i18n/label :t/sent-a-sticker))
constants/content-type-image
(= content-type constants/content-type-image)
(let [sent-photos (if album-images-count
(case author
:you (i18n/label :t/you-sent-n-photos
@@ -119,31 +120,31 @@
(str sent-photos ": " content-text)
sent-photos))
constants/content-type-audio
(= content-type constants/content-type-audio)
(case author
:you (i18n/label :t/you-sent-audio-message)
:other-person (i18n/label :t/user-sent-audio-message {:user primary-name})
:dont-show (i18n/label :t/sent-audio-message)
(i18n/label :t/sent-audio-message))
constants/content-type-gif
(= content-type constants/content-type-gif)
(case author
:you (i18n/label :t/you-sent-a-gif)
:other-person (i18n/label :t/user-sent-audio-message {:user primary-name})
:dont-show (i18n/label :t/sent-a-gif)
(i18n/label :t/sent-a-gif))
constants/content-type-community
(= content-type constants/content-type-community)
(case author
:you (i18n/label :t/you-shared-a-community)
:other-person (i18n/label :t/user-shared-a-community {:user primary-name})
:dont-show (i18n/label :t/shared-a-community)
(i18n/label :t/shared-a-community))
:else
"")]
(subs preview-text 0 (min (count preview-text) max-subheader-length))))
(defn last-message-preview
"Render the preview of a last message to a maximum of max-subheader-length characters"
[group-chat {:keys [deleted? outgoing from deleted-for-me?] :as message}]
@@ -110,9 +110,8 @@
[index {:keys [opacity-value border-value full-screen-scale transparent? props]} portrait?]
(let [{:keys [small-list-ref timers text-sheet-lock?]} props
opacity (reanimated/get-shared-value opacity-value)
scale-value (+ 1
(/ constants/margin
(:width (rn/get-window))))]
scale-value (inc (/ constants/margin
(:width (rn/get-window))))]
(if (= opacity 1)
(do
(js/clearTimeout (:show-0 @timers))
@@ -4,16 +4,17 @@
[utils.re-frame :as rf]))
(defn avatar
[public-key size]
[{:keys [public-key size hide-ring?]}]
(let [display-name (first (rf/sub [:contacts/contact-two-names-by-identity public-key]))
photo-path (rf/sub [:chats/photo-path public-key])
online? (rf/sub [:visibility-status-updates/online? public-key])]
[rn/view {:style {:padding-top 2}}
[rn/view {:style {:padding-top 4}}
[rn/touchable-opacity
{:active-opacity 1
:on-press #(rf/dispatch [:chat.ui/show-profile public-key])}
[quo/user-avatar
{:full-name display-name
:ring? (not hide-ring?)
:profile-picture photo-path
:online? online?
:size size}]]]))
@@ -26,7 +26,8 @@
:landscape
:portrait)
images-count (count (:album message))
;; album images are always square, except when we have 3 images, then they must be rectangular
;; album images are always square, except when we have 3 images, then they must be
;; rectangular
;; (portrait or landscape)
portrait? (and (= images-count rectangular-style-count) (= album-style :portrait))]
(if (and albumize? (> images-count 1))
@@ -61,7 +62,7 @@
(< index constants/max-album-photos))
:shared-element)}]
(when (and (> images-count constants/max-album-photos)
(= index (- constants/max-album-photos 1)))
(= index (dec constants/max-album-photos)))
[rn/view
{:style style/overlay}
[quo/text
@@ -1,13 +1,12 @@
(ns status-im2.contexts.chat.messages.content.reactions.view
(:require [status-im2.constants :as constants]
[quo2.core :as quo]
[react-native.core :as rn]
(:require [quo2.core :as quo]
[status-im2.constants :as constants]
[utils.re-frame :as rf]
[status-im2.contexts.chat.messages.drawers.view :as drawers]
[quo2.theme :as quo.theme]))
(defn- on-press
[own message-id emoji-id emoji-reaction-id]
[{:keys [own message-id emoji-id emoji-reaction-id]}]
(if own
(rf/dispatch [:models.reactions/send-emoji-reaction-retraction
{:message-id message-id
@@ -18,7 +17,7 @@
:emoji-id emoji-id}])))
(defn- on-long-press
[{:keys [message-id emoji-id user-message-content reactions theme]}]
[{:keys [message-id emoji-id user-message-content reactions-order theme]}]
(rf/dispatch
[:chat.ui/emoji-reactions-by-message-id
{:message-id message-id
@@ -33,48 +32,47 @@
[:chat/clear-emoji-reaction-author-details]))
:content (fn []
[drawers/reaction-authors
{:reactions-order reactions
{:reactions-order reactions-order
:theme theme}])
:selected-item (fn []
user-message-content)
:padding-bottom-override 0}]))}]))
(defn- on-press-add
[{:keys [chat-id message-id user-message-content]}]
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch
[:show-bottom-sheet
{:content (fn [] [drawers/reactions
{:chat-id chat-id
:message-id message-id}])
:selected-item (fn []
user-message-content)}]))
(defn- add-emoji-key
[reaction]
(assoc reaction
:emoji
(get constants/reactions (:emoji-id reaction))))
(defn- view-internal
[{:keys [message-id chat-id theme]} user-message-content]
[{:keys [message-id chat-id pinned-by theme]} user-message-content]
(let [reactions (rf/sub [:chats/message-reactions message-id chat-id])]
[:<>
(when (seq reactions)
[rn/scroll-view
{:shows-horizontal-scroll-indicator false
:horizontal true
:style {:margin-left 44
:margin-top 8
:flex-direction :row}}
(for [{:keys [own emoji-id quantity emoji-reaction-id]
:as emoji-reaction} reactions]
^{:key emoji-reaction}
[rn/view {:style {:margin-right 6}}
[quo/reaction
{:emoji (get constants/reactions emoji-id)
:neutral? own
:clicks quantity
:on-press #(on-press own message-id emoji-id emoji-reaction-id)
:on-long-press #(on-long-press
{:message-id message-id
:emoji-id emoji-id
:user-message-content user-message-content
:reactions (map :emoji-id reactions)
:theme theme})
:accessibility-label (str "emoji-reaction-" emoji-id)}]])
[quo/add-reaction
{:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch
[:show-bottom-sheet
{:content (fn [] [drawers/reactions
{:chat-id chat-id
:message-id message-id}])
:selected-item (fn []
user-message-content)}]))}]])]))
[quo/react
{:container-style {:margin-left 44
:margin-top 8}
:reactions (map add-emoji-key reactions)
:add-reaction? true
:use-case (when pinned-by :pinned)
:on-press #(on-press (assoc % :message-id message-id))
:on-long-press #(on-long-press (assoc %
:message-id message-id
:theme theme
:reactions-order (map :emoji-id reactions)))
:on-press-add #(on-press-add {:chat-id chat-id
:message-id message-id
:user-message-content user-message-content})}])]))
(def message-reactions-row (quo.theme/with-theme view-internal))
@@ -104,9 +104,9 @@
(fn [acc e]
(render-inline acc e chat-id style-override mention-first))
[quo/text
{:size :paragraph-1
:style {:margin-bottom (if mention-first (if platform/ios? 4 0) 2)
:margin-top (if mention-first (if platform/ios? -4 0) 2)
{:style {:size :paragraph-1
:margin-bottom (if mention-first (if platform/ios? 4 0) 2)
:margin-top (if mention-first (if platform/ios? -4 0) -1)
:color (when (seq style-override) colors/white)}}]
children)])
@@ -31,15 +31,19 @@
(def delivery-state-showing-time-ms 3000)
(defn avatar-container
[{:keys [content last-in-group? pinned-by quoted-message from]} show-reactions? show-user-info?]
[{:keys [content last-in-group? pinned-by quoted-message from]} show-reactions?
in-reaction-and-action-menu? in-pinned-view?]
(if (or (and (seq (:response-to content))
quoted-message)
last-in-group?
pinned-by
(not show-reactions?)
show-user-info?)
[avatar/avatar from :small]
[rn/view {:padding-top 2 :width 32}]))
in-reaction-and-action-menu?)
[avatar/avatar
{:public-key from
:size :small
:hide-ring? (or in-pinned-view? in-reaction-and-action-menu?)}]
[rn/view {:padding-top 4 :width 32}]))
(defn author
[{:keys [response-to
@@ -50,12 +54,12 @@
from
timestamp]}
show-reactions?
show-user-info?]
in-reaction-and-action-menu?]
(when (or (and (seq response-to) quoted-message)
last-in-group?
pinned-by
(not show-reactions?)
show-user-info?)
in-reaction-and-action-menu?)
(let [[primary-name secondary-name] (rf/sub [:contacts/contact-two-names-by-identity from])
{:keys [ens-verified added?]} (rf/sub [:contacts/contact-by-address from])]
[quo/author
@@ -111,7 +115,8 @@
(defn- user-message-content-internal
[]
(let [show-delivery-state? (reagent/atom false)]
(fn [{:keys [message-data context keyboard-shown? show-reactions? show-user-info? theme]}]
(fn [{:keys [message-data context keyboard-shown? show-reactions? in-reaction-and-action-menu?
theme]}]
(let [{:keys [content-type quoted-message content
outgoing outgoing-status pinned-by]} message-data
first-image (first (:album message-data))
@@ -163,7 +168,8 @@
[rn/view
{:style {:padding-horizontal 4
:flex-direction :row}}
[avatar-container message-data show-reactions? show-user-info?]
[avatar-container message-data show-reactions? in-reaction-and-action-menu?
(:in-pinned-view? context)]
(into
(if show-reactions?
[rn/view]
@@ -172,9 +178,8 @@
:flex 1
:max-height (when-not show-reactions?
(* 0.4 height))}}
[author message-data show-reactions? show-user-info?]
(case content-type
[author message-data show-reactions? in-reaction-and-action-menu?]
(condp = content-type
constants/content-type-text
[content.text/text-content message-data context]
@@ -219,11 +224,11 @@
:selected-item (fn []
[rn/view {:pointer-events :none}
[user-message-content
{:message-data message-data
:context context
:keyboard-shown? keyboard-shown?
:show-reactions? true
:show-user-info? true}]])}]))
{:message-data message-data
:context context
:keyboard-shown? keyboard-shown?
:show-reactions? true
:in-reaction-and-action-menu? true}]])}]))
(defn system-message?
[content-type]
@@ -10,7 +10,7 @@
[status-im2.common.contact-list-item.view :as contact-list-item]
[status-im2.contexts.chat.messages.drawers.style :as style]
[react-native.gesture :as gesture]
[quo2.components.reactions.resource :as reactions.resource]))
[quo2.components.selectors.reaction-resource :as reactions.resource]))
(defn contact-list-item-fn
[{:keys [from compressed-key]}]
@@ -185,8 +185,9 @@
(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)
[quo/reactions-selector
{:emoji reaction-name
:start-pressed? (boolean emoji-reaction-id)
:accessibility-label (str "reaction-" (name reaction-name))
:on-press
(fn []
@@ -229,11 +230,13 @@
:icon (:icon action)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(when on-press (on-press)))}]))
(when-not (empty? danger-actions)
[quo/separator])
(when on-press (on-press)))}]))]
;; DANGER ACTIONS
(when-not (empty? danger-actions)
[quo/separator {:style {:margin-vertical 8}}])
;; DANGER ACTIONS
[rn/view {:style {:padding-horizontal 8}}
(for [action danger-actions]
(let [on-press (:on-press action)]
^{:key (:id action)}
@@ -244,11 +247,13 @@
:icon (:icon action)
:on-press (fn []
(rf/dispatch [:hide-bottom-sheet])
(when on-press (on-press)))}]))
(when-not (empty? admin-actions)
[quo/separator])
(when on-press (on-press)))}]))]
;; ADMIN ACTIONS
(when-not (empty? admin-actions)
[quo/separator {:style {:margin-vertical 8}}])
;; ADMIN ACTIONS
[rn/view {:style {:padding-horizontal 8}}
(for [action admin-actions]
(let [on-press (:on-press action)]
^{:key (:id action)}
@@ -5,17 +5,6 @@
(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 background-view
[theme]
{:position :absolute
@@ -62,14 +51,15 @@
:opacity (if loaded? 1 0)})
(def header-container
{:position :absolute
:top header-offset
:left 0
:right 0
:padding-bottom 8
:display :flex
:flex-direction :row
:overflow :hidden})
{:position :absolute
:top header-offset
:left 0
:right 0
:padding-bottom 8
:padding-horizontal 20
:display :flex
:flex-direction :row
:overflow :hidden})
(def header
{:flex 1})
@@ -15,7 +15,8 @@
[utils.re-frame :as rf]))
(defn f-view
[{:keys [scroll-y chat chat-screen-loaded? all-loaded? display-name online? photo-path]}]
[{:keys [theme scroll-y chat chat-screen-loaded? all-loaded? display-name online? photo-path
back-icon]}]
(let [{:keys [group-chat chat-id]} chat
opacity-animation (reanimated/interpolate scroll-y
[style/navigation-bar-height
@@ -46,22 +47,24 @@
[reanimated/view {:style (style/animated-blur-view all-loaded? opacity-animation)}
[blur/view
{:blur-amount 20
:blur-type (colors/theme-colors :light :dark)
:blur-radius (if platform/ios? 20 10)
:style {:flex 1}}]]
{:blur-amount 20
:blur-type :transparent
:overlay-color (colors/theme-colors colors/white-70-blur colors/neutral-95-opa-70-blur theme)
:blur-radius (if platform/ios? 20 10)
:style {:flex 1}}]]
[rn/view {:style style/header-container}
[rn/touchable-opacity
{:active-opacity 1
[quo/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :back-button
:on-press #(do
(when config/shell-navigation-disabled?
(rf/dispatch [:chat/close]))
(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)}]]
(rf/dispatch [:navigate-back]))}
back-icon]
[reanimated/view
{:style (style/animated-header all-loaded? translate-animation title-opacity-animation)}
[rn/view {:style style/header-content-container}
@@ -88,15 +91,17 @@
:style (style/header-status)}
(i18n/label
(if online? :t/online :t/offline))])]]]
[rn/touchable-opacity
{:active-opacity 1
:style (style/button-container {:margin-right 20})
[quo/button
{:icon-only? true
:type :grey
:background :blur
:size 32
:accessibility-label :options-button
:on-press (fn []
(rf/dispatch [:dismiss-keyboard])
(rf/dispatch [:show-bottom-sheet
{:content (fn [] [actions/chat-actions chat true])}]))}
[quo/icon :i/options {:size 20 :color (colors/theme-colors colors/black colors/white)}]]]
:i/options]]
[:f>
pin.banner/f-banner
{:chat-id chat-id
@@ -39,7 +39,10 @@
(str (when emoji (str emoji " ")) "# " chat-name)
:else (str emoji chat-name))
online? (rf/sub [:visibility-status-updates/online? chat-id])
photo-path (rf/sub [:chats/photo-path chat-id])]
photo-path (rf/sub [:chats/photo-path chat-id])
back-icon (if (= chat-type constants/one-to-one-chat-type)
:i/close
:i/arrow-left)]
(rn/use-effect
(fn []
;; If keyboard is shown then adjust `scroll-y`
@@ -71,6 +74,7 @@
[messages.navigation/navigation-view
{:scroll-y scroll-y
:back-icon back-icon
:chat chat
:chat-screen-loaded? chat-screen-loaded?
:all-loaded? all-loaded?
@@ -206,19 +206,16 @@
featured-communities-count (count featured-communities)]
(fn []
[scroll-page/scroll-page
{:name (i18n/label :t/discover-communities)
:theme theme
:on-scroll #(reset! scroll-height %)
:background-color (colors/theme-colors
colors/white
colors/neutral-95)
:navigate-back? :true
:height (if (> @scroll-height 360)
208
148)}
[render-sticky-header
{:selected-tab selected-tab
:scroll-height scroll-height}]
{:theme theme
:on-scroll #(reset! scroll-height %)
:navigate-back? :true
:height (if (> @scroll-height 360)
208
148)
:sticky-header [render-sticky-header
{:selected-tab selected-tab
:scroll-height scroll-height}]}
[render-communities
selected-tab
featured-communities-count
@@ -1,7 +1,6 @@
(ns status-im2.contexts.communities.overview.view
(:require [oops.core :as oops]
[quo2.core :as quo]
[quo2.foundations.colors :as colors]
[react-native.blur :as blur]
[react-native.core :as rn]
[reagent.core :as reagent]
@@ -101,7 +100,7 @@
(defn get-access-type
[access]
(case access
(condp = access
constants/community-no-membership-access :open
constants/community-invitation-only-access :invite-only
constants/community-on-request-access :request-access
@@ -317,30 +316,32 @@
collapsed? (and initial-joined? (:joined community))
overlay-shown? (boolean (:sheets (rf/sub [:bottom-sheet])))]
[scroll-page/scroll-page
{:cover-image cover
:collapsed? collapsed?
:logo logo
:page-nav-right-section-buttons (page-nav-right-section-buttons id)
:name name
:on-scroll #(reset! scroll-height %)
:navigate-back? true
:background-color (colors/theme-colors colors/white colors/neutral-95)
:height 148
:overlay-shown? overlay-shown?}
[sticky-category-header
{:enabled (> @scroll-height @first-channel-height)
:label (pick-first-category-by-height
@scroll-height
@first-channel-height
@categories-heights)}]
{:cover-image cover
:collapsed? collapsed?
:logo logo
:name name
:on-scroll #(reset! scroll-height %)
:navigate-back? true
:height 148
:overlay-shown? overlay-shown?
:page-nav-props {:type :community
:right-side (page-nav-right-section-buttons id)
:community-name name
:community-logo logo}
:sticky-header [sticky-category-header
{:enabled (> @scroll-height @first-channel-height)
:label (pick-first-category-by-height
@scroll-height
@first-channel-height
@categories-heights)}]}
[community-content
community
pending?
{:on-category-layout (partial add-category-height categories-heights)
:collapsed? collapsed?
:on-first-channel-height-changed
;; Here we set the height of the component and we filter out the categories, as some might
;; have been removed.
;; Here we set the height of the component and we filter out the categories, as some
;; might have been removed.
(fn [height categories]
(swap! categories-heights select-keys categories)
(reset! first-channel-height height))}]]))))
@@ -16,7 +16,9 @@
(def ^:const emoji-row-separator-height 16)
(def ^:const emoji-item-margin-right
(/ (- (:width (rn/get-window)) (* emoji-row-padding-horizontal 2) (* emoji-size emojis-per-row))
(- emojis-per-row 1)))
(/ (- (:width (rn/get-window))
(* emoji-row-padding-horizontal 2)
(* emoji-size emojis-per-row))
(dec emojis-per-row)))
(def ^:const item-height (+ emoji-size emoji-row-separator-height))
@@ -61,7 +61,7 @@
(if (>= current-progress drag-limit)
drag-limit
(-> (quot current-progress progress-threshold)
(+ 1)
inc
(* progress-threshold)))))
(defn get-previous-progress
@@ -70,7 +70,7 @@
(if (< current-progress progress-threshold)
0
(-> (quot current-progress progress-threshold)
(- 1)
dec
(* progress-threshold)))))
(defn use-initialize-animation
@@ -36,7 +36,7 @@
:background-color :transparent
:height 48
:border-color :black
:border-width 3
:border-width 2
:border-radius 44}))
(def picture-avatar-mask
@@ -21,7 +21,7 @@
:color colors/white-opa-70})
(def subtitle-container
{:margin-top 16})
{:margin-top 24})
(def doc-main-content
{:color colors/white
@@ -21,15 +21,16 @@
:weight :semi-bold}
(i18n/label :t/new-to-status)]
[quo/small-option-card
{:variant :main
:title (i18n/label :t/generate-keys)
:subtitle (i18n/label :t/generate-keys-subtitle)
:image (resources/get-image :generate-keys)
:max-height (- (:height window)
(* 2 56) ;; two other list items
(* 2 16) ;; spacing between items
220) ;; extra spacing (top bar)
:on-press #(rf/dispatch [:onboarding-2/navigate-to-create-profile])}]
{:variant :main
:title (i18n/label :t/generate-keys)
:subtitle (i18n/label :t/generate-keys-subtitle)
:button-label (i18n/label :t/lets-go)
:image (resources/get-image :generate-keys)
:max-height (- (:height window)
(* 2 56) ;; two other list items
(* 2 16) ;; spacing between items
220) ;; extra spacing (top bar)
:on-press #(rf/dispatch [:onboarding-2/navigate-to-create-profile])}]
[rn/view {:style style/subtitle-container}
[quo/text
{:style style/subtitle
@@ -1,17 +1,9 @@
(ns status-im2.contexts.profile.create.events
(:require [utils.re-frame :as rf]
[native-module.core :as native-module]
[status-im2.contexts.profile.config :as profile.config]
[utils.security.core :as security]
[re-frame.core :as re-frame]
[status-im2.contexts.shell.jump-to.utils :as shell.utils]
[status-im.transport.core :as transport]
[status-im.communities.core :as communities]
[status-im.data-store.chats :as data-store.chats]
[status-im.multiaccounts.core :as multiaccounts]
[status-im2.contexts.chat.messages.link-preview.events :as link-preview]
[status-im2.common.log :as logging]
[status-im2.navigation.events :as navigation]))
[status-im2.contexts.profile.config :as profile.config]))
(re-frame/reg-fx
::create-profile-and-login
@@ -23,27 +15,8 @@
{:events [:profile.create/create-and-login]}
[_ {:keys [display-name password image-path color]}]
{::create-profile-and-login
(merge (profile.config/create)
{:displayName display-name
:password (native-module/sha3 (security/safe-unmask-data password))
:imagePath (profile.config/strip-file-prefix image-path)
:customizationColor color})})
(rf/defn login-new-profile
[{:keys [db] :as cofx} recovered-account?]
(let [{:profile/keys [profile profiles-overview wallet-accounts]
:networks/keys [current-network networks]} db
network-id
(str (get-in networks [current-network :config :NetworkId]))]
(shell.utils/change-selected-stack-id :communities-stack true nil)
(rf/merge cofx
{:db (assoc db :chats/loading? false)
:wallet/get-tokens [network-id wallet-accounts recovered-account?]}
(transport/start-messenger)
(communities/fetch)
(data-store.chats/fetch-chats-preview
{:on-success #(re-frame/dispatch [:chats-list/load-success %])})
(multiaccounts/switch-preview-privacy-mode-flag)
(link-preview/request-link-preview-whitelist)
(logging/set-log-level (:log-level profile))
(navigation/init-root :enable-notifications))))
(assoc (profile.config/create)
:displayName display-name
:password (native-module/sha3 (security/safe-unmask-data password))
:imagePath (profile.config/strip-file-prefix image-path)
:customizationColor color)})

Some files were not shown because too many files have changed in this diff Show More