Problem: failed equality checks as in "(is (= expected actual))" will give a
single, long line of output that for anything but the simplest data structures
is unreadable by humans, and the output doesn't give a useful diff.
Solution: use library https://github.com/nubank/matcher-combinators and its test
directive "match?" which will pinpoint where two data structures differ. Then,
instead of "(is (= ...", use "(is (match? expected actual)". It works
beautifully.
The library offers other nice matchers, but the majority of the time match? is
sufficient.
Can we use another test runner like Kaocha? kaocha-cljs2
(https://github.com/lambdaisland/kaocha-cljs2) would be able to print better
test errors out of the box, among other features, but I have no clue if it would
work well or at all in our stack (in theory yes, but it's a larger piece of
work).
* update scan qr code page
* update scan qr code page
* fixed: populate the search input with new identity
* dismiss keyboard when scan qr page is activated
This commit:
- Implements the Wallet Account Switcher for easy switching between wallet accounts
- Updates the About tab in the accounts screen to display the correct account address and derivation path along with the profile.
- Updates the account-item component to pass the state from the parent and refactors the depreciated color functions
- Moves the handle-bar in the bottom sheet to a standalone component
- Adds customization-color in account-origin component
---------
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit is the foundational step to start using malli
(https://github.com/metosin/malli) in this project.
Take in consideration we will only be able to realize malli's full power in
future iterations.
For those without context: the mobile team watched a presentation about malli
and went through a light RFC to put everyone on the same page, among other
discussions here and there in PRs.
To keep things relatively short:
1. Unit, integration and component tests will short-circuit (fail) when
inputs/outputs don't conform to their respective function schemas (CI should
fail too).
2. Failed schema checks will not block the app from initializing, nor throw an
exception that would trigger the LogBox. Exceptions are only thrown in the
scope of automated tests.
3. There's zero performance impact in production code because we only
instrument. Instrumentation is removed from the compiled code due to the
usage of "^boolean js.goog/DEBUG".
4. We shouldn't expect any meaningful slowdown during development.
**What are we instrumenting in this PR?**
Per our team's agreement, we're only instrumenting the bare minimum to showcase 2 examples.
- Instrument a utility function utils.money/format-amount using the macro
approach.
- Instrument a quo component quo.components.counter.step.view/view using the
functional approach.
Both approaches are useful, the functional approach is powerful and allow us to
instrument anonymous functions, like the ones we pass to subscriptions or event
handlers, or the higher-order function quo.theme/with-theme. The macro approach
is perfect for functions already defined with defn.
**I evaluated the schema or function in the REPL but nothing changes**
- If you evaluate the source function, you need to evaluate schema/=> or
schema/instrument as well.
- Remember to *var quote* when using schema/instrument.
- You must call "(status-im2.setup.schema/setup!)" after any var is
re-instrumented. It's advisable to add a keybinding in your editor to send
this expression automatically to the CLJS REPL, or add the call at the end of
the namespace you are working on (similar to how some devs add "(run-tests)"
at the end of test namespaces).
**Where should schemas be defined?**
For the moment, we should focus on instrumenting quo components, so define each
function schema in the same namespace as the component's public "view" var.
To be specific:
- A schema used only to instrument a single function and not used elsewhere,
like a quo component schema, wouldn't benefit from being defined in a separate
namespace because that would force the developer to constantly open two files
instead of one to check function signatures.
- A common schema reused across the repo, like ":schema.common/theme" should be
registered in the global registry "schema.registry" so that consumers can just
refer to it by keyword, as if it was a built-in malli schema.
- A common schema describing status-go entities like message, notification,
community, etc can be stored either in the respective
"src/status_im2/contexts/*" or registered globally, or even somewhere else.
This is yet to be defined, but since I chose not to include schemas for them,
we can postpone this guideline.
* fix: composer height when entering and canceling edit
* fix: blur the composer input when canceling edit
* fix: focusing animation and composer height after blur
* fix: input height when canceling edit while unfocused
* ref: removed arbitrary keyboard check
* fix: moved edit-mentions logic to use-edit to fix unresolved mention
* fix: composer edit should put the cursor at the end
* fix: (potentially) fixing the mention not resolved during edit
* fix: emoji-kb handler changing the height when default kb appears
* Fix text content when editing and reentering chat
* prevent composer when focusing on opening chat with edit/reply
* clean
* Clauxx comments
* Apply for reply
* Lintil soup = yummy
* refactor variable name
* Extract the focusing logic from the data setting logic
* Edge case
* fix: composer mention key & edit re-enter issues
* fix: reply cancel input blur and smooth reply focus
---------
Co-authored-by: Ibrkhalil <vampirekid017@gmail.com>
Fixes the bug by explicitly passing all available addresses to be revealed
to wakuext_requestToJoinCommunity and picking up the first available address
as the airdrop address. This is a temporary solution while we work on the
feature to allow users to choose which addresses to expose.
Fixes https://github.com/status-im/status-mobile/issues/17861
*Areas that may be impacted*: join community flows.
----------------------------------------------------------------------
Community "Request to join" option is enabled.
User holds more than X ETH.
Anyone who holds <X> ETH is allowed to Become member in <COMMUNITY>.
Expected: request to join is received by desktop client and accepted,
mobile user joins the community.
----------------------------------------------------------------------
----------------------------------------------------------------------
Community "Request to join" option is enabled.
User holds less than X ETH.
Anyone who holds <X> ETH is allowed to View and post in <CHANNEL>.
Expected: request to join is received by desktop client and accepted, mobile
user joins the community, but can't post in <CHANNEL>.
----------------------------------------------------------------------
----------------------------------------------------------------------
Community "Request to join" option is enabled.
No token permissions.
Expected: request to join is received by desktop client and accepted,
mobile user joins the community.
----------------------------------------------------------------------
* Refactor edit-account view and events:
- Fix `(fn [])` code style.
- Avoid overriding clojure.core/type by destructuring the `:type` key.
- Split the toast callback to a different function.
- `:wallet/save-account` receives `:on-success` instead of `:callback` to improve readability.
* Refactor app-db for `:wallet/tokens` & `:wallet/tokens-loading?`
- Remove the root sub `:wallet/tokens-loading?`, now it's in app-db in `[:wallet :ui :tokens-loading?]`.
- Remove the root subs `:wallet/tokens`, now the value is returned along with the account data by the sub
`:wallet/accounts`, it's stored in app-db in `[:wallet :address "0x..." :tokens]`.
- Fix the format of the token data returned by the endpoint `"wallet_getWalletToken"` and the fn `js->clj`,
- Addresses are no longer keywords (since keywords mustn't start with a number).
- Keys are now kebab-case.
- Chain-ids are no longer keywords, now they are integers.
- Update tests.
* Move logic to calculate `:wallet/balances` and change value returned
- Move logic to `status-im2.contexts.wallet.common.utils`
- The `:wallet/balances` value returned by the sub had the following structure:
[{:address "0x1...", :balance 12345}
{:address "0x2...", :balance 67890} ...]
This required a helper function to get the balance for an address (`get-balance-by-address`)
It has been changed to a map:
{"0x1..." 12345
"0x2..." 67890, ...}
So now we don't need a helper function (just the hashmap itself or `clojure.core/get`).
- Because of the previous change, now the `get-balance-by-address` has been removed.
- The function `get-account-by-address` has zero uses, so it has been removed.
- The test for the sub has been updated.
* Create sub `:wallet/account-cards-data`
This sub returns a vector of maps to render the account cards in the wallet page.
This logic was previously in the `view` namespace, but it was completely calculated from
the subs `:wallet/accounts`, `:wallet/balances` and `:wallet/tokens-loading?`, so it was
clear that's a derived value.
- Fix: when there are only channel token permissions, don't show the text "You
hold 0 of these:" because there are no requirements to show.
- Fix: do not show channel token permissions when the user wants to join a
community. In other words: only "become admin", "become member", "become token
master", and "become token owner" are taken in consideration.
- Fix: render correct channel lock icon in 3 states (no permission, with
permissions and locked and with permissions and unlocked).
- Fix: Previously, before having joined a community, all channels had a lock
icon closed, now the lock icon is only closed when there's a permission set,
otherwise no icon is shown (the lock is never open before the user joins the
community).
- Fix: small UI spacing fix, only display community tags component when there's
at least one tag.
- Bonus fix: community Overview and Discover screens top bar had a regression,
see the screenshots.
Fixes https://github.com/status-im/status-mobile/issues/17267
### Areas that may be impacted
- Community overview, before and after joining a community.
### Steps to test
Code tested using the Goerli network and with the testnet flag enabled in the
Desktop client. Out of scope: minted tokens.
This commit moves the emoji categories selector height from the "showcase-nav" component to the "emoji-picker" as we should not use any "quo" ns (except "quo.core") inside the "status-im2" ns.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit updates the following:
- Allow users to create new wallet accounts without having to re-login (latest account data is fetched immediately)
- Updates the max length of the wallet account name to 20
- Updates the account cards in the wallet home screen to render the actual account colour
- Updates the (individual) account screen to show the correct color, emoji, name and address
- Allows users to edit account name, colour and emoji
- The rest of the wallet screens would see the updated account information immediately
- Fixes the color (uses profile color) of the context tag and button color in the authentication (enter password) bottom sheet
- Fixes the overflowing of the "+" card in the wallet home when there are two or more accounts.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
Add account creation screen
remove icons
remove extra utility and create a new one that would use conventional way of getting an emoji
fix lint
Use button component instead of bottom-actions
Provide global customization color to buttons
Use conventional approach to extract account name
Move to another address
Move to another namespace
Refactor bottom-actions to have button props in maps
Update doc with new icon location
Add spaces between styles
Work on pr comments
Use :on-change-text instead of :on-change for input component
Subscribe to :profile/customization-color directly
Use bottom button from the create-or-edit-account wrapper
Remove extra code
Sort requires
Move ns to proper fileˆ
Fix styles
This commit updates the following in the selectors component:
- Introduces the "type" prop (Figma 1-1 property) to the selectors component for easy switching between the selector types while using it on lists.
- Updates the component styles to use the "resolve-color" function as the "custom-color" function is deprecated
- Updates the component usage across the codebase
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
Fix the unreliable swipe behavior of link previews in the chat composer. In many
occasions, we noticed the horizontal scroll wasn't being triggered properly or
not at all. In other occasions, the swipe gesture would incorrectly register a
press on the parent `Touchable` component.
Fixes https://github.com/status-im/status-mobile/issues/16599
fix: improving UI and gestures
fix: expanding on press now works reliably
fix: center text and disable gestures for short messages
feat: added styling for one-lined messages
fix: small opacity fix
ref: small condition change
fix: horizontal swiping being unreliable with vertical scrolling
ref: reusing shared value from lightbox animations
fix: text-sheet bottom gradient not visible on android
fix: addressed qa issues 1,2,4
fix: zoomable-image stuck if moved vertically when full screen
clean: removed unused var
chore: removed unused require
fix: don't allow returning text-sheet down and up in one move
fix: text-sheet bar styling was wrong
fix: adjusted gradient animation with designs
fix: text-sheet bar opacity
* Align docstring
* Create share-qr-code component
* Remove empty style file
* Implement common.share-qr-code including call to media server
* Add share-qr-code preview screen
* Use share-qr-code component in shell's share screen
* Add tests and some fixes
This commit moves the "network-routing" component test to the quo ns from quo2 ns.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
* feat: debug/show components from the REPL
* ref: some renames and cleanup
* chore: removed comments/imports
* feat: added remount on every eval & small changes
* doc: added tip to debugging doc
* doc: formatting
* doc: more formatting
* fix: fixed docs and other review comments
* fix: moved & renamed into the dev.component-preview ns
* style: adjusted debugging readme spacing
* fix: removed trailing line
Upgrades and cleans up all production Clojure dependencies and 1 dev-only
dependency (com.taoensso/tufte).
- Remove warning "WARNING: update-keys already refers to:
#'clojure.core/update-keys in namespace: io.aviso.exception"
- Remove hickory and mvxcvi/alphabase dependencies they are not used.
- Upgrade com.taoensso/tufte from 2.1.0 to 2.6.3
- Upgrade transit-cljs from 0.8.248 to 0.8.280
- Upgrade cljs-bean from 1.3.0 to 1.9.0
- Remove workaround for com.taoensso/timbre in shadow-cljs.edn
- Upgrade com.taoensso/timbre from 4.10 (Status fork) to 6.3.1
This commit
- Adds the base Screen base for the Create/Edit Account/Address
- Adds the Wallet Edit Account screen
- Adds the gradient in the account options bottom sheet
- Adds a new key/prop "right-icon" in the data-item component to prevent overlapping with the icon used in the description and updates its usage.
- Updates the "section-label" component to receive "container-style" (this will prevent adding a wrapping view in the screens)
---------
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
Adapt the JSON RPC response to the new shape returned by `wakuext_unfurlURLs` on
v0.170.0. The changes were introduced PR
https://github.com/status-im/status-go/pull/4033. There are no behavioral
changes in the API as far as mobile is concerned at the moment.
This commit adds a custom linter to verify i18n/label is called with a qualified
keyword, like :t/foo. More sophisticated linters are possible too.
We also set the stage for other developers to consider more lint automation
instead of manually reviewing conventions in PRs.
If you want to understand how to write custom linters, check out
https://github.com/clj-kondo/clj-kondo/blob/master/doc/hooks.md. You can fire
the Clojure JVM REPL in status-mobile and play with the clj-kondo hook too, it
works beautifully.
Why do we care? By making sure all translation keywords are qualified with "t",
it is trivial to grep or replace them because they're unique in the repo, and
can't be confused with other words if you search by ":t/<something>".
Note: It's a best practice to commit clj-kondo configuration from external
libraries in the .clj-kondo directory. The directory .clj-kondo/babashka is
auto-generated, that's why it was added.
This commit fixes the following issues in the bottom sheet:
- the sheet is cut off at the bottom in the shell (dark blur) theme (the drawers in the onboarding/login flow)
- the incorrect background in the shell (dark blur) theme (the drawers in the onboarding/login flow)
Bug
- the spacing at the bottom is doubled
- the gradient cover is not shown in the bottom sheet
- the spacing between the selected item and the bottom sheet
- the bottom sheet type in "Create Profile" and "Activity Center" screens
---------
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
* 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"
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>
This commit shows how to move away from rf/defn
f12c7401d1/src/utils/re_frame.clj (L1-L90)
& rf/merge
f12c7401d1/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](3082605d1e/src/status_im2/contexts/profile/login/events.cljs (L69)),
[status-im2.contexts.profile.login.events/get-chats-callback](3082605d1e/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).
>
> -- 39adca9367/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?
39adca9367/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.
>
> -- 39adca9367/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.
>
> -- 39adca9367/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
39adca9367/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
39adca9367/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:
39adca9367/docs/releases/2020.md (110-2020-08-24)
This commit adds the "network-dropdown" component to the "wallet-overview" component.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit:
- Removes the existing dropdown component which uses the old button component under the hood
- Implements the Dropdown component
- Updates the usage in the page-nav component for types dropdown, token and wallet-networks
- Updates the usage in the photo selector bottom sheet
- Updates the usage in the Quo2 preview main screen
---------
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
to accomplish #17288
migrated :
notifications
Issue Opened: A bug has been identified within the activity-logs component and is currently under investigation.
This commit updates
- the preview-list component to use "number-tag" for overflow items in the list as a follow-up of the PR Update "Preview list" component.
- the usage of the preview-list component in the context-tag (multiuser and multinetwork type) is updated as it was broken.
- the size APIs of preview-list and number-tag and their usage across the codebase to respect the new guidelines.
---------
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
* chore: update guidelines for sizes
* chore: update to size uses in codebase to follow new convention
---------
Co-authored-by: Ulises M <ulises.ssb@hotmail.com>
* use new API for ens name registration
4cc53630...223d215e
* update status-go-version.json
* update status-go-version.json
* fix Error:Field validation for 'KeycardPairingDataFile' failed on the 'required' tag
* update status-go-version.json
* fix lint issue
Recently, we changed clj-kondo default fail-level from "warning" to "error", but
we missed the fact that we needed to raise the default level for all linters set
to "warning".
This commit adds Emoji Picker in the app for usage in Message Composer and Wallet Account.
---------
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
Every keypress on an input field of the preview descriptor re-rendered the whole
functional component and the input focus was lost.
As we know from past issues, we should never use this pattern:
(defn component
[]
[:f>
(fn []
...)])
Force re-frame to stop warning about subscriptions in non-reactive contexts
while executing subscription tests (i.e. unit tests using the macro
test-helpers.unit/deftest-sub).
The net result? No more hundreds of useless warnings in the output of make test.
re-frame: Subscribe was called outside of a reactive context.
See: https://day8.github.io/re-frame/FAQs/UseASubscriptionInAJsEvent/
The warning is actually useful in production code, but in a subscription test we
already know we're never inside a reactive context.
This PR resolves the issue where Composer becomes detached from the footer during the chat history loading phase, specifically when the skeleton screen is displayed. Fixes#17237.
fixes#17103
### Follow up from #16865
This commit focuses on enhancing the user experience by replacing our legacy skeleton loaders with the latest version. Additionally, it introduces a refined approach for deprecating outdated components and functions to maintain a cleaner codebase.
### Key Changes
Replace existing skeleton loaders with the latest skeleton-list component for improved performance and usability.
Implement a standardized deprecation method, using either naming conventions or metadata annotations, for phasing out old components and functions.
Unshadows all remaining vars in status-mobile, including non
cljs.core/clojure.core ones. The only exceptions are cljs.core/type and
cljs.core/name (which happen quite often, so I'm not sure if it's worth
unshadowing them).
The bug was caused, again, by the fact that the component react-native/image
source was a string and it was not wrapped inside the map {:uri "..."}. The
bug was likely introduced by commit 255a3b9172
I decided to change react-native.image for good, similarly to what we do in
react-native/fast-image
(ebd38295c6/src/react_native/fast_image.cljs (L21))
so we can prevent this type of bug in the future.
Fixes https://github.com/status-im/status-mobile/issues/17157
Fixes: #16123
This commit changes the behavior when joining communities:
1) If the user hasn't joined a community, they will see the
non-collapsed header.
2) If the user has already joined the community, they will see a
collapsed version of the header, in order to have quicker access to
the chats of the community without having to scroll a lot.
One edge case that is taken care of is the following:
1) User visits a community that has not joined, requests to join.
2) The user is on the community page when the state changes from
not-joined -> joined.
In this case the user won't see the collapsed header until they leave
the community view (go back to home say and back to communities).
This is done in order to avoid some janky transition between the non
collapsed & collapsed version of the header.
This is slightly different from as mentioned in the issue "visiting the
page for the 2nd time", as that will require larger changes but it
should be an acceptable behavior, since most of the times they will have
to visit the overview to request access.
Add icon-name to group avatar
Add missing context-tags variants and fix the API to match figma
Updates context-tag previous uses
Update use of group-avatar api
* Refactor page-nav and fix API
* Update and fix page-nav in scroll-page component
* Update page-nav uses in quo-preview
* Update page-nav uses in syncing
* Update page-nav uses in communities
* Update page-nav uses in wallet
* Update page-nav uses in onboarding
This commit updates the "Preview list" component to support the following types (according to Figma):
- collectibles
- dapps
- accounts
- network
- tokens
Additionally, imports the images for wallet networks and dApps.
---------
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit implements the "Showcase nav" component which is needed for emoji picker development.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit adds Gradient Background in the bottom sheet which will be used in Wallet Account Actions, and Switcher bottom sheet.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
Improvements to quo previews:
- Change quo-preview.preview code to be more aligned with our guidelines.
- Remove duplication when setting up preview screens in quo-preview.main. Now we
only need to specify the :name and :component keys and fallback to a good
default for "{:options {:topBar {:visible true}}".
- Auto-generate descriptor labels based on the key.
- Fix form field colors for dark & light, especially for Android.
- Redesigned form fields to look nicer 💄
- Create component that abstracts away the code for rendering the form fields
and the preview area. This will aid us in creating more consistent looking
preview screens and keep things DRY.
How do we use the new code?
Just as before, define a state binding and use it to build up the arguments of
the component. Then, use the preview/preview-container to wrap the component.
You can use certain props to control how the preview is styled, etc, but that's
about it.
(def descriptor
[{:type :text :key :label}
{:key :chevron-position
:type :select
:options [{:key :left}
{:key :right}]}])
(defn view
[]
(let [state (reagent/atom {:chevron-position :left
:label "Welcome"})]
(fn []
[preview/preview-container {:state state :descriptor descriptor}
[quo/divider-label @state]])))
* Inherit custom colour for unread notification dots
* Code style update; notification-dot as a separate component
* Code style update; notification-dot as a separate component
* Removed leftovers
* Updates
* Lint fix
* chore: update quo2 group avatar to best practices
* chore: update quo2 browser-input to best practices
* chore: update quo2 dynamic-button to best practices
* chore: update quo2 tabs to best practices
* chore: cleanup quo2 core file
* chore: use best practices in quo2 banner
* chore: use best practices in quo2 step
This pr. add blur background on continue button on create profile screen should the button container cover the profile color picker when the keyboard is shown
* show button background on scroll
* fix show button on scroll different platforms
* fix: button container loading
* fix: button container background delay
* Add bind-component function to `react-native.navigation`
* Create helper function to use react-native-navigation lifecycle methods
* Fix sync QR code not recognized after trying again & refactor
* Add atom to manage callbacks when qr code scan fails
This commit implements the "Progress bar" component which is needed for wallet screen development.
Additionally, this commit adds a new test helper method "render-with-theme-provider" to test components in different themes.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
* Small typo fix
* Move header-spacing & empty-state to common.home namespace
* Create common animated banner component for chats and communities
* Add animated banner to chats tab
This commit adds two new entry points (button on the top right corner in the navigation bar) for the Quo2 preview screen without having to log in to the app:
- "New to Status"
- "login"
Additionally, this commit updates the theme switcher to be sticky at the top of the Quo2 preview screen to quickly change the theme without having to scroll all the way top.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit adds the "on-press" prop to the "collectible" component which is required for navigating to other screens, such as the details screen when we will start building the wallet.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit implements the "Account Avatar" component which is needed for wallet screen development.
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
This commit implements the "gradient cover" component which is needed for wallet screen development, and upgrades the "react-native-linear-gradient" library to "v2.8.0".
Signed-off-by: Mohamed Javid <19339952+smohamedjavid@users.noreply.github.com>
* Small refactor & add customization color to `info-count` component
* Fix notification mark in group chats for no mentions, also notification alignment styles in the component
* Code style: `change > 0` for `pos?`
* Simplifies community banner and adds style property
* Add animation for communities banner card
* Moves value-in-range to `utils.number` and removes `bounded-val`
* Moves animated values to view namespace
* Make `community.banner` component themeable
This commit upgrades Shadow CLJS from 2.11.16 (released on Feb/21) to latest
2.25.0 (Jul/23), so ~1.5 years worth of upgrades. By upgrading shadow we
can finally use the latest major Clojure version 1.11.x.
Why upgrade shadow?
- Shadow CLJS controls the ClojureScript version we can use. In order to use the
latest major Clojure version we must upgrade Shadow CLJS.
- Shadow CLJS releases new versions very frequently, and if you take a look at
its changelog https://github.com/thheller/shadow-cljs/blob/master/CHANGELOG.md, you'll see
it had tons and tons of bug fixes over the years. I hope some of them help
improve the DX for certain contributors who recently reported issues with
it.
- Clojure 1.11 brings new features, bug fixes and even performance improvements
(although I think the performance mostly impacts Clojure on the JVM). See the
changelog https://github.com/clojure/clojure/blob/master/changes.md#changes-to-clojure-in-version-1110
Things that can be beneficial to us, or are interesting nonetheless:
- New :as-alias to be used in require, which is like :as but does not require
the namespace to load. This means namespaced keywords using :as-alias can't
cause circular dependency errors. This feature would very useful if we used
namespaced keywords, but we don't, so...
https://github.com/clojure/clojure/blob/master/changes.md#22-as-alias-in-require
- New macros run-test and run-test-var to run single test with fixtures and
report.
- New iteration function, useful for processing paginated data.
https://www.abhinavomprakash.com/posts/clojure-iteration/
- New update-keys function: applies a function to every key in a map.
- New update-vals function: applies a function to every value in a map.
Examples for update-vals and update-keys. They should perform better than the
common reduce-kv approach since they use a transient data structure.
(let [m {:a 1 :b 2}]
(update-vals m inc)) ; => {:a 2, :b 3}
(let [m {:a 1 :b 2}]
(update-keys m name)) ; => {"a" 1, "b" 2}
Why change namespaces within __tests__ directories?
Any namespace with the word --tests-- throws an error, like the one below. I
didn't bother investigating why, so I changed the guidelines to reflect the new
convention. It's probably related to the double dashes in the name.
Namespace quo2.components.dividers.--tests--.divider-label-component-spec has a
segment starting with an invalid JavaScript identifier at line 1
This commit initialises the status-go method `startSearchForLocalPairingPeers` which in turn will produce logs that are important for peer discovery while local pairing and will produce logs important to detect local pairing crashes.